DEV Community

Cover image for Day15: Mini-Project: 2 VPC-Peering
Anil KUMAR
Anil KUMAR

Posted on

Day15: Mini-Project: 2 VPC-Peering

Today marks the Day 15 of 30days of aws terraform challenge initiative by Piyush Sachdev. Today we will deep into the concept of VPC Peering. What exactly is VPC-Peering, what are its use-cases and why we need that in the first place.

VPC-Peering:

As we all know VPC is nothing but a Virtual Private Cloud where we will be hosting our apps or services within the assigned IP addresses of that VPC. Suppose you have 2 VPC's in 2 different regions and you want to have communication between them.

Like app service inside a EC2 instance of one region VPC needs to communicate with DB service of another region VPC. Then you need to establish VPC-Peering for communication to happen, else there will be no communication happening between them.

Simply, In the world of cloud infrastructure, network isolation is the default. But what happens when your App service in us-east-1 needs to fetch data from a database in us-west-2

Routing this traffic over the public internet is slow, insecure, and expensive (NAT Gateway costs add up!). The solution is VPC Peering - a networking connection that allows two VPCs to communicate as if they are in the same network.

Low-Level Architecture (LLD):

Before we write code, let's visualize the packet flow. Below is the architecture we are building. Note how traffic flows privately through the AWS backbone, bypassing the public internet entirely for inter-VPC communication.

  1. The Traffic Flow: EC2 Instance (East) sends a packet to 10.1.x.x (West).
  2. Route Table (East): sees the destination is the Peering Connection (pcx-id), not the Internet Gateway.
  3. AWS Backbone routes traffic securely across regions.
  4. Security Group (West) validates the inbound request (is it from 10.0.0.0/16?).
  5. EC2 Instance (West) processes the request.

Below are the resources that will be created during this project:

Networking Components

Two VPCs:

Primary VPC in us-east-1 (10.0.0.0/16)
Secondary VPC in us-west-2 (10.1.0.0/16)

Subnets:

One public subnet in each VPC
Configured with auto-assign public IP

Internet Gateways:

One for each VPC to allow internet access

Route Tables:

Custom route tables with routes to internet and peered VPC
Routes for VPC peering traffic.

VPC Peering Connection:

Cross-region peering between the two VPCs
Automatic acceptance configured

Compute Resources

EC2 Instances:

One t2.micro instance in each VPC
Running Amazon Linux 2
Apache web server installed
Custom web page showing VPC information

Security Groups:

SSH access from anywhere (port 22)
ICMP (ping) allowed from peered VPC
All TCP traffic allowed between VPCs

The Terraform Implementation(Step-by-Step):

Creating 2 new key-pairs for both Ec2 Instances which will be created before:

# For us-east-1
aws ec2 create-key-pair --key-name vpc-peering-demo --region us-east-1 --query 'KeyMaterial' --output text > vpc-peering-demo.pem

# For us-west-2
aws ec2 create-key-pair --key-name vpc-peering-demo --region us-west-2 --query 'KeyMaterial' --output text > vpc-peering-demo.pem

# Set permissions (on Linux/Mac)
chmod 400 vpc-peering-demo.pem
Enter fullscreen mode Exit fullscreen mode

provider.tf:

provider "aws" {
  region = "us-east-1"
  alias  = "primary"
}

provider "aws" {
  region = "us-west-2"
  alias  = "secondary"
}
Enter fullscreen mode Exit fullscreen mode

Creating 2 VPC's:

# Primary VPC in us-east-1
resource "aws_vpc" "primary_vpc" {
  provider             = aws.primary
  cidr_block           = var.primary_vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "Primary-VPC-${var.primary_region}"
    Environment = "Demo"
    Purpose     = "VPC-Peering-Demo"
  }
}

# Secondary VPC in us-west-2
resource "aws_vpc" "secondary_vpc" {
  provider             = aws.secondary
  cidr_block           = var.secondary_vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "Secondary-VPC-${var.secondary_region}"
    Environment = "Demo"
    Purpose     = "VPC-Peering-Demo"
  }
}
Enter fullscreen mode Exit fullscreen mode

The above block creates 2 new VPC's in 2 different regions with different CIDR's block.

Creating 2 Subnets:

# Subnet in Primary VPC
resource "aws_subnet" "primary_subnet" {
  provider                = aws.primary
  vpc_id                  = aws_vpc.primary_vpc.id
  cidr_block              = var.primary_subnet_cidr
  availability_zone       = data.aws_availability_zones.primary.names[0]
  map_public_ip_on_launch = true

  tags = {
    Name        = "Primary-Subnet-${var.primary_region}"
    Environment = "Demo"
  }
}

# Subnet in Secondary VPC
resource "aws_subnet" "secondary_subnet" {
  provider                = aws.secondary
  vpc_id                  = aws_vpc.secondary_vpc.id
  cidr_block              = var.secondary_subnet_cidr
  availability_zone       = data.aws_availability_zones.secondary.names[0]
  map_public_ip_on_launch = true

  tags = {
    Name        = "Secondary-Subnet-${var.secondary_region}"
    Environment = "Demo"
  }
}
Enter fullscreen mode Exit fullscreen mode

The above block creates 2 subnets in 2 different regions within the VPC CIDR block.

Creating Internet Gateways:

# Internet Gateway for Primary VPC
resource "aws_internet_gateway" "primary_igw" {
  provider = aws.primary
  vpc_id   = aws_vpc.primary_vpc.id

  tags = {
    Name        = "Primary-IGW"
    Environment = "Demo"
  }
}

# Internet Gateway for Secondary VPC
resource "aws_internet_gateway" "secondary_igw" {
  provider = aws.secondary
  vpc_id   = aws_vpc.secondary_vpc.id

  tags = {
    Name        = "Secondary-IGW"
    Environment = "Demo"
  }
}
Enter fullscreen mode Exit fullscreen mode

The above block creates 2 Internet Gateways associating with the already existing VPC in 2 regions.

Creating Route Tables:

# Route table for Primary VPC
resource "aws_route_table" "primary_rt" {
  provider = aws.primary
  vpc_id   = aws_vpc.primary_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.primary_igw.id
  }

  tags = {
    Name        = "Primary-Route-Table"
    Environment = "Demo"
  }
}

# Route table for Secondary VPC
resource "aws_route_table" "secondary_rt" {
  provider = aws.secondary
  vpc_id   = aws_vpc.secondary_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.secondary_igw.id
  }

  tags = {
    Name        = "Secondary-Route-Table"
    Environment = "Demo"
  }
}
Enter fullscreen mode Exit fullscreen mode

The above block demonstrates aws_route_table for creating Route tables and creating a route for internet gateway.

Creating Route_table_association

# Associate route table with Primary subnet
resource "aws_route_table_association" "primary_rta" {
  provider       = aws.primary
  subnet_id      = aws_subnet.primary_subnet.id
  route_table_id = aws_route_table.primary_rt.id
}

# Associate route table with Secondary subnet
resource "aws_route_table_association" "secondary_rta" {
  provider       = aws.secondary
  subnet_id      = aws_subnet.secondary_subnet.id
  route_table_id = aws_route_table.secondary_rt.id
}
Enter fullscreen mode Exit fullscreen mode

In this block, we are associating route tables with the subnets.

VPC Peering Connection:

# VPC Peering Connection (Requester side - Primary VPC)
resource "aws_vpc_peering_connection" "primary_to_secondary" {
  provider    = aws.primary
  vpc_id      = aws_vpc.primary_vpc.id
  peer_vpc_id = aws_vpc.secondary_vpc.id
  peer_region = var.secondary_region
  auto_accept = false

  tags = {
    Name        = "Primary-to-Secondary-Peering"
    Environment = "Demo"
    Side        = "Requester"
  }
}

# VPC Peering Connection Accepter (Accepter side - Secondary VPC)
resource "aws_vpc_peering_connection_accepter" "secondary_accepter" {
  provider                  = aws.secondary
  vpc_peering_connection_id = aws_vpc_peering_connection.primary_to_secondary.id
  auto_accept               = true

  tags = {
    Name        = "Secondary-Peering-Accepter"
    Environment = "Demo"
    Side        = "Accepter"
  }
}
Enter fullscreen mode Exit fullscreen mode

VPC Peering is a two-step process: Request and Accept.

The Requester (aws_vpc_peering_connection): Initiates the call from the Primary VPC. We must specify the peer_region because this is a cross-region peer.

The Accepter (aws_vpc_peering_connection_accepter): Lives in the Secondary region. It "picks up the phone" and establishes the tunnel.

Adding routes for VPC-Peering

# Add route to Secondary VPC in Primary route table
resource "aws_route" "primary_to_secondary" {
  provider                  = aws.primary
  route_table_id            = aws_route_table.primary_rt.id
  destination_cidr_block    = var.secondary_vpc_cidr
  vpc_peering_connection_id = aws_vpc_peering_connection.primary_to_secondary.id

  depends_on = [aws_vpc_peering_connection_accepter.secondary_accepter]
}

# Add route to Primary VPC in Secondary route table
resource "aws_route" "secondary_to_primary" {
  provider                  = aws.secondary
  route_table_id            = aws_route_table.secondary_rt.id
  destination_cidr_block    = var.primary_vpc_cidr
  vpc_peering_connection_id = aws_vpc_peering_connection.primary_to_secondary.id

  depends_on = [aws_vpc_peering_connection_accepter.secondary_accepter]
}
Enter fullscreen mode Exit fullscreen mode

Creating the peering connection is not enough. You must tell the VPCs how to use it. We modify the Route Tables in both VPCs to point traffic destined for the other VPC's CIDR block to the peering connection.

Many engineers create the peering link but forget to update the Route Tables. Without these routes, packets will be dropped.

Now create terraform blocks for Security Group and EC2 Instances creation too. I was not including that here as it would make blog bigger. We will be using Data Sources block in EC2 instance creation for availability zones and AMI.

Deployment & Verification

Once all the files were run, run the below 3 commands for creation of Infra.

terraform init
terraform plan
terraform apply
Enter fullscreen mode Exit fullscreen mode

After running terraform plan or apply, you could see 18 resources to be created.

Plan: 18 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + primary_instance_id           = (known after apply)
  + primary_instance_private_ip   = (known after apply)
  + primary_instance_public_ip    = (known after apply)
  + primary_vpc_cidr              = "10.0.0.0/16"
  + primary_vpc_id                = (known after apply)
  + secondary_instance_id         = (known after apply)
  + secondary_instance_private_ip = (known after apply)
  + secondary_instance_public_ip  = (known after apply)
  + secondary_vpc_cidr            = "10.1.0.0/16"
  + secondary_vpc_id              = (known after apply)
  + test_connectivity_command     = (known after apply)
  + vpc_peering_connection_id     = (known after apply)
  + vpc_peering_status            = (known after apply)

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Enter fullscreen mode Exit fullscreen mode

The below images shows the creation of VPC Peering Connection from Primary to Secondary.

You could see the creation of 2 EC2 instances created through Terraform.


ubuntu@ip-10-0-1-126:~$ ping 10.1.1.113
PING 10.1.1.113 (10.1.1.113) 56(84) bytes of data.
64 bytes from 10.1.1.113: icmp_seq=1 ttl=64 time=63.0 ms
64 bytes from 10.1.1.113: icmp_seq=2 ttl=64 time=62.8 ms
64 bytes from 10.1.1.113: icmp_seq=3 ttl=64 time=62.9 ms
64 bytes from 10.1.1.113: icmp_seq=4 ttl=64 time=62.8 ms
64 bytes from 10.1.1.113: icmp_seq=5 ttl=64 time=62.8 ms
^C
--- 10.1.1.113 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 62.752/62.830/63.021/0.101 ms
ubuntu@ip-10-0-1-126:~$ 
ubuntu@ip-10-0-1-126:~$ 
ubuntu@ip-10-0-1-126:~$ curl 10.1.1.113
<h1>Secondary VPC Instance - us-west-2</h1>
<p>Private IP: 10.1.1.113 </p
Enter fullscreen mode Exit fullscreen mode

You could see that I have Pinged the secondary EC2 instance from Primary Ec2 instance and we are able to connect between them.

You could also see while trying to access Primary EC2 instance from Secondary one.

Make sure to terminate all the resources once the project is done.

terraform destroy --auto-approve
Enter fullscreen mode Exit fullscreen mode

Conclusion

This marks the Day 15 of 30 Days of AWS Terraform challenge. Today we have done a mini-project on VPC Peering Connection by trying to establish connection between 2 VPC's in 2 different regions.

Top comments (0)