💡 Introduction
Welcome to the world of Infrastructure and Automation! 🚀
In today’s post, we’re going to explore one of the fundamental building blocks of Terraform — the Module.
If you’ve been using Terraform for a while, you already know how quickly your configuration files can grow messy as your infrastructure expands. That’s where modules come to the rescue. They help you organize, reuse, and standardize your Terraform code — making your infrastructure cleaner, more scalable, and much easier to maintain.
In this guide, we’ll break down:
What a Terraform module is
How it’s structured
Why using modules is beneficial
And finally, how to create your own module for a real-world use case.
To wrap things up, we’ll get hands-on and build a Terraform module for both VPC and EC2, deploy an instance, and serve a simple index.html
page from it.
So, without further ado — let’s dive right in! 🌍
💡 Pre-Requisites
Before we dive into building our Terraform module, let’s make sure your workspace is all set up and ready to go. Here’s what you’ll need:
🧑💻 An AWS Account with an IAM user that has Full Access to both VPC and EC2. The IAM user should have an Access Key generated and configured with the AWS CLI on your system.
If you’re new to this step, I’ve covered it in detail here:
👉 Set up AWS CLI and IAM User (Step 1 from my EKS blog)⚙️ Basic knowledge of Terraform — understanding what it is and how it works will make things much smoother.
Once you have these requirements in place, we’re all set to kick off our journey into Terraform Modules! 🎯
💡 What is a Terraform Module?
Terraform lets you define your infrastructure as code using HashiCorp Configuration Language (HCL). And like any programming language, it embraces one golden principle — DRY (Don’t Repeat Yourself).
Instead of repeatedly provisioning similar resources (like networking components or security groups) for every new environment, Terraform allows you to encapsulate those recurring configurations inside a module.
In simple terms, a Terraform module is a collection of Terraform configuration files (.tf
or .tf.json
) that live together in the same directory and work as a single logical unit.
Here are a few examples of what Terraform modules can represent:
🧱 An AWS VPC module containing subnets, route tables, and internet gateways
💾 A Microsoft SQL Always On cluster in Azure, including NSGs
☁️ A GCP Project module that enables APIs and sets permissions
Why Modules Matter
Imagine you’re setting up an EC2 instance the traditional way — defining every component manually: the security group, IAM role, instance profile, and the instance itself. That’s a lot of repeated configuration!
Here’s what that would look like 👇
resource "aws_security_group" "my_app_sg" {
name = "my-app-sg"
vpc_id = var.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_iam_role" "my_app_role" {
name = "my_app_role"
assume_role_policy = <<EOF
{ ... }
EOF
}
resource "aws_iam_instance_profile" "my_app_profile" {
name = "my_app_profile"
role = aws_iam_role.my_app_role.name
}
resource "aws_instance" "my_app_server" {
ami = "ami-0c55b159fbd7718e9"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.my_app_sg.id]
iam_instance_profile = aws_iam_instance_profile.my_app_profile.name
subnet_id = var.subnet_id
user_data = file("setup.sh")
tags = {
Name = "my-app-server"
}
}
Now, let’s see how modules make this effortless and elegant:
module "ec2_instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "5.6.0"
name = "my-app-server"
ami = "ami-0c55b159fbd7718e9"
instance_type = "t3.micro"
subnet_id = var.subnet_id
tags = {
Name = "my-app-server"
}
# Configuration for managed resources
create_iam_instance_profile = true
iam_role_name = "my_app_role"
iam_role_policy_json = "{ ... }"
security_group_ingress = [
{
description = "HTTP access from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
}
See the difference?
Instead of defining every resource from scratch, you just call a module and pass the required inputs. Terraform takes care of the rest!
We’ll create our own module in the practical demonstration later — but this example perfectly shows why modules are so powerful.
Why Use Terraform Modules?
Here’s how modules simplify your infrastructure management:
📦 Package related resources together into a reusable configuration
👨👩👧👦 Share standardized configurations across your team or projects
💡 Embrace DRY principles, reducing repetitive code
✅ Minimize human error when referencing complex resources manually
Quick Question: How Are Terraform Resources Different from Modules?
That’s a great distinction to understand early on 👇
A resource in Terraform describes a single piece of infrastructure — like a VPC, subnet, EC2 instance, or IAM role.
A module, on the other hand, is a collection of resources grouped together to form a reusable and logical unit — for example, a complete VPC setup or an EC2 deployment stack.
💡 Benefits of Using Terraform Modules
Now that we understand what modules are and how they work, let’s explore why they’re such an essential part of Terraform best practices.
Using modules isn’t just about cleaner code — it’s about making your infrastructure reusable, scalable, and collaborative. Let’s look at the key benefits 👇
🧩 1. Reusability
Just like every programming language has functions, classes, or libraries, Terraform has modules.
They allow you to abstract a set of resources into a single reusable component that can be used across multiple projects or environments.
For example, once you’ve built a VPC module, you can reuse it for your dev, staging, and production environments — without rewriting a single line of networking code.
In short: build once, use anywhere.
🚀 2. Scalability
Modules make scaling your infrastructure seamless.
Let’s say you have a security group defined for your development environment that allows traffic on port 27017
for MongoDB. Instead of manually updating that configuration across multiple environments, you can simply update the module and propagate the change everywhere it’s used.
This ensures consistency and saves tons of time — especially when managing large-scale infrastructure across multiple environments.
🤝 3. Team Collaboration
As your Terraform usage grows across teams, modules help maintain standardization and best practices.
Platform teams can create a catalog of pre-approved modules (for example, VPC, EC2, or EKS modules) that application teams can directly use. This ensures that all deployed infrastructure meets security, compliance, and organizational standards — while allowing developers to focus on their applications.
In short, modules enable smooth collaboration, reduce misconfigurations, and promote a shared IaC culture within your organization.
💡 A Practical Demonstration
Alright, now that we’ve covered the theory — it’s time to get our hands dirty! 🧑💻
Earlier, we looked at an example using the official AWS EC2 module, but in this section, we’ll create our own Terraform modules from scratch.
Our goal? To build a modular Terraform setup that provisions a VPC, Security Group, and EC2 instance, which in turn hosts a static portfolio page.
🧱 What We’ll Be Building
We’ll use some of the most common AWS infrastructure components:
VPC (Virtual Private Cloud)
Subnets
Internet Gateway (IGW)
Route Table (RT)
Security Group (SG)
EC2 Instance
These resources form the foundation of most web applications, whether it’s an e-commerce site, a personal portfolio, or a marketing page.
In production environments, infrastructure code is usually broken down into reusable modules to:
Avoid duplication
Improve maintainability
Enable better team collaboration
For example, a DevOps team might maintain a VPC module that’s reused across multiple environments — dev, staging, and production — ensuring consistent infrastructure everywhere.
Our demo follows the same principle. We’ll organize our project into separate modules for vpc
, security-group
, and ec2
, each containing its own:
main.tf
— core resource definitionsvariables.tf
— variable declarationsoutputs.tf
— exported outputs
We’ll keep variables flexible (no default values) so they can be customized per environment.
⚙️ Automation with user_data
Inside our EC2 module, we’ll use a user_data
script to automatically install Apache and deploy a simple index.html
page.
In real-world deployments, such automation scripts (or tools like Ansible or Chef) are used to configure servers automatically at startup — eliminating the need for manual setup.
This approach is standard for bootstrapping web servers, application backends, or API services.
📂 Project Structure
The complete code is available in my GitHub repository:
👉 https://github.com/Pravesh-Sudha/terra-projects
Navigate to the terra-modules
directory, and you’ll find the following structure:
terra-modules/
|
│-- main.tf # Calls our custom modules
├── variables.tf
│-- outputs.tf
|
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ │
│ ├── security-group/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ │
│ └── ec2/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
Here, each submodule (vpc
, security-group
, ec2
) defines its own configuration logic, and the root main.tf
file simply calls these modules and passes the required variables.
Once everything is set, our static website server configuration will be ready to deploy.
🚀 Running the Terraform Project
Now let’s see it in action!
-
Initialize Terraform
terraform init
This initializes the project and fetches all three modules.
-
Review the execution plan
terraform plan
You’ll see that Terraform plans to create **7 resources** in total.
-
Deploy the infrastructure
terraform apply --auto-approve
Wait for a minute or two — once the deployment completes, Terraform will output the **public IP address** of your EC2 instance.
-
View your static website
Copy the public IP into your browser, and you’ll see your web server running — serving the
index.html
page! 🎉
🧹 Cleanup
When you’re done experimenting, make sure to tear down the infrastructure to avoid unnecessary AWS costs:
terraform destroy --auto-approve
With that, you’ve successfully created your own set of Terraform modules — a foundational skill for structuring production-grade Infrastructure as Code (IaC).
In case you are wondering how to reuse the components, here is an example for your file to create another VPC using our custom module:
module "vpc" "another-vpc" {
source = "./modules/vpc"
vpc_cidr = "<Provide the CIDR>"
subnet_cidr = "<Provide the CIDR>"
app_name = var.app_name
}
variable "app_name" {
default = "Another-App"
type = string
description = "Name of the Application"
}
💡 Conclusion
And that’s a wrap! 🎉
In this guide, we explored the core concept of Terraform modules — what they are, why they’re beneficial, and how they make your infrastructure reusable, scalable, and team-friendly.
We then put theory into practice by creating our own Terraform modules for VPC, Security Group, and EC2, and deployed a working static website on AWS — all using clean, modular Terraform code.
By now, you should have a solid understanding of how to:
Structure Terraform projects into reusable modules
Simplify complex configurations
Collaborate efficiently within teams
Keep your infrastructure code DRY and maintainable
The next step? Try expanding your modules! Add resources like S3 buckets, RDS databases, or Load Balancers — and see how modular design keeps your Terraform journey organized and production-ready.
References
If you’d like to go deeper, here are the key references I used while crafting this guide:
Let’s Connect 🌐
If you enjoyed this blog and want to explore more about DevOps, Terraform, and Cloud automation, feel free to connect with me here:
If you found this post helpful, share it with your DevOps peers and drop your thoughts in the comments — I’d love to hear how you use Terraform modules in your projects!
👋 Adios Amigos!
Top comments (0)