AWS VPC Peering with multiple CIDRS with Terraform

Mar. 1, 2024

Navigating Terraform for VPC peering across multiple CIDRs and regions can be daunting at first, but with a structured approach, it becomes manageable:

Pre-Req

  1. AWS accounts are configured, and awscli is set up.
  2. VPCs, subnets, and routes have already been configured.
  3. Terraform is configured and ready for use.

Define Variables

#accepter
variable "accepter_vpc_id" {
  default = "vpc-xxx"
}
variable "accepter_region" {
  default = "xxx"
}

#requester
variable "requester_vpc_id" {
  default = "xxx"
}
variable "requester_region" {
  default = "xx"
}

Define providers

#### providers ####
#requester
provider "aws" {
  alias  = "requester"
  region = var.requester_region
}

provider "aws" {
  alias  = "peer"
  region = var.requester_region
}

## accepter 
provider "aws" {
  alias  = "accepter"
  region = var.accepter_region
}

data "aws_vpc" "accepter" {
  id       = var.accepter_vpc_id
  provider = aws.accepter
}

Data Gathering

Typically, we filter routes, but I’m adjusting the data for experimentation. If you need all routes, feel free to remove the filters. My routes are tagged as private and local, so I’m filtering them accordingly.

data "aws_route_tables" "accepter_local_routes" {
  vpc_id   = var.accepter_vpc_id
  provider = aws.accepter
  filter {
    name   = "tag:Name"
    values = ["*local*"]
  }

}

data "aws_route_tables" "accepter_priv_routes" {
  vpc_id   = var.accepter_vpc_id
  provider = aws.accepter
  filter {
    name   = "tag:Name"
    values = ["*priv*"]
  }

}

data "aws_vpc" "requester" {
  id       = var.requester_vpc_id
  provider = aws.requester
}

data "aws_route_tables" "requester_local_routes" {
  vpc_id   = var.requester_vpc_id
  provider = aws.requester
  filter {
    name   = "tag:Name"
    values = ["*local*"]
  }
}

data "aws_route_tables" "requester_priv_routes" {
  vpc_id   = var.requester_vpc_id
  provider = aws.requester
  filter {
    name   = "tag:Name"
    values = ["*priv*"]
  }
}

Peering configuration

#### peering configuration #### 
data "aws_availability_zones" "available" {
  provider = aws.peer
}

resource "aws_vpc_peering_connection" "this" {
  vpc_id      = var.requester_vpc_id
  peer_vpc_id = var.accepter_vpc_id
  peer_region = var.accepter_region
  auto_accept = false
  provider    = aws.peer
}

resource "aws_vpc_peering_connection_accepter" "this" {
  provider                  = aws.accepter
  vpc_peering_connection_id = aws_vpc_peering_connection.this.id
  auto_accept               = true
}

resource "aws_vpc_peering_connection_options" "this" {
  vpc_peering_connection_id = aws_vpc_peering_connection.this.id
  accepter {
    allow_remote_vpc_dns_resolution = true
  }
  provider = aws.accepter
}

locals {
  accepter_cidr_block = data.aws_vpc.accepter.cidr_block_associations[*].cidr_block
  requester_cidr_block = data.aws_vpc.requester.cidr_block_associations[*].cidr_block
  routes_requester_local_cidrblocks = setproduct(data.aws_route_tables.requester_local_routes.ids,local.accepter_cidr_block)
  routes_requester_priv_cidrblocks = setproduct(data.aws_route_tables.requester_priv_routes.ids,local.accepter_cidr_block)
  routes_accepter_local_cidrblocks = setproduct(data.aws_route_tables.accepter_local_routes.ids,local.requester_cidr_block)
  routes_accepter_priv_cidrblocks = setproduct(data.aws_route_tables.accepter_priv_routes.ids,local.requester_cidr_block)
}

Routes configuration

####  route tables ####
resource "aws_route" "requester_local_routes" {
  count                     = length(local.routes_requester_local_cidrblocks)
  route_table_id            = local.routes_requester_local_cidrblocks[count.index][0]
  destination_cidr_block    = local.routes_requester_local_cidrblocks[count.index][1]
  vpc_peering_connection_id = aws_vpc_peering_connection.this.id
  provider                  = aws.peer
}

resource "aws_route" "requester_priv_routes" {
  count                     = length(local.routes_requester_priv_cidrblocks)
  route_table_id            = local.routes_requester_priv_cidrblocks[count.index][0]
  destination_cidr_block    = local.routes_requester_priv_cidrblocks[count.index][1]
  vpc_peering_connection_id = aws_vpc_peering_connection.this.id
  provider                  = aws.peer
}

resource "aws_route" "accepter_local_routes" {
  count                     = length(local.routes_accepter_local_cidrblocks)
  route_table_id            = local.routes_accepter_local_cidrblocks[count.index][0]
  destination_cidr_block    = local.routes_accepter_local_cidrblocks[count.index][1]
  vpc_peering_connection_id = aws_vpc_peering_connection.this.id
  provider                  = aws.accepter
}

resource "aws_route" "accepter_priv_routes" {
  count                     = length(local.routes_accepter_priv_cidrblocks)
  route_table_id            = local.routes_accepter_priv_cidrblocks[count.index][0]
  destination_cidr_block    = local.routes_accepter_priv_cidrblocks[count.index][1]
  vpc_peering_connection_id = aws_vpc_peering_connection.this.id
  provider                  = aws.accepter
}

Deploy

terraform init
terraform fmt
terraform plan
terraform apply