First, create variables.tf. This file allows us to define the variables we need—a VPC CIDR block, private subnets, and public subnets.
variable "vpc_cidr_block" {
description = "CIDR of vpc"
type = string
}
variable "public_subnets" {
description = "Map of public subnets that should be created"
type = map(object({
cidr_block = string
availability_zone = string
}))
}
variable "private_subnets" {
description = "Map of private subnets that should be created"
type = map(object({
cidr_block = string
availability_zone = string
}))
}
variable "application_name" {
description = "Name of the application"
type = string
}
Next, we'll provide the variable definitions in terraform.tfvars.
vpc_cidr_block = "10.0.0.0/24"
public_subnets = {
subnet_1 = {
cidr_block = "10.0.0.0/26"
availability_zone = "us-east-1a"
}
subnet_2 = {
cidr_block = "10.0.0.64/26"
availability_zone = "us-east-1b"
}
}
private_subnets = {
subnet_1 = {
cidr_block = "10.0.0.128/26"
availability_zone = "us-east-1a"
}
subnet_2 = {
cidr_block = "10.0.0.192/26"
availability_zone = "us-east-1b"
}
}
application_name = "serverless-jenkins-on-ecs"
Now, create vpc.tf and create the VPC first. An AWS VPC (Virtual Private Cloud) is a virtual network logically isolated from other virtual networks in the AWS Cloud, providing you with control over IP addresses, subnets, route tables, and network gateways.
# VPC
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr_block
enable_dns_hostnames = true
tags = {
Name = var.application_name
}
}
Create the public subnets. What makes these subnets public is that they will have a route to an Internet Gateway (IGW), which allows resources within the subnet to communicate directly with the Internet.
# Public Subnets
resource "aws_subnet" "public" {
for_each = var.public_subnets
vpc_id = aws_vpc.this.id
cidr_block = each.value.cidr_block
availability_zone = each.value.availability_zone
map_public_ip_on_launch = true
tags = {
Name = format("public-%s-%s", var.application_name, each.value.availability_zone)
}
}
Create the IGW and associate it with the VPC. We'll reference it later in our route table that we'll connect to our public subnets.
# IGW
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
}
Create a route table for your public subnet and add a route to funnel traffic through the IGW. After which, create the route table association to wire up the IGW with the public subnets.
# Public Route Table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.this.id
tags = {
Name = "public"
}
}
# Add IGW Route
resource "aws_route" "public" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}
# Associate Route Table with Subnet
resource "aws_route_table_association" "public" {
for_each = aws_subnet.public
subnet_id = each.value.id
route_table_id = aws_route_table.public.id
}
Next, create the private subnets. As the name implies, these subnets are inaccessible from the outside world.
# Private Subnets
resource "aws_subnet" "private" {
for_each = var.private_subnets
vpc_id = aws_vpc.this.id
cidr_block = each.value.cidr_block
availability_zone = each.value.availability_zone
tags = {
Name = format("private-%s-%s", var.application_name, each.value.availability_zone)
}
}
Now, let's create an Elastic IP (EIP) and a NAT Gateway. We need the EIP, as AWS requires one as part of the NAT Gateway creation. The NAT Gateway is necessary as that's how the private subnets can communicate with the network. Imagine that you have RHEL EC2s that need to receive yum updates. You'll need a NAT Gateway.
# EIP for NAT Gateway
resource "aws_eip" "this" {
for_each = aws_subnet.private
}
# NAT Gateway
resource "aws_nat_gateway" "this" {
for_each = aws_subnet.private
subnet_id = aws_subnet.public[each.key].id
allocation_id = aws_eip.this[each.key].id
tags = {
Name = format("private-%s-%s", var.application_name, each.value.availability_zone)
}
}
Next, we'll create the route table, the route to associate the NAT Gateway to the private subnets, and the route table association.
# Private Route Table
resource "aws_route_table" "private" {
for_each = aws_subnet.private
vpc_id = aws_vpc.this.id
tags = {
Name = format("private-%s-%s", var.application_name, each.value.availability_zone)
}
}
# Add Route - Private Subnets to NAT Gateway
resource "aws_route" "private" {
for_each = aws_subnet.private
route_table_id = aws_route_table.private[each.key].id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this[each.key].id
}
# Associate Private RT with Private Subnets
resource "aws_route_table_association" "private" {
for_each = aws_subnet.private
subnet_id = each.value.id
route_table_id = aws_route_table.private[each.key].id
}
You are ready. Run the following commands.
# Initialize Terraform
terraform init
# Check and see what will be created
terraform plan
# Let's do this!
terraform apply
Now, navigate to the AWS console and search for VPC.
Scroll down to the VPC resource map.
Click on subnets next.
Also, check out your Route Tables, Internet Gateways, NAT Gateways, and Elastic IPs.
Fin.