Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. We can use Terraform  to design, implement and manage the AWS infrastructure.

There are many articles out there explain this in detail so I will dive straight into the example.

If you want to learn Terraform basics, please check out my other blog: https://packetswitch.co.uk/terrafrom-aws/

Assumptions

  • AWS account is set up and correct permission is assigned to Terraform user
  • Terraform is installed on your machine and have access to AWS

Let's say you want to deploy the below infrastructure in AWS. You can of course use the web console, however, what if you want to do the same again for another VPC? That's where Terraform comes in to play.

Terraform will outline exactly what will happen when we run the code. Using Terraform is faster than manually navigating an interface when you need to deploy  resources across multiple VPCs / accounts. I will discuss how can we accomplish this using Terraform in AWS.

diagram

Steps

  • Create a VPC with 10.22.0.0/16 CIDR
  • Create public, private and intra subnets in two Availability zones.
  • Create an Internet Gateway for public subnets
  • Create 3x routing tables and associate it with private, public and intra subnets
  • Pubic subnets should have access to both 20.20.20.0/24 and 30.30.30.0/24 via IGW
  • Create 2x Elastic IPs
  • Create 2x NAT gateways and associate the EIPs created on the previous step.

Note - In this example, I am not going to associate the NAT gateway with the private subnets routing table.

I created three files vars.tf , main.tf and vpc-subnets.tf

vars.tf

Input variables serve as parameters for a Terraform module, allowing aspects of the module to be customized without altering the module's own source code, and allowing modules to be shared between different configurations.

variable "region" {
  default = "eu-west-2"
}

variable "vpc-cidr" {
  default = "10.22.0.0/16"
}

variable "azs" {
  type = list
  default = ["eu-west-2a" , "eu-west-2b"]
}

variable "prod-intra-subnets" {
  type = list
  default = ["10.22.0.0/26" , "10.22.0.64/26"]
}

variable "prod-private-subnets" {
  type = list
  default = ["10.22.8.0/24" , "10.22.9.0/24"]
}

variable "prod-public-subnets" {
  type = list
  default = ["10.22.16.0/24" , "10.22.17.0/24"]
}

#this is just for testing
variable "public-subnets" {
  type = list
  default = ["20.20.20.0/24" , "30.30.30.0/24"]
}

main.tf

It contains provider, region and profile. I work with multiple accounts hence using the profiles. AWS provider is used to interact with the many resources supported by AWS.

provider "aws" {
  region  = var.region
  profile = "suresh-stage"
}

vpc-subnets.tf

It contain the main set of configuration for the deployment.

resource "aws_vpc" "prod-vpc" {
  cidr_block       = var.vpc-cidr

  tags = {
    Name = "prod-vpc"
  }
}

resource "aws_subnet" "prod-intra-subnets" {
  vpc_id = aws_vpc.prod-vpc.id
  count = length(var.azs)
  cidr_block = element(var.prod-intra-subnets , count.index)
  availability_zone = element(var.azs , count.index)

  tags = {
    Name = "prod-intra-subnet-${count.index+1}"
  }
}

resource "aws_subnet" "prod-private-subnets" {
  vpc_id = aws_vpc.prod-vpc.id
  count = length(var.azs)
  cidr_block = element(var.prod-private-subnets , count.index)
  availability_zone = element(var.azs , count.index)

  tags = {
    Name = "prod-private-subnet-${count.index+1}"
  }
}

resource "aws_subnet" "prod-public-subnets" {
  vpc_id = aws_vpc.prod-vpc.id
  count = length(var.azs)
  cidr_block = element(var.prod-public-subnets , count.index)
  availability_zone = element(var.azs , count.index)

  tags = {
    Name = "prod-public-subnet-${count.index+1}"
  }
}

#IGW
resource "aws_internet_gateway" "prod-igw" {
  vpc_id = aws_vpc.prod-vpc.id

  tags = {
    Name = "prod-igw"
  }
}

#route table for public subnet
resource "aws_route_table" "prod-public-rtable" {
  vpc_id = aws_vpc.prod-vpc.id

  tags = {
    Name = "prod-public-rtable"
  }
}

#add routes to public-rtable this is just for testing
resource "aws_route" "ner-subnets-public-rtable" {
  count                     = length(var.public-subnets)
  route_table_id            = aws_route_table.prod-public-rtable.id
  destination_cidr_block    = element(var.public-subnets , count.index)
  gateway_id                = aws_internet_gateway.prod-igw.id
}

#route table association public subnets
resource "aws_route_table_association" "public-subnet-association" {
  count          = length(var.prod-public-subnets)
  subnet_id      = element(aws_subnet.prod-public-subnets.*.id , count.index)
  route_table_id = aws_route_table.prod-public-rtable.id
}

EIP
resource "aws_eip" "nat-eip" {
  count = length(var.azs)
  vpc      = true

  tags = {
    Name = "EIP--${count.index+1}"
  }
}

NAT gateway
resource "aws_nat_gateway" "prod-nat-gateway" {
  count = length(var.azs)
  allocation_id = element(aws_eip.nat-eip.*.id , count.index)
  subnet_id     = element(aws_subnet.prod-public-subnets.*.id , count.index)

  tags = {
    Name = "NAT-GW--${count.index+1}"
  }
}

Few things to consider

count - The number of identical resources to create. Above you can see I have used count parameter to create two subnets. I could have said count = 2, but what if in the future I wanted another subnet in the third AZ. That's why I have used length function with count.

length determines the length of a given list, map, or string. In our case, length of the variable called"azs"  is 2 (eu-west-2a and eu-west-2a), so two subnets are created. If I add one more AZ to the list then count will become 3 and three subnets will be created.

Element retrieves a single element from a list. In our example, we have a variable called prod-intra-subnets with two subnets (list). We need to provide these subnet CIDRs one by one while creating a subnet. By using element function, I'm instructing Terraform to pick the subnet CIDR one by one in a loop.

Let's take a look at the NAT Gateway tag, for "tag Name", I am using ${count.index} otherwise I will have a same tag for all resources. I have used count.index to get the index of each “iteration” in the “loop”.

Verification

vpc
subnets
AZs
route table
routes
NAT gateway

Thanks for reading

As always, your feedback and comments are more than welcome.

Reference

What is Terraform?
This guide highlights everything you need to know about Terraform—a tool that allows programmers to build, change, and version infrastructure safely and efficiently.
Provider: AWS - Terraform by HashiCorp
The Amazon Web Services (AWS) provider is used to interact with the many resources supported by AWS. The provider needs to be configured with the proper credentials before it can be used.
Session-2 : AWS - Terraform variables | Data Sources | Terraform Loops
For online/class room trainings please contact +91 9886611117
Buy me a coffeeBuy me a coffee