Terraform create VPC Peering

October 23, 2023

terraformlogo

You can find source code in our repo

VPC Peering in AWS gives you opportunity to connect VPC. It is common used practise, in this article we will use terraform to connect our separate VPCs

Prerequisites:

  1. AWS Account: You must have an active AWS account. If you don't have one, you can sign up for an AWS account on the AWS website. You can create it here
  2. IAM User or Role: Create an IAM (Identity and Access Management) user or role in your AWS account with the necessary permissions to create and manage EC2 Instances. At a minimum, the user or role should have permissions to create EC2 instances, VPCs, and related resources.
  3. Terraform Installed: Install Terraform on your local machine. You can download Terraform from the official Terraform website and follow the installation instructions for your operating system here

Peering

What is VPC Peering? Connecting two different Virtual Networks in a cloud environment

In our example we will create 2 EC2 Instances in two different VPCs(A and B). First instance will be in Public subnet with a Public IP. Second instance will be in a private subnet, without allocated Public Ip, so we can only use it's private address.

VPC creation
module "vpc-a"{
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"

  name = "vpc-a"

  cidr = "10.0.0.0/16"
  azs  = ["us-east-1a", "us-east-1b"]

  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.3.0/24", "10.0.4.0/24"]

  enable_nat_gateway   = true
  single_nat_gateway   = true
  enable_dns_hostnames = true
}

module "vpc-b"{
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"

  name = "vpc-b"

  cidr = "10.1.0.0/16"
  azs  = ["us-east-1a", "us-east-1b"]

  private_subnets = ["10.1.1.0/24", "10.1.2.0/24"]
  public_subnets  = ["10.1.3.0/24", "10.1.4.0/24"]

  enable_nat_gateway   = true
  single_nat_gateway   = true
  enable_dns_hostnames = true
}

Note: To connect two VPCs with perring conection your Cird Blocks should not overlap, so we will use 10.0.0.0/16 and 10.1.0.0/16

For each VPC we will attach security groups allowing ingress traffic on HTTP port and SSH

Creating EC2
resource "tls_private_key" "ssh_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "local_file" "private_key" {
    content  = tls_private_key.ssh_key.private_key_pem
    filename = "ssh/ec2_key.pem"
    file_permission = "0600"
}

resource "aws_key_pair" "public_key" {
  key_name   = "EC2_key"
  public_key = tls_private_key.ssh_key.public_key_openssh
}

module "ec2-instance-a" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "5.5.0"

  name = "Instance-a"
  instance_type = "t3.small"
  subnet_id = module.vpc-a.public_subnets[0]
  associate_public_ip_address = true

  key_name = aws_key_pair.public_key.key_name
  vpc_security_group_ids = [aws_security_group.ssh-a.id]
}

module "ec2-instance-b" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "5.5.0"

  name = "Instance-b"
  instance_type = "t3.small"
  subnet_id = module.vpc-b.private_subnets[0]
  associate_public_ip_address = false
  user_data = file("shell.sh")

  vpc_security_group_ids = [aws_security_group.ssh-b.id]
}
  • We will create Instance A with a Public Ip address in a public subnet
  • We will create a new SSH key to connect to Instance A
  • Instance B would be created in VPC B in a private subnet
  • We will use user data to provide script for installing httpd server so we can check if we can get response from it using private ip
AWS VPC Peering Resources

Now we need to connect those two VPCs using the following code:

resource "aws_vpc_peering_connection" "foo" {
  peer_vpc_id   = module.vpc-a.vpc_id
  vpc_id        = module.vpc-b.vpc_id
  auto_accept   = true

  tags = {
    Name = "VPC Peering between vpc-a and vpc-b"
  }
}

resource "aws_route" "peering_routes-ab" {
  count                     = length(module.vpc-a.public_route_table_ids)
  route_table_id            = tolist(module.vpc-a.public_route_table_ids)[count.index]
  destination_cidr_block    = module.vpc-b.vpc_cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.foo.id
}

resource "aws_route" "peering_routes-ba" {
  count                     = length(module.vpc-b.private_route_table_ids)
  route_table_id            = tolist(module.vpc-b.private_route_table_ids)[count.index]
  destination_cidr_block    = module.vpc-a.vpc_cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.foo.id
}

output "Connect_to_instance_a" {
  description = "The public IP address assigned to the instance"
  value       = "ssh -i ${local_file.private_key.filename} ec2-user@${module.ec2-instance-a.public_ip}"
}

output "Private_ip_instance_b" {
  description = "The public IP address assigned to the instance"
  value       = module.ec2-instance-b.private_ip
}

aws_vpc_peering_connection - is used to create peering for both VPC. Need to provide both VPC ids
aws_route - after peering is created we need to update our Route Tables, in order to forward traffic. Since we will use VPC A Public Subnet and VPC B Private subnet in our example we would we would update only those.
Connect_to_instance - We will use this output to connect to our Public Instance A
Private_ip_instance_b - we will use to ssh to Instance A

Testing

After applying terraform code, connect to your EC2 Instance A, using command in your output:

ssh -i <PATH_TO_THE_KEY> ec2-user@<PUBLIC_IP>

After connected to Instance A use your second output to check connection:

curl <Private_ip_Instance_B>

You should get the following output:

<h1> Welcome to server B</h1>

You can find source code in our repo