Build a Disposable AWS VPC with Terraform

Consistent with the concepts of disposable infrastructure, we want to build an isolated VPC within our existing AWS account to isolate experiments with infrastructure-as-code. Using Terraform by HashiCorp, we can build and tear down the VPC structures easily, quickly, and consistently. This base automation can make it easier to identify any AWS objects contained with the VPC boundaries for cleanup, even for manual experiments or diverse automation tools.

Design

Let’s keep the design as simple as possible for a basic, isolated VPC to build and tear down at will.

Required components:

  • VPC – with DNS and DNS hostnames support
  • Subnets – across three availability zones (AZs) to match cluster voting cases
  • Internet Gateway
  • Route Table – update the default created with VPC
    • Routes – default route to Internet Gateway
    • Subnet associations
  • Network ACL – use the default created with VPC (no changes)
  • Security Group – use the default created with VPC (no changes)

For the above components, we need to specify a CIDR block for the VPC, and from within that address range, a separate CIDR block for each subnet. We will also place each subnet is a separate AZ.

Design parameters:

VPC10.54.0.0/16
Subnets10.54.14.0/24, us-east-1a
10.54.16.0/24, us-east-1b
10.54.24.0/24, us-east-1c
*Adjust these values to avoid conflicts with existing CIDR blocks in your AWS account

Setup

To run Terraform with the AWS provider requires the following prerequisites, which are beyond the scope of this blog:

  • Access keys for an AWS account with required permissions
    • AWS managed policy AdministratorAccess in the example
  • Terraform version 0.12+
  • Python pip version 3 installed

To set up the AWS provider, install and configure the AWS CLI, pasting in the access key and secret key from AWS.

root@ansible3:~# pip3 install awscli
[...]
Successfully installed awscli-1.18.218 botocore-1.19.58 docutils-0.15.2 jmespath-0.10.0 pyasn1-0.4.8 rsa-4.5 s3transfer-0.3.4

root@ansible3:~# aws --version
aws-cli/1.18.218 Python/3.8.5 Linux/5.4.0-51-generic botocore/1.19.58

paul@ansible3:~$ aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXxxxxxXXXXXXXXXXXxXxxxXxxXXXXXxx
Default region name [None]: us-east-1
Default output format [None]: json

Terraform Code

For this use case with very few input parameters, we can keep the code simple (and easy to consume in blog format) by hard-coding all the values.

First, pull in the provider and configure it. This is pinned to version the current version 3.25 for stability.

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "3.25"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

Next, define the VPC, with name tagged as “labvpc”.

# VPC
resource "aws_vpc" "labvpc" {
  cidr_block = "10.54.0.0/16"
  instance_tenancy = "default"

  tags = {
    Name = "labvpc"
  }
}

Next, define the Internet Gateway, with name “labvpc-igw”.

# Internet Gateway
resource "aws_internet_gateway" "lab_gw" {
  vpc_id = aws_vpc.labvpc.id

  tags = {
    Network = "Public"
    Name = "labvpc-igw"
  }
}

Next, define the three Subnets in separate AZs.

# Subnets
resource "aws_subnet" "public0" {
  vpc_id = aws_vpc.labvpc.id
  cidr_block = "10.54.14.0/24"
  availability_zone = "us-east-1a"
  map_public_ip_on_launch = "true"

  tags = {
    Network = "Public"
    Name = "labvpc-public-us-east-1a"
  }
}

resource "aws_subnet" "public1" {
  vpc_id = aws_vpc.labvpc.id
  cidr_block = "10.54.16.0/24"
  availability_zone = "us-east-1b"
  map_public_ip_on_launch = "true"

  tags = {
    Network = "Public"
    Name = "labvpc-public-us-east-1b"
  }
}

resource "aws_subnet" "public2" {
  vpc_id = aws_vpc.labvpc.id
  cidr_block = "10.54.24.0/24"
  availability_zone = "us-east-1c"
  map_public_ip_on_launch = "true"

  tags = {
    Network = "Public"
    Name = "labvpc-public-us-east-1c"
  }
}

Now, using the Route Table that was created by default with the VPC, define the Internet Gateway route and associate the Subnets.

# Internet Route
resource "aws_route" "internet_route" {
  route_table_id = aws_vpc.labvpc.default_route_table_id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id = aws_internet_gateway.lab_gw.id
}

# Subnet Associations to Route Table
resource "aws_route_table_association" "srta0" {
  subnet_id = aws_subnet.public0.id
  route_table_id = aws_vpc.labvpc.default_route_table_id
}

resource "aws_route_table_association" "srta1" {
  subnet_id = aws_subnet.public1.id
  route_table_id = aws_vpc.labvpc.default_route_table_id
}

resource "aws_route_table_association" "srta2" {
  subnet_id = aws_subnet.public2.id
  route_table_id = aws_vpc.labvpc.default_route_table_id
}

That’s it! We will not need to fuss with a Security Group until we add something into the VPC, such as an EC2 instance.

The lines of code above can be pasted into a single file named “main.tf” or into multiple *.tf files in the same directory for the usage below.

Usage

The AWS provider needs to be downloaded and installed on the first run, and any time the version changes.

# Go to the directory where the code is stored
cd code/aws/vpc
# Initialize Terraform setup
terraform init

Now the build and tear down can be repeated, as needed. (The “plan” and “show” commands are optional. Make sure all of the object in the VPC are cleaned up before issuing the “destroy”.)

# Show what changes Terraform will make
terraform plan
# Apply the changes
terraform apply
# Show state information for the resources
terraform show
# Remove the resources
terraform destroy

Conclusion

The Terraform code and commands shown above provide a solid foundation for safely moving in many directions . Future blog entries will leverage the “labvpc” to build out additional infrastructure. Perhaps this simple VPC would be a good use case to package up the code in a Terraform module!

1 comment

Leave a comment

Your email address will not be published. Required fields are marked *