Learning Terraform and AWS: Tool installation and VPC & Subnet Creation

The cool thing about a tech career is that the tech never stands still. I've been working in telecom for over 20 years. As an industry that domain has been significantly automated over those years. I did a fair bit of that automation myself! Now I find myself looking for fresh challenges and that requires a new set of skills using a new set of tools.

A lot of development, product and service has moved into the cloud. That cloud may be AWS, Azure, Google or any of a hundred others. To manage that cloud infrastructure in a formal/repeatable way requires a set of tools designed for the task, and that is where Terraform comes in.

Terraform is an application that allows you to describe your applications cloud environment and automate the provisioning of it. This is usually referred to as Infrastructure as Code.

I'll be describing my lessons learned using Terraform with AWS. My goal is to cover the basics of an application environment over the next couple of weeks

2018-May-18:

  • Create an AWS account to work with. This may cost you a few dollars over the following exercises, but if you tear down your infrastructure after each lesson the cost should not be too high.
  • Download and install Terraform. I'm doing everything in an Ubuntu 18.04 Desktop VM under Windows 10 Pro

2018-May-20:
Initial AWS CLI configuration and Terraform configuration:
Installing the AWS CLI on Ubuntu 18.04 is super easy:
sudo apt install awscli
Installing Terraform isn't much harder:
Download Terraform
Just remember to add the installation directory to your PATH

Configuring the AWS CLI with your Amazon account information:
Look here for Amazons instructions: Configuring the AWS CLI
Configuring Terraform with your Amazon account information:
Look here for HashiCorps instructions: AWS Provider
I would suggest configuring Terraform in a manner that avoids embedding your AWS credentials into a terraform file. Here is how I did it, based on a Terraform example:
provider "aws" {
region = "ca-central-1"
shared_credentials_file = "/home/jardine/Documents/Terraform/permissions"
}

2018-May-21:
Today I'm running through the creation of a Virtual Private Cloud containing 2 Availability Zones each with 3 sub-nets. Basically, it will look like this:

Normally you'd have at least two version of this; a Production version and a testing or staging version. Here we will setup the structure to support this but only define things for the Production version.
variable "Environment" {
default = "Production"
}

This breaks down into the following actions:

  • Create the VPC
  • Create the subnet for LoadBalancerA
  • Create the subnet for WebServerA
  • Create the subnet for DBServerA
  • Create the subnet for LoadBalancerB
  • Create the subnet for WebServerB
  • Create the subnet for DBServerB

Doing this with AWS CLI would look like:
read OBJECTTYPE CIDR DHCPOPTS TENANCY DEFAULT STATE VPC_ID < <(aws ec2 create-vpc --cidr-block "172.16.0.0/19" | head -1)
aws ec2 wait vpc-available --vpc-ids "${VPC_ID}"
aws ec2 create-tags --resources "${VPC_ID}" --tags Key=Name,Value=HoN_CLI_VPC_Demo
 
read OBJECTTYPE JUNK1 AZ_NAME AVAIL_IPS CIDR DEFAULTFORAZ MAPPUBLIC STATE LBA_SNID VPC_ID < <(aws ec2 create-subnet --availability-zone "ca-central-1a" --cidr-block "172.16.0.0/23" --vpc-id "${VPC_ID}" | head -1)
aws ec2 create-tags --resources "${LBA_SNID}" --tags Key=Name,Value=LoadBalancerPoolA
 
read OBJECTTYPE JUNK1 AZ_NAME AVAIL_IPS CIDR DEFAULTFORAZ MAPPUBLIC STATE WSA_SNID VPC_ID < <(aws ec2 create-subnet --availability-zone "ca-central-1a" --cidr-block "172.16.2.0/23" --vpc-id "${VPC_ID}" | head -1)
aws ec2 modify-subnet-attribute --map-public-ip-on-launch --subnet-id "${WSA_SNID}"
aws ec2 create-tags --resources "${WSA_SNID}" --tags Key=Name,Value=WebServerPoolA
 
read OBJECTTYPE JUNK1 AZ_NAME AVAIL_IPS CIDR DEFAULTFORAZ MAPPUBLIC STATE DBA_SNID VPC_ID < <(aws ec2 create-subnet --availability-zone "ca-central-1a" --cidr-block "172.16.4.0/23" --vpc-id "${VPC_ID}" | head -1)
aws ec2 create-tags --resources "${DBA_SNID}" --tags Key=Name,Value=DatabasePoolA
 
read OBJECTTYPE JUNK1 AZ_NAME AVAIL_IPS CIDR DEFAULTFORAZ MAPPUBLIC STATE LBB_SNID VPC_ID < <(aws ec2 create-subnet --availability-zone "ca-central-1b" --cidr-block "172.16.6.0/23" --vpc-id "${VPC_ID}" | head -1)
aws ec2 create-tags --resources "${LBB_SNID}" --tags Key=Name,Value=LoadBalancerPoolB
 
read OBJECTTYPE JUNK1 AZ_NAME AVAIL_IPS CIDR DEFAULTFORAZ MAPPUBLIC STATE WSB_SNID VPC_ID < <(aws ec2 create-subnet --availability-zone "ca-central-1b" --cidr-block "172.16.8.0/23" --vpc-id "${VPC_ID}" | head -1)
aws ec2 modify-subnet-attribute --map-public-ip-on-launch --subnet-id "${WSB_SNID}"
aws ec2 create-tags --resources "${WSB_SNID}" --tags Key=Name,Value=WebServerPoolB
 
read OBJECTTYPE JUNK1 AZ_NAME AVAIL_IPS CIDR DEFAULTFORAZ MAPPUBLIC STATE DBB_SNID VPC_ID < <(aws ec2 create-subnet --availability-zone "ca-central-1b" --cidr-block "172.16.10.0/23" --vpc-id "${VPC_ID}" | head -1)
aws ec2 create-tags --resources "${DBB_SNID}" --tags Key=Name,Value=DatabasePoolB

Doing this with Terraform would look like:
/* Define the VPC where our subnets and instances reside */
resource "aws_vpc" "HoN_TF_VPC_Demo" {
cidr_block = "172.16.0.0/19"
tags {
Name = "HoN_TF_VPC_Demo"
Environment = "${var.Environment}"
}
}
 
/* Define 3 subnets in 2 availability zones */
resource "aws_subnet" "loadBalancer_subnetA" {
vpc_id = "${aws_vpc.HoN_TF_VPC_Demo.id}"
cidr_block = "172.16.0.0/23"
availability_zone = "ca-central-1a"
tags {
Name = "LoadBalancerPoolA"
Environment = "${var.Environment}"
}
}
 
resource "aws_subnet" "webServer_subnetA" {
vpc_id = "${aws_vpc.HoN_TF_VPC_Demo.id}"
cidr_block = "172.16.2.0/23"
availability_zone = "ca-central-1a"
map_public_ip_on_launch = true
tags {
Environment = "${var.Environment}"
Name = "WebServerPoolA"
}
}
 
resource "aws_subnet" "databaseServer_subnetA" {
vpc_id = "${aws_vpc.HoN_TF_VPC_Demo.id}"
cidr_block = "172.16.4.0/23"
availability_zone = "ca-central-1a"
tags {
Name = "DatabasePoolA"
Environment = "${var.Environment}"
}
}
 
resource "aws_subnet" "loadBalancer_subnetB" {
vpc_id = "${aws_vpc.HoN_TF_VPC_Demo.id}"
cidr_block = "172.16.6.0/23"
availability_zone = "ca-central-1b"
tags {
Name = "LoadBalancerPoolB"
Environment = "${var.Environment}"
}
}
 
resource "aws_subnet" "webServer_subnetB" {
vpc_id = "${aws_vpc.HoN_TF_VPC_Demo.id}"
cidr_block = "172.16.8.0/23"
availability_zone = "ca-central-1b"
map_public_ip_on_launch = true
tags {
Name = "WebServerPoolB"
Environment = "${var.Environment}"
}
}
 
resource "aws_subnet" "databaseServer_subnetB" {
vpc_id = "${aws_vpc.HoN_TF_VPC_Demo.id}"
cidr_block = "172.16.10.0/23"
availability_zone = "ca-central-1b"
tags {
Name = "DatabasePoolB"
Environment = "${var.Environment}"
}
}

Summary
At this point you should have the AWS CLI & Terraform installed.
AWS CLI should have been configured with your Amazon credentials.
The Terraform provider has been configured.
A containing Virtual Private Cloud has been created.
3 sub-nets have been created in the first availability region.
3 sub-nets have been created in the second availability region.

We have build the network topology - but at this point we don't have any functionality. We haven't created any EC2 instances. Our infrastructure doesn't “do” anything ... yet.

Next Step: Learning Terraform and AWS: Configuring Routing & Gateways