DEV Community

Habil BOZALİ
Habil BOZALİ

Posted on • Originally published at habil.dev on

Building a Private AWS VPC with Terraform + S3

Introduction

Building a Private AWS VPC with Terraform + S3

Terraform is an open-source infrastructure as code (IaC) tool developed by HashiCorp. It allows you to define and manage your infrastructure resources using a declarative configuration language. With Terraform, you can describe your desired infrastructure state in code, and Terraform will automatically provision and manage the resources needed to reach that state.

Here are some key reasons why you might need Terraform:

  1. Infrastructure Automation
  2. Cross-Platform Compatibility
  3. Infrastructure Versioning and Collaboration
  4. Infrastructure as Code (IaC) Best Practices
  5. Scalability and Consistency

Overall, Terraform simplifies infrastructure management, reduces manual effort, and provides a systematic approach to infrastructure provisioning. It is an essential tool for organizations adopting cloud computing, microservices architectures, or seeking to automate their infrastructure operations.

I'll write some Terraform codes for private AWS VPC creation in this article.

Requirements

Sample Code

I'll save all terraform states in an S3 bucket. If you will follow the article, please make sure you create the bucket just before the execution. Also, you need to make sure your AWS credentials are correct.

main.tf:

This is our main entry point for the terraform codes. The following code block provides 3 main things.

  • s3 configuration for the state
  • Terraform provider plugin
  • Module source (All related tf files need to be in the VPC directory.)
terraform {
  required_version = ">= 0.12.0"
  backend "s3" {
    bucket = "habil-dev-bucket"
    key = "prod/vpc/habil-dev.tfstate"
    region = "eu-central-1"
  }
}

provider "aws" {
  version = "~> 4.0"
  region = "eu-central-1"
}

module "vpc" {
  source = "./vpc"
}
Enter fullscreen mode Exit fullscreen mode

vpc.tf:

This file contains the CIDR block and other DNS-specific settings.

resource "aws_vpc" "vpc" {
  cidr_block = "10.20.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support = true
  assign_generated_ipv6_cidr_block = true

  tags = {
    Name = "habil-dev-vpc"
    Environment = "dev"
  }
}
Enter fullscreen mode Exit fullscreen mode

vpc_subnets.tf

This file contains public and private subnets.

// Public Subnets

resource "aws_subnet" "sn-1a-public-xlb" {
  vpc_id = aws_vpc.vpc.id
  cidr_block = "10.20.96.0/24"
  availability_zone = "eu-central-1a"

  tags = {
    Name = "habil-dev-1a-public-xlb"
    CostCenter = "habil-dev-1a-public-xlb"
  }
}

resource "aws_subnet" "sn-1b-public-xlb" {
  vpc_id = aws_vpc.vpc.id
  cidr_block = "10.20.97.0/24"
  availability_zone = "eu-central-1b"

  tags = {
    Name = "habil-dev-1b-public-xlb"
    CostCenter = "habil-dev-1b-public-xlb"
  }
}

resource "aws_subnet" "sn-1c-public-xlb" {
  vpc_id = aws_vpc.vpc.id
  cidr_block = "10.20.98.0/24"
  availability_zone = "eu-central-1c"

  tags = {
    Name = "habil-dev-1c-public-xlb"
    CostCenter = "habil-dev-1c-public-xlb"
  }
}

// Private Generic Subnets

resource "aws_subnet" "sn-1a-private-generic" {
  vpc_id = aws_vpc.vpc.id
  cidr_block = "10.20.104.0/21"
  availability_zone = "eu-central-1a"

  tags = {
    Name = "habil-dev-1a-private-generic"
    CostCenter = "habil-dev-1a-private-generic"
  }
}

resource "aws_subnet" "sn-1b-private-generic" {
  vpc_id = aws_vpc.vpc.id
  cidr_block = "10.20.112.0/21"
  availability_zone = "eu-central-1b"

  tags = {
    Name = "habil-dev-1b-private-generic"
    CostCenter = "habil-dev-1b-private-generic"
  }
}

resource "aws_subnet" "sn-1c-private-generic" {
  vpc_id = aws_vpc.vpc.id
  cidr_block = "10.20.120.0/21"
  availability_zone = "eu-central-1c"

  tags = {
    Name = "habil-dev-1c-private-generic"
    CostCenter = "habil-dev-1c-private-generic"
  }
}
Enter fullscreen mode Exit fullscreen mode

vpc_route_tables.tf:

This file contains public and generic route tables.

// Public Route Table
resource "aws_route_table" "rt-1a-public-xlb" {

   route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gateway.id
  }

  tags = {
    Name = "habil-dev-1a-public-xlb-rt"
    CostCenter = "habil-dev-1a-public-xlb-rt"
  }

  vpc_id = aws_vpc.vpc.id
}

resource "aws_route_table" "rt-1b-public-xlb" {

   route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gateway.id
  }

  tags = {
    Name = "habil-dev-1b-public-xlb-rt"
    CostCenter = "habil-dev-1b-public-xlb-rt"
  }

  vpc_id = aws_vpc.vpc.id
}

resource "aws_route_table" "rt-1c-public-xlb" {

   route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gateway.id
  }

  tags = {
    Name = "habil-dev-1c-public-xlb-rt"
    CostCenter = "habil-dev-1c-public-xlb-rt"
  }

  vpc_id = aws_vpc.vpc.id
}

// Private Generic Route Table

resource "aws_route_table" "rt-1a-private-generic" {

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat-1a.id
  }

  tags = {
    Name = "habil-dev-1a-private-generic-rt"
    CostCenter = "habil-dev-1a-private-generic-rt"
  }

  vpc_id = aws_vpc.vpc.id
}

resource "aws_route_table" "rt-1b-private-generic" {

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat-1b.id
  }

  tags = {
    Name = "habil-dev-1b-private-generic-rt"
    CostCenter = "habil-dev-1b-private-generic-rt"
  }

  vpc_id = aws_vpc.vpc.id
}

resource "aws_route_table" "rt-1c-private-generic" {

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat-1c.id
  }

  tags = {
    Name = "habil-dev-1c-private-generic-rt"
    CostCenter = "habil-dev-1c-private-generic-rt"
  }

  vpc_id = aws_vpc.vpc.id
}
Enter fullscreen mode Exit fullscreen mode

vpc_route_table_associations.tf

resource "aws_route_table_association" "rta-1a-public-xlb" {
  route_table_id = aws_route_table.rt-1a-public-xlb.id
  subnet_id = aws_subnet.sn-1a-public-xlb.id
}

resource "aws_route_table_association" "rta-1b-public-xlb" {
  route_table_id = aws_route_table.rt-1b-public-xlb.id
  subnet_id = aws_subnet.sn-1b-public-xlb.id
}

resource "aws_route_table_association" "rta-1c-public-xlb" {
  route_table_id = aws_route_table.rt-1c-public-xlb.id
  subnet_id = aws_subnet.sn-1c-public-xlb.id
}

// Private Generic Subnets

resource "aws_route_table_association" "rta-1a-private-generic" {
  route_table_id = aws_route_table.rt-1a-private-generic.id
  subnet_id = aws_subnet.sn-1a-private-generic.id
}
resource "aws_route_table_association" "rta-1b-private-generic" {
  route_table_id = aws_route_table.rt-1b-private-generic.id
  subnet_id = aws_subnet.sn-1b-private-generic.id
}
resource "aws_route_table_association" "rta-1c-private-generic" {
  route_table_id = aws_route_table.rt-1c-private-generic.id
  subnet_id = aws_subnet.sn-1c-private-generic.id
}
Enter fullscreen mode Exit fullscreen mode

vpc_nat_gateway.tf

Nat Gateway definitions.

resource "aws_nat_gateway" "nat-1a" {
  allocation_id = aws_eip.nat-1a.id
  subnet_id = aws_subnet.sn-1a-public-xlb.id
  tags = {
    Name = "habil-dev-1a-nat-gw"
    Environment = "dev"
    CostCenter = "habil-dev-1a-nat-gw"
  }
}

resource "aws_nat_gateway" "nat-1b" {
  allocation_id = aws_eip.nat-1b.id

  subnet_id = aws_subnet.sn-1b-public-xlb.id

  tags = {
    Name = "habil-dev-1b-nat-gw"
    Environment = "dev"
    CostCenter = "habil-dev-1b-nat-gw"
  }
}

resource "aws_nat_gateway" "nat-1c" {
  allocation_id = aws_eip.nat-1c.id

  subnet_id = aws_subnet.sn-1c-public-xlb.id

  tags = {
    Name = "habil-dev-1c-nat-gw"
    Environment = "dev"
    CostCenter = "habil-dev-1c-nat-gw"
  }
}
Enter fullscreen mode Exit fullscreen mode

vpc_igw.tf

Internet Gateway Definition.

resource "aws_internet_gateway" "gateway" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "habil-dev-igw"
    Environment = "dev"
    CostCenter = "habil-dev-igw"
  }
}
Enter fullscreen mode Exit fullscreen mode

vpc_eip.tf

Elastic IP definitions.

resource "aws_eip" "nat-1a" {
  vpc = true
  tags = {
    Name = "habil-dev-nat-eip-1a"
    Environment = "dev"
    CostCenter = "habil-dev-nat-eip-1a"
  }
}

resource "aws_eip" "nat-1b" {
  vpc = true
  tags = {
    Name = "habil-dev-nat-eip-1b"
    Environment = "dev"
    CostCenter = "habil-dev-nat-eip-1b"
  }
}

resource "aws_eip" "nat-1c" {
  vpc = true
  tags = {
    Name = "habil-dev-nat-eip-1c"
    Environment = "dev"
    CostCenter = "habil-dev-nat-eip-1c"
  }
}
Enter fullscreen mode Exit fullscreen mode

Execution

The terraform init command initializes a working directory containing Terraform configuration files. This is the first command that should be run after writing a new Terraform configuration or cloning an existing one from version control. It is safe to run this command multiple times.

terraform init
Enter fullscreen mode Exit fullscreen mode

The terraform plan command creates an execution plan, which lets you preview the changes that Terraform plans to make to your infrastructure. By default, when Terraform creates a plan it:

  • Reads the current state of any already-existing remote objects to make sure that the Terraform state is up-to-date.
  • Compares the current configuration to the prior state and noting any differences.
  • Proposes a set of change actions that should, if applied, make the remote objects match the configuration.
terraform plan
Enter fullscreen mode Exit fullscreen mode

The terraform apply command executes the actions proposed in a Terraform plan.

terraform apply
Enter fullscreen mode Exit fullscreen mode

Terraform will create a new VPC with provided configuration. Also will save the state into provided S3 bucket for you.

If you want to destroy all your changes you need to execute following command.

terraform destroy
Enter fullscreen mode Exit fullscreen mode

Result

We have created a private AWS VPC with;

  • subnets
  • route tables
  • route table associations
  • nat gateway
  • Internet gateway
  • elastic IP

resources with our terraform files.

As always, you can find related files in my GitHub Repository:

GitHub logo habil / habil-dev-blog

Contains materials used in blog posts.

habil-dev-blog

Contains materials used in blog posts.

See you in the next article. 👻

Top comments (0)