Creating a Secure and Scalable AWS VPC with Terraform

Ayushmaan Srivastav
4 min readSep 12, 2024

--

In this blog post, we’ll walk through the process of setting up a Virtual Private Cloud (VPC) on AWS using Terraform. This setup includes creating public and private subnets, routing tables, a NAT gateway, and security groups to ensure a secure and scalable network environment.

Prerequisites

Before we dive into the Terraform configuration, make sure you have the following:

  • An AWS account.
  • Terraform installed on your local machine.
  • Basic understanding of AWS networking components and Terraform.

Terraform Configuration Breakdown

We’ll be creating the following AWS resources:

  1. VPC: The virtual network where our resources will reside.
  2. Subnets: Public and private subnets to separate different types of resources.
  3. Internet Gateway (IGW): Allows instances in the public subnet to access the internet.
  4. NAT Gateway: Provides internet access to instances in the private subnet.
  5. Route Tables: Direct traffic between subnets and the internet.
  6. Security Groups: Define inbound and outbound rules for instances.

Here’s a detailed breakdown of the Terraform configuration:

1. Define AWS Provider:

provider "aws" {
region = "us-west-2" # Change to your preferred region
}

This block specifies the AWS region where resources will be created. Adjust the region based on your requirements.

2. Create VPC:

resource "aws_vpc" "my_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "my_vpc"
}
}

The VPC will have a CIDR block of 10.0.0.0/16, allowing us to create a large number of IP addresses. DNS support and hostnames are enabled for easier management.

3. Create Internet Gateway:

resource "aws_internet_gateway" "my_igw" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "my_igw"
}
}

The Internet Gateway enables resources in the public subnet to access the internet.

4. Create Subnets

Public Subnet

resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a" # Change as needed
map_public_ip_on_launch = true
tags = {
Name = "public_subnet"
}
}

The public subnet is configured to automatically assign public IP addresses to instances upon launch.

Private Subnet

resource "aws_subnet" "private_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-west-2a" # Change as needed
tags = {
Name = "private_subnet"
}
}

The private subnet does not have direct internet access.

5. Create Route Tables

Public Route Table

resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.my_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.my_igw.id
}
tags = {
Name = "public_route_table"
}
}

This route table directs all outbound traffic (0.0.0.0/0) to the Internet Gateway.

Private Route Table

resource "aws_route_table" "private_route_table" {
vpc_id = aws_vpc.my_vpc.id
}

The private route table is initially empty. It will be updated to route traffic through a NAT Gateway.

6. Create NAT Gateway

resource "aws_eip" "nat_eip" {
vpc = true
}

An Elastic IP (EIP) is required for the NAT Gateway.

resource "aws_nat_gateway" "my_nat_gateway" {
allocation_id = aws_eip.nat_eip.id
subnet_id = aws_subnet.public_subnet.id
tags = {
Name = "my_nat_gateway"
}
}

The NAT Gateway is placed in the public subnet and uses the Elastic

IP created above to provide internet access to the private subnet.

7. Update Private Route Table

resource "aws_route" "private_route" {
route_table_id = aws_route_table.private_route_table.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.my_nat_gateway.id
}

This route directs outbound traffic from the private subnet through the NAT Gateway.

8. Associate Route Tables with Subnets

Public Subnet Association

resource "aws_route_table_association" "public_subnet_association" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.public_route_table.id
}

Associates the public subnet with the public route table.

Private Subnet Association

resource "aws_route_table_association" "private_subnet_association" {
subnet_id = aws_subnet.private_subnet.id
route_table_id = aws_route_table.private_route_table.id
}

9. Create Security Groups

Private Security Group

resource "aws_security_group" "private_sg" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "private_sg"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = []
}
}

The private security group allows all outbound traffic but no inbound traffic.

Public Security Group

resource "aws_security_group" "public_sg" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "public_sg"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

The public security group allows all inbound and outbound traffic.

10. Output Values

output "vpc_id" {
value = aws_vpc.my_vpc.id
}

output "public_subnet_id" {
value = aws_subnet.public_subnet.id
}

output "private_subnet_id" {
value = aws_subnet.private_subnet.id
}

output "nat_gateway_id" {
value = aws_nat_gateway.my_nat_gateway.id
}

These output values provide important information about the resources created, which can be useful for integration with other configurations or for troubleshooting.

Applying the Configuration

  1. Initialize Terraform: Run terraform init to initialize the working directory containing the configuration files.
  2. Plan the Deployment: Run terraform plan to see what changes will be made. This step helps verify the configuration before applying.
  3. Apply the Configuration: Run terraform apply to create the resources defined in the configuration file. Confirm the action when prompted.

Conclusion

In this blog post, we have covered how to set up a VPC with public and private subnets using Terraform. This setup provides a secure and scalable network architecture with internet access capabilities for both public and private instances. By following these steps, you can ensure your AWS environment is well-structured and ready for deploying various applications.

Feel free to adjust the CIDR blocks, availability zones, and other parameters based on your specific requirements. With Terraform, you can easily manage and scale your infrastructure as your needs evolve.

--

--

No responses yet