In this project, we’ll dive into building a 3-tier AWS VPC architecture using Terraform. This architecture demonstrates how to span resources across multiple Availability Zones (AZs), create both public and private subnets, and configure NAT Gateways to enable secure internet access (e.g downloading software updates) for instances in private subnets via the Internet Gateway.
Prerequisites
- AWS Account: Active AWS account with permissions to create resources like VPCs, subnets, and EC2 instances.
- Terraform Installed: Install Terraform from the official website.
- AWS CLI
Note on Costs
Deploying this architecture will incur AWS costs, even when using free-tier eligible resources, due to the following:
- Elastic IPs: Costs associated with each NAT Gateway.
- NAT Gateways: Charges for usage and data transfer.
Tip: Use the AWS Pricing Calculator to estimate costs.
Architectural Diagram
Project Steps
Here’s a breakdown of how the 3-tier AWS VPC architecture was created.
Create the Network Infrastructure
- A VPC was set up with a CIDR block of
10.0.0.0/16
to host all the resources. - Public subnets were created in two Availability Zones (AZs) to host NAT Gateways and allow public-facing resources to access the internet.
-
Private subnets were configured:
- Application subnets to host the application instances.
- Database subnets for securely hosting database instances.
- An Internet Gateway was attached to the VPC to provide internet connectivity for resources in public subnets.
- NAT Gateways were deployed in the public subnets to enable secure internet access for private subnets, allowing both application and database instances to download software updates and libraries from the internet.
-
Route tables were created and associated with the subnets:
- Public subnets routed traffic directly through the Internet Gateway.
- Private subnets routed outbound traffic through the NAT Gateways.
Clean Up Resources
- Terraform’s
destroy
command ensured all resources (VPC, subnets, NAT Gateways, instances, and route tables) were systematically deleted once the deployment was no longer needed.
Terraform Implementation
Here’s the Terraform configurations used to set up the VPC, subnets, NAT gateways and route tables.
1. Providers and Variables
providers.tf
:
terraform {
required_version = ">= 1.7.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
variables.tf
:
Defined key variables for modularity and flexibility.
variable "project_name" {
type = string
description = "The name of the project"
default = "3-tier-architecture"
}
variable "aws_region" {
type = string
description = "The AWS region to create resources in"
default = "us-east-1"
}
variable "azs" {
type = list(string)
description = "The availability zones to create resources in"
default = ["us-east-1a", "us-east-1b"]
}
variable "vpc_cidr" {
type = string
description = "The CIDR block for the VPC"
default = "10.0.0.0/16"
}
variable "public_subnets" {
type = list(string)
description = "The public subnets to create"
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "app_subnets" {
type = list(string)
description = "The private subnets to host the application"
default = ["10.0.3.0/24", "10.0.4.0/24"]
}
variable "db_subnets" {
type = list(string)
description = "The private subnets to host the database"
default = ["10.0.5.0/24", "10.0.6.0/24"]
}
2. VPC and Subnets
network.tf
:
locals {
common_tags = {
Project = var.project_name
}
}
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(local.common_tags, { Name = "${var.project_name}-vpc" })
}
# Public Subnets
resource "aws_subnet" "public" {
for_each = toset(var.public_subnets)
vpc_id = aws_vpc.main.id
cidr_block = each.key
availability_zone = element(var.azs, index(var.public_subnets, each.key))
map_public_ip_on_launch = true
tags = merge(local.common_tags, { Name = "public-subnet-${each.key}" })
}
# App Subnets
resource "aws_subnet" "app-subnet" {
for_each = toset(var.app_subnets)
vpc_id = aws_vpc.main.id
cidr_block = each.key
availability_zone = element(var.azs, index(var.app_subnets, each.key))
tags = merge(local.common_tags, { Name = "app-subnet-${each.key}" })
}
# DB Subnets
resource "aws_subnet" "db-subnet" {
for_each = toset(var.db_subnets)
vpc_id = aws_vpc.main.id
cidr_block = each.key
availability_zone = element(var.azs, index(var.db_subnets, each.key))
tags = merge(local.common_tags, { Name = "db-subnet-${each.key}" })
}
3. Internet Gateway and NAT Gateways
nat_gateway.tf
:
# Internet Gateway
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = merge(local.common_tags, { Name = "${var.project_name}-igw" })
}
# Elastic IPs for NAT Gateways
resource "aws_eip" "nat_eip" {
for_each = toset(var.azs)
tags = merge(local.common_tags, { Name = "nat-eip-${each.key}" })
}
# NAT Gateways
resource "aws_nat_gateway" "nat" {
for_each = toset(var.azs)
allocation_id = aws_eip.nat_eip[each.key].id
subnet_id = aws_subnet.public[element(var.public_subnets, index(var.azs, each.key))].id
tags = merge(local.common_tags, { Name = "nat-gateway-${each.key}" })
}
4. Route Tables
routes.tf
:
# Public Route Table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "public-rt"
}
}
# Associate Public Subnets with Public Route Table
resource "aws_route_table_association" "public" {
for_each = aws_subnet.public
subnet_id = each.value.id
route_table_id = aws_route_table.public.id
}
# Private Route Tables for App and DB Subnets
resource "aws_route_table" "private" {
for_each = toset(var.azs)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat[each.key].id
}
tags = {
Name = "private-rt-${each.key}"
}
}
# Associate Private Subnets with Private Route Tables
resource "aws_route_table_association" "app" {
for_each = aws_subnet.app
subnet_id = each.value.id
route_table_id = element(aws_route_table.private[*].id, index(var.app_subnets, each.key))
}
resource "aws_route_table_association" "db" {
for_each = aws_subnet.db
subnet_id = each.value.id
route_table_id = element(aws_route_table.private[*].id, index(var.db_subnets, each.key))
}
Next Steps
- Deploy application EC2 instances in the private application subnets to host the application layer.
- Set up a database instance (e.g., Amazon RDS) in the private database subnets.
- Integrate an Elastic Load Balancer (ELB) and Auto Scaling Group (ASG) to ensure high availability and scalability for instances in the application layer.
Try It Yourself
- Clone the repository:
git clone https://github.com/bokal2/terraform-projects.git
cd 3-tier-architecture
- Follow the steps in README to deploy resources.
Thank you and please feel free to share your thoughts. Cheers!
Top comments (0)