Deploying an AWS VPC, Subnet, and Security Group using Terraform

Enrico Portolan
4 min readNov 4, 2020
Photo by Emile Perron on Unsplash

In this blog post, we will learn how to configure a VPC Network using Terraform.

The VPC will have two Public Subnets, an Internet Gateway and a Security Group attached to it.

Prerequisites

Network Architecture

VPC Network Architecture
VPC Architecture

Define Terraform Variables

Variables in Terraform are a useful way of declaring parameters that will be used in your module. For our configuration, we will use the following variables:

variable.tf

variable "profile" {
type = string
default = "default"
}
variable "region-main" {
type = string
default = "eu-west-2"
}
variable "external_ip" {
type = string
default = "0.0.0.0/0"
}

Define Terraform Providers

Providers are Terraform’s building blocks, they provide the resources of the different cloud vendors, such as AWS, GCP or Azure. The secret element when you define providers is the alias parameter. Defining an alias will let us invoke the specific provider in our Terraform configuration.

providers.tf

provider "aws" {
profile = var.profile
region = var.region-main
alias = "region-main"
}
variable "external_ip" {
type = string
default = "0.0.0.0/0"
}

Define VPC, Subnets, IGW and Security Groups

Based on the network architecture above, we are going to deploy a VPC in eu-west-2 with two public subnets and an Internet Gateway.

To create a VPC using Terraform, we will use the aws_vpc resource as follow:

networks.tf

resource "aws_vpc" "vpc_main" {
provider = aws.region-main
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "main-vpc-1"
}
}

The VPC needs a mandatory parameter to be created: the CIDR block, which is 10.0.0.0/16. Notice how we are using the aws.region-main parameter defined in the previous section.

Next step is to create the Internet Gateway (IGW):

networks.tf

# Create IGW in eu-west-2resource "aws_internet_gateway" "igw" {
provider = aws.region-main
vpc_id = aws_vpc.vpc_main.id
}

Again, we defined the required parameters using a reference to the previous resources created (vpc_main and aws.region-main).

Now let’s create the subnets. The subnet resource requires the following parameters:

  • cidr_block: the CIDR block for the subnet.
  • vpc_id: the VPC ID.
  • availability_zones: (Optional) the AZ for the subnet.

To get the list of the availability zones within a specific region, we will use the Terraform data resources:

networks.tf

# Get all available AZ's in VPC for the Main Regiondata "aws_availability_zones" "azs" {
provider = aws.region-main
state = "available"
}

The code above will create a reference to the AZs of a specific region (in our example, eu-west-2). This reference becomes handy when we declare the two subnets:

network.tf

# Create subnet 1 in eu-west-2resource "aws_subnet" "subnet_1" {
provider = aws.region-main
availability_zone = element(data.aws_availability_zones.azs.names, 0)
vpc_id = aws_vpc.vpc_main.id
cidr_block = "10.0.1.0/24"
}# Create subnet 2 in eu-west-2resource "aws_subnet" "subnet_2" {
provider = aws.region-main
availability_zone = element(data.aws_availability_zones.azs.names, 1)
vpc_id = aws_vpc.vpc_main.id
cidr_block = "10.0.2.0/24"
}

The subnet parameters are straight-forward to understand. Please notice how we are using the element function to get the first and second AZs of the specified region and use them in the availability_zone parameter.

When we created the VPC in the first step of this section, AWS created a default routing table attached to it. So, we would need to create a new routing table that will overwrite the default one.

network.tf

#Create route table
resource "aws_route_table" "internet_route" {
provider = aws.region-main
vpc_id = aws_vpc.vpc_main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
lifecycle {
ignore_changes = all
}
tags = {
Name = "Main-Region-RT"
}
}
# Overwrite default route table of VPC(Main) with our route table entries
resource "aws_main_route_table_association" "set-main-default-
routin-table" {
provider = aws.region-main
vpc_id = aws_vpc.vpc_main.id
route_table_id = aws_route_table.internet_route.id
}

So far, we have created a VPC in eu-west-2, two public subnets, one IGW attached to the VPC and the routing table. The final step is to create a Security Group for the VPC to allow 443 traffic from anywhere, 80 from anywhere to allow redirection to 443 and 22 from our Public IP.

security-groups.tf

# Create SG for VPC, only TCP/80,TCP/443 and outbound accessresource "aws_security_group" "lb-sg" {provider    = aws.region-main
name = "vpc-1-sg"
description = "Allow 443"
vpc_id = aws_vpc.vpc_main.id
ingress {
description = "Allow 443 from anywhere"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow 80 from anywhere for redirection"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow 22 from our public IP"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.external_ip]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

Deploy

At this point, we should have the following files in the root directory:

  • variables.tf: contains the variables declared.
  • providers.tf: contains the providers declared.
  • network.tf : contains the VPC, Subnet, Security Groups and IGW definitions.
  • security-groups.tf: contains the security groups definition.

Let’s run terraform fmt to format our code and make it beautiful andterraform init to initialize the environment. Then, runterraform validateto validate the files.

If you would like to check which resources will be created by the terraform files, run terraform plan to get a full list of the resources.

Finally, time to deploy! Run terraform apply to deploy the resources on AWS.

Tip: if you are looking for deleting the resource, terraform destroy will clean up all the resources created for you.

Code: https://github.com/enricop89/terraform-vpc-public-subnet-example

--

--

Enrico Portolan

Passionate about cloud, startups and new technologies. Full-stack web engineer