AWS Networking with Terraform: VPC Peering Connection between VPCs
Tags: aws, terraform, awscommunitybuilders, upskilling
Published: 2024-02-13 17:45:27

In our previous post, we started learning AWS networking concepts, culminating in the creation of a bastion host to access private hosts securely. 

Now, let's explore how to establish connectivity between two Virtual Private Clouds (VPCs) using VPC peering connections, leveraging Terraform modules for streamlined deployment.

A VPC peering connection is a networking connection between two VPCs that enables you to route traffic between them using private IPv4 addresses or IPv6 addresses. Instances in either VPC can communicate with each other as if they are within the same network. You can create a VPC peering connection between your own VPCs, or with a VPC in another AWS account. The VPCs can be in different Regions (also known as an inter-Region VPC peering connection).

## Architecture Overview:
Before diving into implementation, let's grasp the architecture we're about to construct:

![VPC Peering Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jxasgifluvhccgk3ex4m.png)

## Step 1: Create two VPCs with non-overlapping CIDRs.
Create vpc_a and vpc_b with non-overlapping cidrs. Please refer to my github repo in resources section below.
```terraform
####################################################
# Create two VPC and components
####################################################

module "vpc_a" {
  source               = "./modules/vpc"
  name                 = "VPC-A"
  aws_region           = var.aws_region
  vpc_cidr_block       = var.vpc_cidr_block_a #"10.1.0.0/16"
  public_subnets_cidrs = [cidrsubnet(var.vpc_cidr_block_a, 8, 1)]
  enable_dns_hostnames = var.enable_dns_hostnames
  aws_azs              = var.aws_azs
  common_tags          = local.common_tags
  naming_prefix        = local.naming_prefix
}


module "vpc_b" {
  source               = "./modules/vpc"
  name                 = "VPC-B"
  aws_region           = var.aws_region
  vpc_cidr_block       = var.vpc_cidr_block_b #"10.2.0.0/16"
  public_subnets_cidrs = [cidrsubnet(var.vpc_cidr_block_b, 8, 1)]
  enable_dns_hostnames = var.enable_dns_hostnames
  aws_azs              = var.aws_azs
  common_tags          = local.common_tags
  naming_prefix        = local.naming_prefix
}
```

## Step 2: Deploy EC2 instances within each VPC.
Create an EC2 instance in each VPC. Please refer to my github repo in resources section below.
```terraform
####################################################
# Create EC2 Server Instances
####################################################

module "vpc_a_public_host" {
  source        = "./modules/web"
  instance_type = var.instance_type
  instance_key  = var.instance_key
  subnet_id     = module.vpc_a.public_subnets[0]
  vpc_id        = module.vpc_a.vpc_id
  ec2_name      = "Public Host A"
  common_tags   = local.common_tags
  naming_prefix = local.naming_prefix
}

module "vpc_b_public_host" {
  source        = "./modules/web"
  instance_type = var.instance_type
  instance_key  = var.instance_key
  subnet_id     = module.vpc_b.public_subnets[0]
  vpc_id        = module.vpc_b.vpc_id
  ec2_name      = "Public Host B"
  common_tags   = local.common_tags
  naming_prefix = local.naming_prefix
}
```

## Step 3: Establish a peering connection to enable communication between the two VPCs.
Create a VPC peering connection and add a route in route table of each VPC.
```terraform
####################################################
# Create VPC Peering Connection
####################################################
resource "aws_vpc_peering_connection" "vpc_to_vpc" {
  vpc_id      = module.vpc_a.vpc_id
  peer_vpc_id = module.vpc_b.vpc_id
  auto_accept = true
  tags = merge(local.common_tags, {
    Name = "${local.naming_prefix}-vpc-peering"
  })
}

resource "aws_route" "peering_routes_a" {
  route_table_id            = module.vpc_a.public_route_table_id
  destination_cidr_block    = var.vpc_cidr_block_b
  vpc_peering_connection_id = aws_vpc_peering_connection.vpc_to_vpc.id
}

resource "aws_route" "peering_routes_b" {
  route_table_id            = module.vpc_b.public_route_table_id
  destination_cidr_block    = var.vpc_cidr_block_a
  vpc_peering_connection_id = aws_vpc_peering_connection.vpc_to_vpc.id
}
```

## Steps to Run Terraform
Follow these steps to execute the Terraform configuration:
```terraform
terraform init
terraform plan 
terraform apply -auto-approve
```

Upon successful completion, Terraform will provide relevant outputs.
```terraform
Apply complete! Resources: 17 added, 0 changed, 0 destroyed.

Outputs:

vpc_a_public_host_IP = "18.212.2.189"
vpc_b_public_host_IP = "3.84.14.236"
```

## Testing the outcome
VPCs Created with exclusive CIDRs
![VPCs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yuq360ns395ohhb61zyj.png)

Peering connections with route tables
![VPC Peering Connection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkls40ncb54ya3huchhj.png)

Route Tables showing rote to peering connection:

![Rote Table A](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znfh6sqyl8crdtmmv6uo.png)

![Route Table B](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ml17vt8bggs80ui5ehxt.png)

Connecting to EC2 in VPC-B from VPC-A
![VPC A to B](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/azobe94kgupd7bueaat2.png)

Connecting to EC2 in VPC-A from VPC-B
![VPC B to A](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrym2aq44rqx8xhrj9tq.png)


## Cleanup:
Remember to stop AWS components to avoid large bills.
```terraform
terraform destroy -auto-approve
```

In our upcoming module, we'll dive deeper into AWS networking, extending our setup to establish transit gateway VPC connections.

## Resources:
Github Link: https://github.com/chinmayto/terraform-aws-networking-vpc-peering
VPC Peering Documentation: https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html