DEV Community

Cover image for Step-by-Step Guide to Setting Up a Secure and Scalable VPC on AWS Using Terraform
Shreya Nalawade
Shreya Nalawade

Posted on • Edited on

Step-by-Step Guide to Setting Up a Secure and Scalable VPC on AWS Using Terraform

Introduction to VPC

An logically isolated area of the Amazon Web Services (AWS) cloud where you can launch and use AWS resources can be created and managed using the AWS VPC (Amazon Virtual Private Cloud) web service offered by AWS. To put it simply, a VPC allows you to build your own private network inside the AWS cloud.

The IP address range, subnets, route tables, network gateways, and security settings of a VPC are completely under your control once you build it. With this level of control, you may design and set up the network environment to meet your individual demands and security requirements.

VPC


Subnets

Subnets in AWS VPC can be categorised as either public or private depending on how easily they can access the internet. Let's examine what distinguishes public from private subnets:

  • Public Subnet: An Internet Gateway (IGW) route is what makes a subnet a public subnet. A public subnet allows for instances to have public IP addresses and enable direct internet communication. This enables outbound traffic to the internet and makes them accessible from it. For resources that require direct internet connectivity, like web servers or instances hosting publicly available apps, public subnets are often used.

  • Private Subnet: An unconnected private subnet lacks a direct connection to the internet. To access resources on the internet, instances within a private subnet use a Network Address Translation (NAT) gateway or NAT instance because they lack public IP addresses. The fact that private subnets are inaccessible from the internet adds an extra layer of protection. They are frequently utilized for backend servers, databases, or other resources that demand internal VPC communication.

Route Tables

The traffic routing within a VPC is managed by a route table. It includes directives (routes) that describe how traffic should be routed across subnets, NAT gateways, VPN connections, and other resources.

Internet Gateway

A horizontally scalable, redundant AWS service called an Internet Gateway (IGW) enables connectivity between instances inside of your VPC and the internet. It gives internet-bound traffic a target and enables resources on public subnets to connect directly to the internet.

Security Groups

Security Groups act as virtual firewalls for your resources within a VPC. They control inbound and outbound traffic at the instance level by specifying rules that allow or deny traffic based on protocols, ports, and IP addresses.

To Explore more, visit AWS Documentation

"Secure VPC Setup with Terraform"

Terraform is an open-source Infrastructure as Code (IaC) tool that enables the automation and management of cloud infrastructure using a declarative configuration language. It allows users to define the desired state of their infrastructure and automates the process of provisioning and managing resources across various cloud providers such as AWS, Azure, and Google Cloud. With Terraform, infrastructure can be described in configuration files, making it easier to manage, version, and scale environments in a repeatable manner.

When Terraform is used to configure a Virtual Private Cloud (VPC) on AWS, it automates the creation and management of various networking components within the VPC. This includes defining the VPC itself, specifying CIDR blocks for IP address allocation, and enabling DNS support. Terraform also provisions subnets (public and private) to segregate different types of resources, such as instances requiring internet access and those that must remain isolated. Additionally, Terraform configures routing tables to manage the flow of traffic between subnets and the internet, using components like Internet Gateways (IGW) for public access and NAT Gateways for secure private subnet connectivity. It also sets up security groups to control inbound and outbound traffic for resources, ensuring proper isolation and protection. Through this automated process, Terraform ensures that the AWS environment is securely configured, scalable, and easily replicable across multiple instances or accounts.

Here's the recommended file structure for your Terraform project, organized for clarity and maintainability:

terraform-aws-vpc-setup/
├── main.tf               # Main Terraform configuration file
├── variables.tf         
├── outputs.tf           
├── provider.tf           
├── network/
│   ├── vpc.tf            
│   ├── subnets.tf        
│   ├── route_tables.tf 
│   └──nat_gateway.tf
    └── security_groups.tf
├── terraform.tfvars     
└── terraform.tfstate    

Enter fullscreen mode Exit fullscreen mode

Explanation of Each File:

main.tf:

The entry point for your Terraform configuration. It includes calls to modular files like network/.

module "network" {
  source = "./network"  # References the network module
}

output "vpc_id" {
  value = module.network.vpc_id
}

output "public_subnet_ids" {
  value = module.network.public_subnet_ids
}

output "private_subnet_ids" {
  value = module.network.private_subnet_ids
}


Enter fullscreen mode Exit fullscreen mode

variables.tf:

Contains definitions of input variables (e.g., VPC CIDR block, subnet CIDR blocks, region) to make the configuration reusable.

variable "region" {
  description = "AWS region for resource deployment"
  type        = string
  default     = "us-west-2"
}

variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "public_subnet_cidrs" {
  description = "List of CIDR blocks for public subnets"
  type        = list(string)
  default     = ["10.0.1.0/24"]
}

variable "private_subnet_cidrs" {
  description = "List of CIDR blocks for private subnets"
  type        = list(string)
  default     = ["10.0.2.0/24"]
}
Enter fullscreen mode Exit fullscreen mode

outputs.tf:

Exposes useful information such as VPC ID, subnet IDs, and NAT Gateway ID after the infrastructure is created.

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

output "public_subnet_ids" {
  value = aws_subnet.public_subnet.*.id
}

output "private_subnet_ids" {
  value = aws_subnet.private_subnet.*.id
}

Enter fullscreen mode Exit fullscreen mode

provider.tf:

Specifies the provider (AWS in this case) and the region for deployment.

provider "aws" {
  region = var.region
}
Enter fullscreen mode Exit fullscreen mode

network/vpc.tf:

Contains the VPC configuration.

resource "aws_vpc" "my_vpc" {
  cidr_block           = var.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "my_vpc"
  }
}

Enter fullscreen mode Exit fullscreen mode

network/subnets.tf:

Defines the public and private subnets.

resource "aws_subnet" "public_subnet" {
  count                  = length(var.public_subnet_cidrs)
  vpc_id                 = aws_vpc.my_vpc.id
  cidr_block             = var.public_subnet_cidrs[count.index]
  map_public_ip_on_launch = true
  availability_zone      = "us-west-2a"
  tags = {
    Name = "public_subnet_${count.index}"
  }
}

resource "aws_subnet" "private_subnet" {
  count                  = length(var.private_subnet_cidrs)
  vpc_id                 = aws_vpc.my_vpc.id
  cidr_block             = var.private_subnet_cidrs[count.index]
  availability_zone      = "us-west-2a"
  tags = {
    Name = "private_subnet_${count.index}"
  }
}

Enter fullscreen mode Exit fullscreen mode

network/route_tables.tf:

Contains route table definitions and associations.

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"
  }
}

resource "aws_route_table_association" "public_association" {
  count          = length(aws_subnet.public_subnet)
  subnet_id      = aws_subnet.public_subnet[count.index].id
  route_table_id = aws_route_table.public_route_table.id
}

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

resource "aws_route_table_association" "private_association" {
  count          = length(aws_subnet.private_subnet)
  subnet_id      = aws_subnet.private_subnet[count.index].id
  route_table_id = aws_route_table.private_route_table.id
}

Enter fullscreen mode Exit fullscreen mode

network/nat_gateway.tf:

Configures the NAT Gateway and its associated Elastic IP.

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

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

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
}

Enter fullscreen mode Exit fullscreen mode

network/security_groups.tf:

Defines the security groups for public and private resources.

resource "aws_security_group" "public_sg" {
  vpc_id = aws_vpc.my_vpc.id
  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"]
  }
  tags = {
    Name = "public_sg"
  }
}

resource "aws_security_group" "private_sg" {
  vpc_id = aws_vpc.my_vpc.id
  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 = []
  }
  tags = {
    Name = "private_sg"
  }
}

Enter fullscreen mode Exit fullscreen mode

terraform.tfvars:

Contains actual values for the variables defined in variables.tf (e.g., CIDR blocks, region, tags).

region = "us-west-2"
vpc_cidr = "10.0.0.0/16"
public_subnet_cidrs = ["10.0.1.0/24"]
private_subnet_cidrs = ["10.0.2.0/24"]

Enter fullscreen mode Exit fullscreen mode

terraform.tfstate:

Automatically generated by Terraform to track the state of the infrastructure. This file should be stored securely or in a remote backend.

  1. Set Up Terraform: Execute terraform init to prepare the working directory containing your configuration files. This step ensures that all necessary plugins and modules are downloaded.
  2. Preview Changes: Use terraform plan to review the modifications Terraform will make. This step allows you to confirm the accuracy of the configuration before proceeding.
  3. Deploy Resources: Run terraform apply to create the infrastructure defined in the configuration. When prompted, confirm to proceed with the deployment.

Top comments (0)