Deploying a Multi-Account Cloud WAN Infrastructure with VPC attachments using Terraform, and GitLab
Components:
For specifications and available options, the following links should be useful:
AWS Cloud WAN Build, manage, and monitor global wide area networks
AWS Resource Access Manager Simply and securely share your AWS resources across multiple accounts
HashiCorp Terraform AWS Provider Use the Amazon Web Services (AWS) provider to interact with the many resources supported by AWS. Lifecycle management of AWS resources powered by the AWS Cloud Control API.
Gitlab CI CD Infrastructure as Code with Terraform and GitLab all tiers
Setting up git with ci/cd:
Create the following enviroment variables in the repo to be acccessed by CI/CD runner.
➜ cwman git:(master) glab variable list
KEY PROTECTED MASKED SCOPE
AWS_SECRET_ACCESS_KEY false false *
AWS_ACCESS_KEY_ID false false *
TF_STATE_NAME false false *
Create .gitlab-ci.yml that specifies pipeline configuration. Manual action would be required for deploy and destory.
include:
- template: Terraform/Base.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml
- template: Jobs/SAST-IaC.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml
stages:
- validate
- test
- build
- deploy
- cleanup
fmt:
extends: .terraform:fmt
needs: []
validate:
extends: .terraform:validate
needs: []
build:
extends: .terraform:build
deploy:
extends: .terraform:deploy
dependencies:
- build
environment:
name: $TF_STATE_NAME
destroy:
extends: .terraform:destroy
Create backend.tf which defines remote terraform backend managed with Gitlab HTTP backend. TF_STATE_NAME defines the state file name.
terraform {
backend "http" {
}
}
Create provider.tf to specicify AWS providers and provider specifications. Note us-east-1.
While cloudwan is a global construct, resource sharing for core networks is only available at us-east-1.
terraform {
required_providers {
aws = { source = "hashicorp/aws" }
awscc = { source = "hashicorp/awscc" }
}
}
provider "aws" {
region = "us-east-1"
}
Set up VPCs for VPC
Create vpc.tf to include a couple of VPCs. This example intends to be limited to this poc.
resource "aws_vpc" "cwman_vpc_1" {
cidr_block = "172.16.0.0/24"
tags = {
Name = "cwman_vpc_1"
}
}
resource "aws_vpc" "cwman_vpc_2" {
cidr_block = "172.16.1.0/24"
tags = {
Name = "cwman_vpc_2"
}
}
resource "aws_subnet" "cwman_subnet_1" {
vpc_id = aws_vpc.cwman_vpc_1.id
cidr_block = "172.16.0.0/28"
availability_zone = "us-east-1a"
tags = {
Name = "cwman_subnet_1"
}
}
resource "aws_subnet" "cwman_subnet_2" {
vpc_id = aws_vpc.cwman_vpc_2.id
cidr_block = "172.16.1.0/28"
availability_zone = "us-east-1b"
tags = {
Name = "cwman_subnet_2"
}
}
Deploy Cloud WAN
Create global_network.tf that defines the global network
resource "aws_networkmanager_global_network" "cwman" {
description = "cwman"
tags = { "Name" : "cwman" }
}
Create core_network.tf that specifies core network and attaches with the global network
resource "aws_networkmanager_core_network" "cwmancore" {
global_network_id = aws_networkmanager_global_network.cwman.id
description = "cwmancore"
}
Create policy_document.tf specifies a Core Network policy document. This example specifies
- all the edge locations the core network will be active in: us-east-1
- creation of segments and specifications: flatnet, pita
- attachement policy towards segments, which matches variables while creating vpc attachements and assigning respective segments.
data "aws_networkmanager_core_network_policy_document" "cwmanpolicy" {
core_network_configuration {
vpn_ecmp_support = true
asn_ranges = ["64515-64530"]
edge_locations {
location = "us-east-1"
}
}
segments {
name = "pita"
description = "unflatearth"
require_attachment_acceptance = true
}
segments {
name = "flatnet"
description = "flatearth"
require_attachment_acceptance = true
}
segment_actions {
action = "share"
mode = "attachment-route"
segment = "pita"
share_with = ["*"]
}
attachment_policies {
rule_number = 100
condition_logic = "or"
conditions {
type = "tag-value"
operator = "equals"
key = "segment"
value = "flatnet"
}
action {
association_method = "constant"
segment = "flatnet"
}
}
attachment_policies {
rule_number = 200
condition_logic = "or"
conditions {
type = "tag-value"
operator = "equals"
key = "segment"
value = "pita"
}
action {
association_method = "constant"
segment = "pita"
}
}
}
Create policy_document_attachment.tf that enables policy document attachment with the core network.
resource "aws_networkmanager_core_network_policy_attachment" "cwmanpolicyattach" {
core_network_id = aws_networkmanager_core_network.cwmancore.id
policy_document = data.aws_networkmanager_core_network_policy_document.cwmanpolicy.json
}
Set up VPC attachements:
Create vpc_attachement.tf. Note segment tag which would cause policy condition match.
resource "aws_networkmanager_vpc_attachment" "cwman_vpc_1_attach" {
subnet_arns = [aws_subnet.cwman_subnet_1.arn]
core_network_id = aws_networkmanager_core_network.cwmancore.id
vpc_arn = aws_vpc.cwman_vpc_1.arn
tags = { "segment" = "flatnet" }
}
resource "aws_networkmanager_vpc_attachment" "cwman_vpc_2_attach" {
subnet_arns = [aws_subnet.cwman_subnet_2.arn]
core_network_id = aws_networkmanager_core_network.cwmancore.id
vpc_arn = aws_vpc.cwman_vpc_2.arn
tags = { "segment" = "pita" }
}
Create vpc_attachment_accepter.tf which actions attachement acceptance of a resource with a segment.
Here out of 2 VPCs only one is defined, so while one will be attached, the second one will be in pending.
resource "aws_networkmanager_attachment_accepter" "cwman_vpc_1_accept" {
attachment_id = aws_networkmanager_vpc_attachment.cwman_vpc_1_attach.id
attachment_type = aws_networkmanager_vpc_attachment.cwman_vpc_1_attach.attachment_type
}
At this point the network is locally available within the account scope.
Set up multi account attachments using AWS RAM
Create resource_share.tf which defines a resource share which will compromise of core network & accounts.
resource "aws_ram_resource_association" "cwman_share_association" {
resource_arn = aws_networkmanager_core_network.cwmancore.arn
resource_share_arn = aws_ram_resource_share.cwman_share.arn
}
Create resource_share_association which associates core network with resoure share.
resource "aws_ram_resource_association" "cwman_share_association" {
resource_arn = aws_networkmanager_core_network.cwmancore.arn
resource_share_arn = aws_ram_resource_share.cwman_share.arn
}
Create resource share pricipal resource_share_principal.tf which defines sharing scope.
This can contain specific account IDs or the entire organisation.
The following shares the resource with an entire organisation.
resource "aws_ram_principal_association" "cwman_share_principal" {
principal = "arn:aws:organizations::<12-digit-org_master_account_ID>:organization/o-<org-ID>"
resource_share_arn = aws_ram_resource_share.cwman_share.arn
}
VPC attachments can now be introduced from other accounts, by associating those VPCs with the shared core network.