In my previous post, I documented getting started with Terraform coming from an AWS CloudFormation background.
As I continue my Terraform learning journey from a CloudFormation background, two concepts that took more effort to grasp are variables and modules. Both offer powerful capabilities for customization and reusability — let’s break it down!

What are Variables?
Terraform variables provide a way to make parts of your configuration customizable at runtime, so you don’t have to hardcode values. For example, you can define a variable to control EC2 instance type:
variable "instance_type" {
default = "t3.micro"
}
resource "aws_instance" "app" {
instance_type = var.instance_type
}
Now users can override the value “t3.micro” default via CLI, environment variable, input prompts etc. By exposing instance types, or database names, as variables, many configurations can reuse your Terraform code!
Assigning an input value to a variable looks like:
terraform apply -var="instance_type=t2.small"
Why use Modules?
Terraform modules help organize and encapsulate reusable pieces of infrastructure in one place. For example, a VPC module:
module "vpc"{
source = "../vpc"
vpc_cidr = var.vpc_cidr
public_subnet_cidr = var.public_subnet_cidr
private_subnet_cidr = var.private_subnet_cidr
}
module "ec2_instance" {
source = "../ec2-instance"
ami_id = var.ami_id
instance_type = var.instance_type
instance_name = var.instance_name
security_group_name = var.security_group_name
}
This allows easy reuse of the VPC, EC2 definitions across environments. Modules also encourage separation of concerns, testing in isolation, and make collaboration easier via self-contained packages.
Deploying an EC2 Instance and VPC
I have created a set of modules for various AWS services (Ex., VPC, EC2.., etc.)
I have modified my Terraform GitHub repo — (https://github.com/kbrepository/handson_terraform) to add a VPC and an EC2 instance declaration. This includes referencing the variables:
VPC Configuration
## VPC Configuration
resource "aws_vpc" "ExampleVPC" {
cidr_block = var.vpc_cidr
tags = {
Name = var.vpc_tag
}
}
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.ExampleVPC.id
cidr_block = var.public_subnet_cidr
availability_zone = "${var.region}a"
}
resource "aws_subnet" "private_subnet" {
vpc_id = aws_vpc.ExampleVPC.id
cidr_block = var.private_subnet_cidr
availability_zone = "${var.region}b"
}
EC2 Configuration
## EC2 Instance configuration
resource "aws_instance" "example" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = var.instance_name
}
}
Reusable Modules
Following Terraform best practices, I split my configuration into separate main.tf, VPC module, and EC2 modules. This improves the organization and reusability of VPC across projects.
image
What’s Next?
In my next post I plan to cover:
- Automated testing of modules
- Deployment of load-balanced web applications
For readers interested in learning more about Terraform, I recommend the book Architecting AWS with Terraform. It provides practical, hands-on labs for mastering Terraform on AWS.
Hopefully this gives beginners an easier introduction into how variables and modules work! Check out Terraform’s documentation or reach out with any other questions!
Hi, this is a comment.
To get started with moderating, editing, and deleting comments, please visit the Comments screen in the dashboard.
Commenter avatars come from Gravatar.