AWS VPC 3-Tier Architecture using Terraform

Dwaipayan SomDwaipayan Som
7 min read

AWS services and components used in this project:

  1. Amazon Virtual Private Cloud (VPC)

  2. Internet Gateway

  3. NAT Gateway

  4. Elastic IP address

  5. Route table

  6. Public Subnet

  7. Private Subnet

Project Summary:

This project demonstrates the deployment of a 3-tier architecture on AWS using Terraform as the Infrastructure as Code (IaC) tool. The architecture consists of public and private subnets across two availability zones (AZs), ensuring high availability and fault tolerance. Below is an overview of the setup:

  1. VPC (Virtual Private Cloud):
    The project begins with the creation of a custom VPC to host all resources securely. This isolated virtual network allows for fine-grained control over the resources' access.

  2. Subnets:

    • Public Subnets: These subnets, located in both AZs, are attached to an Internet Gateway, allowing instances within them to communicate with the internet. Resources like NAT Gateways are deployed here.

    • Private Subnets: Both AZs have private subnets dedicated to hosting EC2 instances and databases. These subnets do not have direct internet access for enhanced security.

  3. Internet Gateway:
    An Internet Gateway is deployed to enable outbound communication from the public subnets, providing public instances the ability to connect to the internet.

  4. NAT Gateway:
    In each availability zone, a NAT Gateway is provisioned within the public subnets to enable instances in private subnets to access the internet for updates and patches, without exposing them to inbound internet traffic.

  5. Elastic IP Addresses:
    Each NAT Gateway is associated with an Elastic IP address, allowing consistent communication with external networks.

  6. Route Tables:
    The public subnets are associated with route tables that route internet-bound traffic via the Internet Gateway. Private subnets have route tables that route internet-bound traffic through the NAT Gateway, securing the instances while allowing outbound connections.

  7. Tiered Services:

    • Web Tier: Hosted in public subnets, it handles all internet-facing services.

    • Application Tier: EC2 instances in the private subnet manage business logic and communicate with the database layer.

    • Database Tier: Dedicated private subnets ensure secure and isolated database operations.

By leveraging Terraform, the entire infrastructure is automated and can be provisioned with minimal manual intervention, improving efficiency and scalability. The design ensures high availability, security, and flexibility, critical for production environments.

Let’s Dive In fellow enthusiasts,

Step-01: Introduction

  • Understand about Terraform Modules

  • Create VPC using Terraform Modules

  • Define Input Variables for VPC module and reference them in VPC Terraform Module

  • Define local values and reference them in VPC Terraform Module

  • Create terraform.tfvars to load variable values by default from this file

  • Create vpc.auto.tfvars to load variable values by default from this file related to a VPC

  • Define Output Values for VPC

Step-02: v1-vpc-module - Hardcoded Model

Step-02-01: How to make a decision of using the public Registry module?

  1. Understand about Terraform Registry and Modules

  2. We are going to use a VPC Module from Terraform Public Registry

  3. Understand about Authenticity of a module hosted on Public Terraform Registry with HashiCorp Verified Tag

  4. Review the download rate for that module

  5. Review the latest versions and release history of that module

  6. Review our feature needs when using that module and ensure if our need is satisfied use the module else use the standard terraform resource definition appraoch.

  7. Review module inputs, outputs and dependencies too.

Step-02-02: v1-vpc-module

  • c1-versions.tf

        # Terraform Block
        terraform {
          required_version = ">= 1.6" # which means any version equal & above 0.14 like 0.15, 0.16 etc and < 1.xx
          required_providers {
            aws = {
              source  = "hashicorp/aws"
              version = ">= 5.0"  
            }
          }
        }
    
        # Provider Block
        provider "aws" {
          region  = var.aws_region
          profile = "default"
        }
    
  • c2-generic-variables.tf

        # Input Variables
    
        # AWS Region
        variable "aws_region" {
          description = "Region in which AWS Resources to be created"
          type = string
          default = "us-east-1"  
        }
    
  • c3-vpc.tf

        # Create VPC Terraform Module
        module "vpc" {
          source  = "terraform-aws-modules/vpc/aws"
          version = "2.78.0"
    
          # VPC Basic Details
          name = "vpc-dev"
          cidr = "10.0.0.0/16"   
          azs                 = ["us-east-1a", "us-east-1b"]
          private_subnets     = ["10.0.1.0/24", "10.0.2.0/24"]
          public_subnets      = ["10.0.101.0/24", "10.0.102.0/24"]
    
          # Database Subnets
          create_database_subnet_group = true
          create_database_subnet_route_table= true
          database_subnets    = ["10.0.151.0/24", "10.0.152.0/24"]
    
          #create_database_nat_gateway_route = true
          #create_database_internet_gateway_route = true
    
          # NAT Gateways - Outbound Communication
          enable_nat_gateway = true
          single_nat_gateway = true
    
          # VPC DNS Parameters
          enable_dns_hostnames = true
          enable_dns_support = true
    
          public_subnet_tags = {
            Type = "public-subnets"
          }
    
          private_subnet_tags = {
            Type = "private-subnets"
          }
    
          database_subnet_tags = {
            Type = "database-subnets"
          }
    
          tags = {
            Owner = "som"
            Environment = "dev"
          }
    
          vpc_tags = {
            Name = "vpc-dev"
          }
        }
    
  • Terraform AWS VPC Module

Step-03: Execute Terraform Commands

# Working Folder
terraform-manifests/v1-vpc-module

# Terraform Initialize
terraform init
Observation:
1. Verify if modules got downloaded to .terraform folder

# Terraform Validate
terraform validate

# Terraform plan
terraform plan

# Terraform Apply
terraform apply -auto-approve
Observation:
1) Verify VPC
2) Verify Subnets
3) Verify IGW
4) Verify Public Route for Public Subnets
5) Verify no public route for private subnets
6) Verify NAT Gateway and Elastic IP for NAT Gateway
7) Verify NAT Gateway route for Private Subnets
8) Verify no public route or no NAT Gateway route to Database Subnets
9) Verify Tags

# Terraform Destroy
terraform destroy -auto-approve

# Delete Files
rm -rf .terraform*
rm -rf terraform.tfstate*

Step-04: Version Constraints in Terraform with Modules

  • Terraform Version Constraints

  • For modules locking to the exact version is recommended to ensure there will not be any major breakages in production

  • When depending on third-party modules, require specific versions to ensure that updates only happen when convenient to you

  • For modules maintained within your organization, specifying version ranges may be appropriate if semantic versioning is used consistently or if there is a well-defined release process that avoids unwanted updates.

  • Best practices

Step-05: v2-vpc-module-standardized - Standardized and Generalized

  • In the next series of steps we are going to standardize the VPC configuration

  • c1-versions.tf

        # Terraform Block
        terraform {
          required_version = ">= 1.6" # which means any version equal & above 0.14 like 0.15, 0.16 etc and < 1.xx
          required_providers {
            aws = {
              source  = "hashicorp/aws"
              version = ">= 5.0"
            }
          }
        }
    
        # Provider Block
        provider "aws" {
          region  = var.aws_region
          profile = "default"
        }
    
  • c2-generic-variables.tf

        # Input Variables
        # AWS Region
        variable "aws_region" {
          description = "Region in which AWS Resources to be created"
          type = string
          default = "us-east-1"  
        }
        # Environment Variable
        variable "environment" {
          description = "Environment Variable used as a prefix"
          type = string
          default = "dev"
        }
        # Business Division
        variable "business_divsion" {
          description = "Business Division in the large organization this Infrastructure belongs"
          type = string
          default = "HR"
        }
    
  • c3-local-values.tf

        # Define Local Values in Terraform
        locals {
          owners = var.business_divsion
          environment = var.environment
          name = "${var.business_divsion}-${var.environment}"
          common_tags = {
            owners = local.owners
            environment = local.environment     
          }
        }
    
  • c4-01-vpc-variables.tf

    GitHub Link

  • c4-02-vpc-module.tf

    GitHub Link

  • c4-03-vpc-outputs.tf

        # VPC Output Values
    
        # VPC ID
        output "vpc_id" {
          description = "The ID of the VPC"
          value       = module.vpc.vpc_id
        }
    
        # VPC CIDR blocks
        output "vpc_cidr_block" {
          description = "The CIDR block of the VPC"
          value       = module.vpc.vpc_cidr_block
        }
    
        # VPC Private Subnets
        output "private_subnets" {
          description = "List of IDs of private subnets"
          value       = module.vpc.private_subnets
        }
    
        # VPC Public Subnets
        output "public_subnets" {
          description = "List of IDs of public subnets"
          value       = module.vpc.public_subnets
        }
    
        # VPC NAT gateway Public IP
        output "nat_public_ips" {
          description = "List of public Elastic IPs created for AWS NAT Gateway"
          value       = module.vpc.nat_public_ips
        }
    
        # VPC AZs
        output "azs" {
          description = "A list of availability zones spefified as argument to this module"
          value       = module.vpc.azs
        }
    
  • terraform.tfvars

        # Generic Variables
        aws_region = "us-east-1"  
        environment = "dev"
        business_divsion = "HR"
    
  • vpc.auto.tfvars

        # VPC Variables
        vpc_name = "myvpc"
        vpc_cidr_block = "10.0.0.0/16"
        vpc_availability_zones = ["us-east-1a", "us-east-1b"]
        vpc_public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
        vpc_private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
        vpc_database_subnets= ["10.0.151.0/24", "10.0.152.0/24"]
        vpc_create_database_subnet_group = true 
        vpc_create_database_subnet_route_table = true   
        vpc_enable_nat_gateway = true  
        vpc_single_nat_gateway = true
    

Step-06: Execute Terraform Commands

# Working Folder
terraform-manifests/v2-vpc-module-standardized

# Terraform Initialize
terraform init

# Terraform Validate
terraform validate

# Terraform plan
terraform plan

# Terraform Apply
terraform apply -auto-approve
Observation:
1) Verify VPC
2) Verify Subnets
3) Verify IGW
4) Verify Public Route for Public Subnets
5) Verify no public route for private subnets
6) Verify NAT Gateway and Elastic IP for NAT Gateway
7) Verify NAT Gateway route for Private Subnets
8) Verify no public route or no NAT Gateway route to Database Subnets
9) Verify Tags

Step-13: Clean-Up

# Terraform Destroy
terraform destroy -auto-approve

# Delete Files
rm -rf .terraform*
rm -rf terraform.tfstate*

End Notes:

  1. Do not execute the ‘observation’ parts in your terminal.

  2. c4-01-vpc-variables.tf and c4-02-vpc-module.tf had some formatting issues since Hashnode doesn’t support HCL, and so I’ve put the github links to those files, please refer to them.

  3. Hashnode doesn’t have HCL option for pasting code so I have set it to plain text, save the files with the given respective extensions (.tf and .tfvars).

  4. GitHub Repository Link

Thank You for reading this far, I hope the blog helps in your terraform with AWS journey fellow enthusiast.

10
Subscribe to my newsletter

Read articles from Dwaipayan Som directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dwaipayan Som
Dwaipayan Som