Learn about Terraform and how it can be used to automate infrastructure management.
Terraform is a platform used for provisioning, deploying, and managing various resources and services across public clouds (AWS, Google Cloud Platform, or Microsoft Azure), private clouds, network infrastructures, and more. It allows cloud architects and developers to manage external resources by using the code defined in Terraform configuration files.
In this article, I’ll explain everything you need to know about how Terraform configuration files and modules work.
Terraform modules are collections of configuration files (such as main.tf) in a directory. They help organize configurations into logical components that can be reused.
A main directory can contain multiple modules (for AWS S3, EC2, and EKS), each with its own configuration files (main.tf, variables.tf, output.tf, and Readme.md).
You can create your own modules or import them from a public or private registry. A module block is used to call a child module. Examples:
module "public_registry" {
source = "./app" // Source 1
}
source = "hashicorp/consul/aws" // Source 2
source = "app.terraform.io/example-corp/k8s-cluster/azurerm" // Source 3
source = "[email protected]:hashicorp/example.git" // Source 4
source = "https://aws.com/module.zip" // Source 5
source = "s3::https://s3-us-west-1.amazonaws.com/terraform-modules/vpc.zip" // Source 6
Modules can be used individually, but you can also link them with a root module, which calls child modules. Example:
module "Calling AWS S3" {
source = "./AWS S3"
}
module "Calling AWS EC2" {
source = "./AWS EC2"
}
module "Calling AWS EKS" {
source = "./AWS EKS"
}
Each Terraform workspace has a root directory with configuration files called the root module.
Modules set up via module blocks are child modules. When you apply a configuration, the root module calls its child modules and Terraform manages their resources as part of your workspace.
As infrastructures grow, modules simplify code management by keeping it organized and reusable.
A resource block is used to define the resources you want to manage. These resources can include one or more infrastructure objects such as virtual machines, virtual networks, DNS records, etc.
Resource blocks are declared with the syntax below:
resource <block-type> "<block-label>" {
<identifier> = <expression> # Arguments or Attributes of a resource block.
}
Terraform modules contains various resource blocks and can call other child modules as well. They contain all the code to manage various services in one go.
When an IT infrastructure grows, it becomes more complex and difficult to manage, especially when you have to deal with multiple configuration files. However, Terraform modules are designed to streamline the code management process for developers.
Example: creating an AWS EC2 instance.
Installing Terraform on Linux
mkdir ~/terraform-ec2-practice
cd ~/terraform-ec2-practice

sudo yum install -y yum-utils shadow-utils

sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo

sudo yum -y update
sudo yum -y install terraform
terraform --version

touch main.tf

resource "aws_instance" "web" {
for_each = {
"instance1" = {
instance_type = "t2.micro"
ami = "ami-0f3c9c466bb525749"
}
"instance2" = {
instance_type = "t2.nano"
ami = "ami-0f3c9c466bb525749"
}
"instance3" = {
instance_type = "t2.micro"
ami = "ami-0f3c9c466bb525749"
}
"instance4" = {
instance_type = "t2.nano"
ami = "ami-0f3c9c466bb525749"
}
}
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.key
}
}
data "aws_availability_zones" "az" {
state = "available"
}
data "aws_region" "current” {}
data "aws_caller_identity" "current" {}
locals {
name_tag_val = join("-[data.aws_caller_identity.current.account_id, data.aws_availability_zones.az.id])
tags = { Name = local.name_tag_val }
}
resource "aws_instance" "web1" {
count = var.create
ami = var.ami
instance_type = var.instance_type
tags = merge(local.tags,var.tags)
timeouts {
create = lookup(var.timeouts, "create", null)
update = lookup(var.timeouts, "update", null)
delete = lookup(var.timeouts, "delete", null)
}
}
The code above contains different resource blocks where we define the resources to create. The syntax of each resource block is as follows:
resource <block-type> "<block-label>" {
<identifier> = <expression> # Arguments or Attributes of a resource block.
}
In the first resource block, we are creating the AWS EC2 instance. That’s why we’re using ‘aws_instance’ as the block type, followed by the block label ‘web’ (a label can be anything).
Next, ‘for_each’ is a meta argument, which is a special argument that gets additional configuration options for Terraform resource blocks and modules. It can be used to create multiple items with different configurations.
Here we’re using this meta argument to create four different instances (instance1,instance2,instance3,instance4) with the following configurations:
All four instances within the ‘for_each’ meta argument are a map of objects containing two attributes, ‘instance_type’ and ‘ami’.
Here’s an explainer for some other resource blocks in the code:
Another important block is ‘locals’, which is where we join the current region and availability zone, and assign them to the variables tags. We define this variable once so that it can be used in multiple places without any code repetition. We’re also using the following attributes:
We’ll now create another a variable configuration file named variables.tf in the ~/terraform-ec2-practice directory. We’re once again using the touch command to do that:
touch variables.tf
Paste this code defining ami, instance_type, tags, create, and timeouts.
variable "ami" {
type = string
default = "ami"
}variable "instance_type" {
type = string
default = "t2.micro"
}variable "tags" {
type = map(string)
default = {
"Name" = "env"
}
}
variable "create" {
description = "Whether to create an instance"
type = bool
default = true
}
variable "timeouts" {
description = "Define maximum timeout for creating, updating, and deleting EC2 instance resources"
type = map(string)
default = {
create = "20m"
update = "20m"
delete = "10m"
}
}
The variables.tf file contains all the variables that we have already used in the main.tf configuration file:
Again, you can create the outputs.tf file using the touch command in the ~/terraform-ec2-practice directory and copy and paste the code below in it:
touch output.tf
Paste this code defining outputs for id, arn, public_ip, and private_ip.
output "id" {
description = "The ID of the instance"
value = try(aws_instance.web[0].id, "")
}
output "arn" {
description = "The ARN of the instance"
value = try(aws_instance.web[0].arn, "")
}
output "public_ip" {
description = "The public IP address assigned to the instance"
value = try(aws_instance.web[0].public_ip, "")
}
output "private_ip" {
description = "The private IP address assigned to the instance."
value = try(aws_instance.web[0].private_ip, "")
}
The code will produce four outputs:
Before we can run any Terraform commands, we need to configure a provider configuration file. Here, we will define AWS as the provider so that Terraform can connect with the AWS cloud using APIs.
touch provider.tf
Paste AWS provider configuration:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "4.57.0"
}
}
}
provider "aws" {
region = "us-east-2"
}
Now, we’re ready to run Terraform commands to deploy the AWS EC2 instances that we defined in main.tf file. To do that, we will use three commands in a specific sequence:
To run these commands, make sure that you are already in the ~/terraform-ec2-practice directory.
terraform init

Next, run the terraform plan command. It will show you all the resources that will be deployed in the AWS Cloud.
terraform plan

Finally, run the terraform apply command in the current working directory. The actual deployment of resource will take place in the AWS Cloud.
terraform apply

Check the AWS EC2 dashboard to verify the instances.

Conclusion
In this article, I gave you an overview of Terraform, explained its main components, and walked you through creating configuration files to deploy AWS EC2 instances. Terraform makes managing cloud and on-premises resources more efficient, and its reusability and modularity provide significant advantages.