Terraform is an open-source infrastructure as code software tools created by HashiCrop and it enables user to define and provisioning a datacenter infrastructure using high-level configuration language which is know as HashiCorp Configuration Language (HCL) or optionally you can using JSON.
Terraform is platform independent tools where you can use it to code for infrastructure from different provider such as AWS, Azure, GCP, Alibaba Cloud, Akamai, Heroku, Chef, CloudFlare and many more. (You may refer to the list at their website)
The major advantage of using the code to deploy your infrastructure is that every single step is traceable and repeatable, you can easily repeat the same stack just a few command run, it will reproduce exactly the same resources as per you code.
There are a lot of learning resources and sample code you may found in Google or GitHub which can speed up your introduction to Terraform
In this post, I will share and demo the simple way to set up a VPC some resources as per following:
VPC | CIDR (10.0.0.0/16) |
2 Private subnets | Private subnet 1 CIDR (10.0.1.0/24) Private subnet 2 CIDR (10.0.2.0/24) |
2 Public Subnets | Public subnet 1 CIDR (10.0.10.0/24) Public subnet 2 CIDR (10.0.20.0/24) |
1 Internet Gateway (IGW) | 1 NAT Gateway |
2 Route Table |
In order to do the setup, you may need to have following criteria ready:
- IAM user which sufficient permission to create the above resources
- You have the AWS access key id and AWS secret access key ready
- You already install the AWS CLI tools in your machine
- You already install the terraform in your machine
Step
- Create a VPC with 10.0.0.0/16 CIDR
- Create public, private subnet in 2 availability zones
- Create an Internet Gateway for public subnets
- Create 2 routing table and associate it with private, public subnet
- Create 1 Elastic IP
- Create 1 NAT Gateway and associate EIP with it
- Initialise the terraform project using command “terraform init“, this will download all resources, module, provider which we write in the tf file
- Check the resources which will be deploy using “terraform plan“, at this step, terraform only check the syntax in the file, display all the resources which will be create, change or delete from the target provider
- Deploy the actual resources using the “terraform apply” or “terraform apply –auto-approve“
I will create 3 files for the demo main.tf, vars.tf, vpc.tf. You need to place all 3 files in the same folder and the run the command in the folder, terraform will auto check the file with the .tf extension and execute it.
vars.tf
Input variables to serve as the parameters for the Terraform project, this allow us to change the values without touching the source code.
variable "region" {
default = "ap-southeast-1"
}
variable "vpc-cidr" {
default = "10.0.0.0/16"
}
variable "azs" {
type = list
default = ["ap-southeast-1a", "ap-southeast-1b"]
}
variable "public-subnets" {
type = list
default = ["10.0.10.0/24" , "10.0.20.0/24"]
}
variable "private-subnets" {
type = list
default = ["10.0.1.0/24" , "10.0.2.0/24"]
}
main.tf
It’s contains the provider and profile, you may change the profile is you are working under difference credential.
provider "aws" {
region = "ap-southeast-1"
profile = "demo"
}
vpc.tf
This file contain all the actual configuration for the deployment
resource "aws_vpc" "prod-vpc" {
cidr_block = var.vpc-cidr
tags = {
Name = "prod-vpc"
}
}
resource "aws_subnet" "private-subnets" {
vpc_id = aws_vpc.prod-vpc.id
count = length(var.azs)
cidr_block = element(var.private-subnets , count.index)
tags = {
Name = "private-subnet-${count.index+1}"
}
}
resource "aws_subnet" "public-subnets" {
vpc_id = aws_vpc.prod-vpc.id
count = length(var.azs)
cidr_block = element(var.public-subnets , count.index)
tags = {
Name = "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" "public-rtable" {
vpc_id = aws_vpc.prod-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.prod-igw.id
}
tags = {
Name = "prod-public-rtable"
}
depends_on = [aws_internet_gateway.prod-igw]
}
#route table association public subnets
resource "aws_route_table_association" "public-subnet-association" {
count = length(var.public-subnets)
subnet_id = element(aws_subnet.public-subnets.*.id , count.index)
route_table_id = aws_route_table.public-rtable.id
}
# Elastic IP for NAT Gateway
resource "aws_eip" "nat-eip" {
vpc = true
tags = {
Name = "EIP-NAT"
}
}
# NAT Gateway
resource "aws_nat_gateway" "prod-nat-gateway" {
allocation_id = aws_eip.nat-eip.id
subnet_id = aws_subnet.public-subnets[1].id
tags = {
Name = "NAT-GW"
}
}
#route table for private subnet
resource "aws_route_table" "private-rtable" {
vpc_id = aws_vpc.prod-vpc.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.prod-nat-gateway.id
}
tags = {
Name = "prod-private-rtable"
}
depends_on = [aws_nat_gateway.prod-nat-gateway]
}
#route table association private subnets
resource "aws_route_table_association" "private-subnet-association" {
count = length(var.private-subnets)
subnet_id = element(aws_subnet.private-subnets.*.id , count.index)
route_table_id = aws_route_table.private-rtable.id
}
Few build in functions
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 (ap-southeast-1a and ap-southeast-1a), 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 public-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.
depends_on to make sure the sequence of the creation of resources. In our example, the public route table public-rtable need to wait for the aws_internet_gateway.prod-igw to be successfully created before it can be create, this is to ensure that the binding of the gateway working as expected.
To verify is all the resource created as per our configuration, we can log into the AWS console and verify it.







Look like all the resources that we configure in the terraform is successfully deploy to our environment. After finish the testing and verified everything working fine, you may just destroy the whole stack in order not be charge for any resources that you deploy.
In order to destroy all the resources, just enter the command “terraform destroy”, than all the resources will be auto removed from your account.
This is just a very simple example for what terraform can do, it’s actually can do more than that, you can setup the whole stack for your production including database, s3 bucket, route 53, load balance, EC2, ECS and many more. It’s worth for all of us spending time to learn and use it for our environment.