DEV Community

Cover image for Creating a Multi-Region VPC Peering on AWS using Terraform
Adeoluwa Adeyemo
Adeoluwa Adeyemo

Posted on

Creating a Multi-Region VPC Peering on AWS using Terraform

Hi, Welcome to the second edition in the VPC Peering Series.

Here is a link to the previous edition on VPC Peering.

In this edition, you will learn how to create and peer VPCs together that are in different regions. This shows how to create a multi region VPC peering on AWS using terraform.

What is AWS Multi Region VPC Peering?

Amazon Web Services (AWS) Multi Region Virtual Private Cloud (VPC) peering is a networking feature that allows you to connect two VPCs together in different regions, enabling communication between them as if they were on the same network.

Region VPC Diagram

  • Requester Region - This is the region attached to the Requester VPC from which we are initiating a request to establish a peering connection with another VPC in a different region.

  • Accepter Region - This is the region attached to the Accepter VPC from which receives and accepts the peering request from requester VPC in the requester region.


Terraform Script

  • Provider.tf - This shows the specific provider used for this script. This is an important terraform script as you need to pay close attention to the naming and alias used here.
provider "aws" {
  access_key = var.access_key
  secret_key = var.secret_key
  region     = var.requester_region
}

# Provider for Requester
provider "aws" {
  alias      = "requester"
  region     = var.requester_region
  access_key = var.access_key
  secret_key = var.secret_key
}

# Provider for Accepter 
provider "aws" {
  alias      = "accepter"
  region     = var.accepter_region
  access_key = var.access_key
  secret_key = var.secret_key
}


terraform {
  required_version = ">= 1.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Requester Vpc.tf - This shows the creation of the Requester Vpc. Here the "provider" block must be specified as that will tie the vpc and other resources in the script to the specified region.
# Create VPC for Requester
resource "aws_vpc" "requester_vpc" {
  cidr_block = var.vpc_cidr_block
  provider   = aws.requester
  #  enable_dns_hostnames = true
  tags = {
    Name = "requester_vpc"
  }
}

# Create Internet Gateway for Requester
resource "aws_internet_gateway" "requester-igw" {
  vpc_id   = aws_vpc.requester_vpc.id
  provider = aws.requester
  tags = {
    Name = "requester-igw"
  }
}

# Create Route Table for Requester
resource "aws_route_table" "route_table_requester" {
  vpc_id   = aws_vpc.requester_vpc.id
  provider = aws.requester

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.requester-igw.id
  }

  route {
    cidr_block                = "10.1.5.0/24"
    vpc_peering_connection_id = aws_vpc_peering_connection.requester.id
  }
}

# Data for availability Zones
data "aws_availability_zones" "availability_zones" {
  provider = aws.requester
}

# Create Subnet for Requester
resource "aws_subnet" "subnet_requester" {
  provider                = aws.requester
  vpc_id                  = aws_vpc.requester_vpc.id
  cidr_block              = var.requester_subnet_cidr_block
  map_public_ip_on_launch = true # This makes public subnet
  availability_zone       = data.aws_availability_zones.availability_zones.names[0]
  tags = {
    Name = "requester-subnet"
  }
}

# Associate Requester Subnet to Requester Route Table
resource "aws_route_table_association" "route_table_requester" {
  provider       = aws.requester
  subnet_id      = aws_subnet.subnet_requester.id
  route_table_id = aws_route_table.route_table_requester.id
}
Enter fullscreen mode Exit fullscreen mode
  • Accepter Vpc.tf - This shows the creation of the Accepter Vpc. Here the "provider" block must be specified as that will tie the vpc and other resources in the script to the specified region.
# Create VPC for Accepter
resource "aws_vpc" "accepter_vpc" {
  cidr_block = var.peer_vpc_cidr_block
  provider   = aws.accepter
  #  enable_dns_hostnames = true
  tags = {
    Name = "accepter_vpc"
  }
}

# Create Route Table for Accepter
resource "aws_route_table" "route_table_accepter" {
  vpc_id   = aws_vpc.accepter_vpc.id
  provider = aws.accepter

  route {
    cidr_block                = "10.0.5.0/24"
    vpc_peering_connection_id = aws_vpc_peering_connection.requester.id
  }
}

# Data for availability Zones
data "aws_availability_zones" "azs" {
  provider = aws.accepter
}

# Create Subnet for Accepter
resource "aws_subnet" "subnet_accepter" {
  provider                = aws.accepter
  vpc_id                  = aws_vpc.accepter_vpc.id
  cidr_block              = var.accepter_subnet_cidr_block
  map_public_ip_on_launch = false # This makes private subnet
  availability_zone       = data.aws_availability_zones.azs.names[1]
  tags = {
    Name = "accepter-subnet"
  }
}

# Associate Accepter Subnet to Accepter Route Table
resource "aws_route_table_association" "route_table_accepter" {
  provider       = aws.accepter
  subnet_id      = aws_subnet.subnet_accepter.id
  route_table_id = aws_route_table.route_table_accepter.id
}
Enter fullscreen mode Exit fullscreen mode
  • Vpc Peering.tf - This shows the vpc peering created in the different region, with the peering connection created in the Accepter region enabling its peering connection. The "provider" block must also be stated.
# Create VPC peering connection between both vpcs in different regions in the Requester Region
resource "aws_vpc_peering_connection" "requester" {
  provider    = aws.requester
  vpc_id      = aws_vpc.requester_vpc.id
  peer_vpc_id = aws_vpc.accepter_vpc.id
  peer_region = var.accepter_region
  #auto_accept = false

  tags = {
    Name = "peer_to_accepter"
  }

  depends_on = [
    aws_vpc.requester_vpc,
    aws_vpc.accepter_vpc
  ]
}

# Create VPC peering connection between VPC Peering in the Requester Region and the peering in the Accepter Region
resource "aws_vpc_peering_connection_accepter" "accepter" {
  provider                  = aws.accepter
  vpc_peering_connection_id = aws_vpc_peering_connection.requester.id
  auto_accept               = true

  tags = {
    Name = "peer_to_requester"
  }

  depends_on = [
    aws_vpc.requester_vpc,
    aws_vpc.accepter_vpc
  ]
}
Enter fullscreen mode Exit fullscreen mode
  • Variables.tf - This shows important the variables declared. Note: The ami ids are specific to each region, which makes them different in each region and also the key pairs are different for each region.
variable "access_key" {
  description = "The Access Key"
  default     = [{}]
}

variable "secret_key" {
  description = "The Secret Key"
  default     = [{}]
}

variable "requester_region" {
  type    = string
  default = "eu-west-1"
}

variable "accepter_region" {
  type    = string
  default = "us-east-1"
}

variable "vpc_cidr_block" {
  description = "VPC Cidr Block"
  default     = "10.0.0.0/16"
}

variable "peer_vpc_cidr_block" {
  description = "Peer VPC Cidr Block"
  default     = "10.1.0.0/16"
}

variable "requester_subnet_cidr_block" {
  description = "Requester Subnet Cidr Block"
  default     = "10.0.5.0/24"
}

variable "accepter_subnet_cidr_block" {
  description = "Accepter Subnet Cidr Block"
  default     = "10.1.5.0/24"
}

variable "instance_type" {
  type    = string
  default = "t2.micro"
}

variable "ami_id" {
  type    = string
  default = "ami-0136ddddd07f0584f"
}

variable "ami_key_pair_name" {
  type    = string
  default = "peering"
}

variable "ami_id_accepter" {
  type    = string
  default = "ami-0261755bbcb8c4a84"
}

variable "ami_key_pair_name_accepter" {
  type    = string
  default = "peering_2"
}
Enter fullscreen mode Exit fullscreen mode
  • Security.tf - This shows how any EC2 instance created in the different regions associated to the different vpcs communicate with each other. Security groups for both Requester Vpc and Accepter Vpc are created. It is also important to add the "provider" block for each security group as it ties to both vpc and specified region. Here we are testing the vpc peering created above.
# Create Security Group for EC2 Instance for Requester
resource "aws_security_group" "requester_security_group" {
  provider    = aws.requester
  name        = "requester_security_group"
  description = "requester security group"
  vpc_id      = aws_vpc.requester_vpc.id

  # Allow inbound traffic from Accepter CIDR block
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.accepter_vpc.cidr_block]
  }

  # Allow inbound traffic from Accepter CIDR block
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "udp"
    cidr_blocks = [aws_vpc.accepter_vpc.cidr_block]
  }

  # Allow inbound ICMP traffic from Accepter CIDR block
  ingress {
    from_port   = -1
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = [aws_vpc.accepter_vpc.cidr_block]
  }

  # Allow SSH for EC2 Connect or SSH from a terminal
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }


  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
  tags = {
    Name = "requester_security_group"
  }
}

# Create Security Group for EC2 Instance for Accepter
resource "aws_security_group" "accepter_security_group" {
  provider    = aws.accepter
  name        = "accepter_security_group"
  description = "accepter security group"
  vpc_id      = aws_vpc.accepter_vpc.id

  # Allow inbound traffic from Requester CIDR block
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.requester_vpc.cidr_block]
  }

  # Allow inbound traffic from Requester CIDR block
  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "udp"
    cidr_blocks = [aws_vpc.requester_vpc.cidr_block]
  }

  # Allow inbound ICMP traffic from Requester CIDR block
  ingress {
    from_port   = -1
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = [aws_vpc.requester_vpc.cidr_block]
  }

  # Allow SSH from Requester EC2 Instance
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.requester_vpc.cidr_block]
  }


  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]

  }
  tags = {
    Name = "accepter_security_group"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Ec2.tf - This shows the EC2 instances created to test the vpc peering connection associated with the different vpcs created in the different regions. Note the "provider" block, as it would tie the instances created to the specified region. Here we are testing the vpc peering created above. Note: Requester-server has both public and private ip and Accepter-server has a private ip only.
resource "aws_instance" "requester_server" {
  provider        = aws.requester
  ami             = var.ami_id
  instance_type   = var.instance_type
  key_name        = var.ami_key_pair_name # This is an existing key pair that has already been created in the specified region
  security_groups = [aws_security_group.requester_security_group.id]
  subnet_id       = aws_subnet.subnet_requester.id
  monitoring      = false

  tags = {
    Name = "requester_server"
  }
}

resource "aws_instance" "accepter_server" {
  provider        = aws.accepter
  ami             = var.ami_id_accepter
  instance_type   = var.instance_type
  key_name        = var.ami_key_pair_name_accepter # This is an existing key pair that has already been created in the specified region
  security_groups = [aws_security_group.accepter_security_group.id]
  subnet_id       = aws_subnet.subnet_accepter.id
  monitoring      = false

  tags = {
    Name = "accepter_server"
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing

This shows the outcome of the terraform script and also shows the communication between the vpcs.

  • terraform apply
    After Terraform Apply has been applied

  • Vpc Peering Connection Requester Region
    Vpc Peering Connection Requester Region

  • Vpc Peering Connection Accepter Region
    Vpc Peering Connection Accepter Region

  • VPC Requester Region
    VPC Requester

  • VPC Accepter Region
    VPC Accepter

  • Security Group Requester Region
    Requester Security Group

  • Security Group Accepter Region
    Accepter Security Group

  • EC2 Instance Requester Region
    Requester Ec2 Instance

  • EC2 Instance Accepter Region
    Accepter Ec2 Instance

  • Requester-server Communicating with Accepter-server
    Pinging the private ip of the Accepter instance.
    Requester-server pings Accepter-server ip

  • ssh into Accepter-server from Requester-server
    ssh into Accepter-server from Requester-server

  • Accepter-server Communicating with Requester-server
    Pinging the private ip of the Requester instance.
    Accepter-server pings Requester-server ip


Conclusion

Multi-Region VPC peering serves as a valuable solution for cases requiring secure sharing of resources, services, or data between different VPCs located in separate AWS regions. This functionality proves beneficial for various scenarios, like linking development and production environments, facilitating database sharing, and seamlessly integrating diverse applications within the AWS infrastructure. It offers an efficient approach to constructing intricate multi-tier architectures, ensuring scalability and isolation beyond the confines of a single region in AWS. Illustrated above is an example showing the connection between two EC2 instances located in different regions, establishing communication via Multi-Region VPC peering.

Top comments (0)