Terraforming AWS VPC

AWS VPC stands for Virtual Private Cloud and it represents the networking layer for the AWS EC2 services (computing). In this post, we are going to cover how to automate the configuration of AWS VPC using Terraform. If you are not familiar with Terraform, you can check my introductory post here.

If you want to go straight to the code, you can check it out at aws-terraform-examples/03-terraforming-aws-vpc.

Already back? Great! Let’s dive in.

Key concepts

Before getting into the implementation, let’s briefly mention some of the essential concepts around networking and VPC.

Availability Zones (AZ) and Regions

AWS has two main concepts to refer to the physical locations of their data centers:

  • Availability Zone (AZ): It represents one or more discrete data centers with redundant power, networking and connectivity in a particular AWS Region. All AZs are connected with high-bandwidth, low-latency networking with fully redundant and dedicated fiber communications, and traffic is encrypted.
  • Region: Consists of multiple, isolated and physically separate AZ’s within a geographic area.

Virtual Private Cloud (VPC)

There are several key components in a VPC:

  • Subnet: A range of IP addresses. Can be either public or private.
  • Route table: A set of rules that are used to determine where network traffic is directed. Each subnet is associated with a route table.
  • Internet Gateway: Allows enabling communication between resources in your VPC and the internet and it serves two main purposes:
    • Provides a target in your VPC route tables for internet-routable traffic.
    • Performs network address translation (NAT) for instances that have been assigned public IPv4 addresses.
  • DNS in VPC: AWS provides an AWS Route53 Resolver to act as a DNS server for a given VPC. The key considerations are:
    • Public and private DNS hostnames are provided for corresponding IPv4 addresses for each instance.
    • Managing DNS in the VPC is done through the attributes enableDNSHostnames (indicates if instances with public IP addresses get corresponding DNS hostnames) and enableDNSSupport (indicates whether DNS resolution is supported).
  • NAT: A Network Translation Gateway (NAT) allows instances in a private subnet to connect to the internet while preventing external agents from initiating a request to the instance.

Terraform module

In this post, we are going to use a Terraform module to facilitate the VPC declaration. According to the docs, a module is a container for multiple resources that work together. Modules can be used to create lightweight abstractions to describe infrastructure in terms of its architecture, rather than directly in terms of physical objects

Uff!! After a heavy dose of concepts, let’s get right into the implementation.


The VPC declaration in Terraform is relatively short since we are leveraging the VPC Module) maintained by the Terraform community, which comes packed with sane abstractions and useful defaults.

To create the VPC, we need to obtain the existing Availability Zones in the current region. We can either manually specify the AZ names, or leverage instead the aws_availability_zones data source. This will allow Terraform to dynamically obtain the list of AZ from the region configured in the provider.

data "aws_availability_zones" "available" {}

Afterwards, we proceed to configure our new VPC as shown below:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 2.0"

  name                 = "example"
  cidr                 = ""
  azs                  = data.aws_availability_zones.available.names
  private_subnets      = ["", "", ""]
  public_subnets       = ["", "", ""]
  enable_nat_gateway   = true
  single_nat_gateway   = true
  enable_dns_hostnames = true

  tags = {
    Terraform = "true"
    Environment = "dev"

Let’s discuss some of the fields in the module declaration above:

cidr field

  • Represents the range of IPv4 addresses that are going to be available for this VPC. Generally should refer to IPs considered to be a private range (e.g., -, -
  • By assigning to it, it means that the VPC will have access to 65536 (2^16) different IP addresses

Subnets fields

  • Both private_subnets and public_subnets are specifying 3 different subnets to be created, each one with 256 (2^8) IP addresses allocated to it.
  • For both subnets, the module will make sure that the appropriate route tables and NAT gateways are created and attached.
  • For the public subnets, the module will wire them up with the Internet Gateway associated with the VPC.

NAT gateways fields

  • enable_nat_gateway informs the module that NAT Gateways should be provisioned for the private networks
  • single_nat_gateway informs the module that all private subnets will share a single NAT Gateway

enable_dns_hostnames field

  • Combined with enable_dns_support (which is enabled by default), this informs the module to configure DNS resolution for the public IP addresses associated with instances in the VPC.


Below, it is a condensed list of all the resources mentioned throughout the post as well as a few others I consider may be of interest to deepen your knowledge.





Let’s sum up what we discussed in this post. First, we looked at some of the basic concepts around AWS VPC and AZ. Next, we dove into how to declare the VPC by leveraging the VPC module from Terraform and analyzed some of the fields that were specified as part of the module declaration. Finally, we listed the online resources used to create the post in case you want to go deeper in the topic.

Thank you so much for reading this post. Hope you enjoyed reading it as much as I did writing it. See you soon and stay tuned for more!!

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.