Using Terraform to Create AWS VPC Peering
Tags: devops, aws, terraform, beginners
Published: 2023-08-21 08:47:08


Hi, Welcome to the first in the VPC Peering Series.
In this edition, you will learn how to create and peer VPCs together that are in the same region. This shows how to create VPC peering on AWS using terraform.

## What is AWS VPC Peering?
Amazon Web Services (AWS) Virtual Private Cloud (VPC) peering is a networking feature that allows you to connect two VPCs together, enabling communication between them as if they were on the same network. VPC peering is a private and secure connection, which means traffic between the peered VPCs stays within the AWS network and does not traverse the public internet.


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

- 
_Requester VPC_ - This is the VPC from which we are initiating a request to establish a peering connection with another VPC.

- 
_Accepter VPC_ - This refers to the VPC that will receive and accept the peering request from the requester VPC.

---
## Terraform Script
Terraform is an infrastructure as code (IaC) tool used to define and provision the creation of infrastructure resources.

- 
_**Provider.tf**_ - This shows the specific provider used for this script and in this case we are using AWS.
```
provider "aws" {
  access_key = var.access_key
  secret_key = var.secret_key
  region     = var.region
}

terraform {
  required_version = ">= 1.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}
```

- 
_**Vpc.tf**_ - This shows the creation of the respective vpcs (Requester Vpc and Accepter Vpc) and the Vpc peering connection.

```
# Create VPC for Requester
resource "aws_vpc" "requester_vpc" {
  cidr_block = var.vpc_cidr_block
  #  enable_dns_hostnames = true
  tags = {
    Name = "requester_vpc"
  }
}

# Create VPC for Accepter
resource "aws_vpc" "accepter_vpc" {
  cidr_block = var.peer_vpc_cidr_block
  #  enable_dns_hostnames = true
  tags = {
    Name = "accepter_vpc"
  }
}

# Create VPC peering connection between both vpcs
resource "aws_vpc_peering_connection" "vpc_peering_connection" {
  vpc_id      = aws_vpc.requester_vpc.id
  peer_vpc_id = aws_vpc.accepter_vpc.id
  auto_accept = true

  tags = {
    Name = "peering_connection"
  }

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

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

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

  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.vpc_peering_connection.id
  }
}

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


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

# Data for availability Zones
data "aws_availability_zones" "availability_zones" {}

# Create Subnet for Requester
resource "aws_subnet" "subnet_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"
  }
}

# Create Subnet for Accepter
resource "aws_subnet" "subnet_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.availability_zones.names[1]
  tags = {
    Name = "accepter-subnet"
  }
}

# Associate Requester Subnet to Requester Route Table
resource "aws_route_table_association" "route_table_requester" {
  subnet_id      = aws_subnet.subnet_requester.id
  route_table_id = aws_route_table.route_table_requester.id
}

# Associate Accepter Subnet to Accepter Route Table
resource "aws_route_table_association" "route_table_accepter" {
  subnet_id      = aws_subnet.subnet_accepter.id
  route_table_id = aws_route_table.route_table_accepter.id
}
```

- 
_**Variables.tf**_ - This shows the variables declared.
```
variable "access_key" {
  description = "The Access Key"
  default     = [{}]
}

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

variable "region" {
  type    = string
  default = "eu-west-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" #key pair has been created in this case
}
```

- 
_**Security.tf**_ - This shows how any EC2 instance created in the different vpcs communicate with each other. Security groups for both Requester Vpc and Accepter Vpc are created. _Here we are testing the vpc peering created above_.

```
# Create Security Group for EC2 Instance for Requester
resource "aws_security_group" "requester_security_group" {
  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" {
  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"
  }
}
```

- 
_**Ec2.tf**_ - This shows the EC2 instances created to test the vpc peering connection associated with the different vpcs created. Ec2 instances for both Requester Vpc and Accepter Vpc are created. _Here we are testing the vpc peering created above_. <u>Note: Requester-server has both public and private ip and Accepter-server has a private ip only.</u>

```
resource "aws_instance" "requester_server" {
  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
  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" {
  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
  security_groups = [aws_security_group.accepter_security_group.id]
  subnet_id       = aws_subnet.subnet_accepter.id
  monitoring      = false

  tags = {
    Name = "accepter_server"
  }
}
```

---
## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iuoh6r9wfhdbe361nzb1.png)

- 
_**Vpc Peering Connection**_ 
![Vpc Peering Connection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akt2tr1bt691jlqo0b1c.png)

- 
_**VPCs**_ 
![VPCs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5qui2c6oc3pvauu3fry.png)

- 
_**Security Groups**_ 
![Security Groups](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/piilw59415s89y2u3ifi.png)

- 
_**EC2 Instances**_ 
![Ec2 Instances](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kfm2936zzpr17nyvp9cd.png)

- 
_**Requester-server Communicating with Accepter-server**_ 
Pinging the private ip of the Accepter instance.
![Requester-server pings Accepter-server ip](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mhocc825fkvppt8sh33.png)

- 
ssh into Accepter-server from Requester-server
![ssh into Accepter-server from Requester-server](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/umq3tf7mdb9h4bh93wp3.png)

- 
_**Accepter-server Communicating with Requester-server**_ 
Pinging the private ip of the Requester instance.
![Accepter-server pings Requester-server ip](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cbi461u0ssd3wzd87ohm.png)

---
## Conclusion
VPC peering is useful for scenarios where you want to share resources, services, or data securely between separate VPCs, such as connecting development and production environments, sharing databases, or integrating different applications within your AWS infrastructure. It provides a convenient way to build complex multi-tier architectures in a scalable and isolated manner. As shown above, its shows the connection between 2 ec2 instances on different VPCs connecting with each other via the VPC Peering.