Skip to main content

Command Palette

Search for a command to run...

🎯 Designing, Deploying, and Validating a Terraform Module on AWS

Published
6 min read
🎯 Designing, Deploying, and Validating a Terraform Module on AWS
F

🎓 Certified: Aviatrix ACE

👋 Hi, I’m Betty Musari — a former food scientist now diving deep into the world of DevOps and cloud engineering. I write about my hands-on journey with AWS, Docker, and CI/CD, translating complex concepts into clear, beginner-friendly stories. With a scientific mindset and a passion for continuous learning, I'm on a mission to demystify the cloud — one container at a time. ✨As a Food Technologist & Sales Strategist turned DevOps Explorer, I leverage problem-solving and client-centric skills to build resilient cloud systems. Currently mastering CI/CD pipelines, Azure, and Infrastructure as Code (Terraform) to automate deployments like a well-oiled production line. Passionate about merging operational efficiency with technical innovation—because great systems, like great recipes, require precision and scalability.

By Betty Musari

Abstract

Before jumping into code, it’s always best to define scope. A basic Terraform module on AWS should do a few things well:

  • Provide clean interfaces (inputs + outputs).

  • Wrap a logical set of resources (like a VPC + subnets or an EC2 + security group).

  • Follow the standard Terraform layout: main.tf, variables.tf, and outputs.tf.

  • Keep security, reusability, and maintainability in mind from the start.

Good modules aren’t about writing “fancy” Terraform. They’re about building something reusable, clean, and production-friendly — without needless complexity.


Introduction

If you’ve ever tried managing cloud resources manually, you already know it’s a recipe for chaos. One missed checkbox in the AWS console and suddenly your subnet isn’t routing, or your EC2 is wide open to the internet 🙈.

That’s why we reach for Infrastructure as Code tools like Terraform. Instead of clicking through endless dashboards, you define your infrastructure once — in code — and reuse it everywhere.

But here’s the magic: Terraform modules.
They let you bundle up repeatable resource patterns (like a VPC, RDS, or EC2 cluster), give them parameters, and use them again and again across teams and environments.

In this blog, we’ll build a simple AWS VPC module step by step, use it in a root configuration, and then evaluate it against best practices.


🛠Step 1: Prerequisites & Setup

For this project, I’m running on Ubuntu (64-bit) inside VMware/VirtualBox.

Make sure you’ve got:

  • Terraform installed → check with terraform version

  • AWS CLI installed → check with aws --version

Without these two, we’re not going far.

🛠Step 2: Laying the Foundation: Project Structure

A clear structure is the first step toward a maintainable codebase. Let's create a logical layout that separates our module from the code that uses it.

Now, let’s set up our project folder: mkdir terraform_project, cd terraform_project, mkdir -p modules/vpc

📂 Project Folder Structure

terraform_project/
├── main.tf
├── outputs.tf
└── modules/
    └── vpc/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

We’ll drop our main.tf, variables.tf, and outputs.tf inside modules/vpc, then build a root module in the top-level folder.

📂Step 3: Crafting the Module:

The Three Pillars (main.tf, variables.tf, outputs.tf)

A well-architected module rests on three pillars: resources, configurability, and usability.

This shows readers the standard Terraform mo

  • main.tf → actual AWS resources

  • variables.tf → input variables (with types, defaults, and validations)

  • outputs.tf → outputs for consumers (like VPC IDs, subnets, AMI IDs)

Following this structure keeps your module tidy, predictable, and reusable.


✍️Step 4: Writing the VPC Module

1. main.tf: The Core Resources:
This file houses the primary resource definitions. We define an AWS provider, a VPC, and a subnet, and fetch the latest Amazon Linux AMI dynamically. (inside modules/vpc)

2. variables.tf: The Module's Interface
This is how users configure your module. We define an input for the region with a sensible default.

3. outputs.tf: The Module's Results
Outputs expose valuable information from the module, making it usable by other parts of your configuration.

Boom 💥 — our VPC module is ready.


🏗 Step 5: Using the Module in the Root Project

Now, cd back to terraform_projects

create main.tf:

And an outputs.tf to expose the EC2 private IP:

🔐 Best Practices

  • Tag everything → Name, Environment, Owner. Tags save lives (and cloud bills).

  • Use least privilege → Your module shouldn’t hand out admin-level IAM roles.

  • Secure by default → No wide-open security groups, prefer private subnets, enable encryption where possible.

  • Version pinning → Lock Terraform and provider versions to avoid surprises.

Before deploying, format the code:

terraform fmt -recursive

☁️ Architecture Diagram ofwhat we’ve done so far:

(VPC → Subnet → EC2 instance)

+------------------+
|      AWS VPC     |
|  (10.0.0.0/16)   |
+------------------+
          |
          v
+------------------+
|   Subnet         |
|  (10.0.1.0/24)   |
+------------------+
          |
          v
+------------------+
|   EC2 Instance   |
|  Amazon Linux 2  |
|   t2.micro       |
+------------------+

🚦Step 6: Initialize, Validate & Plan

Run these in sequence: terraform init and terraform validate

terraform plan

🚀Step 7:Deployment

If all is good, deploy by running: terraform apply --approve

Terraform will spin up:

  • 1 VPC

  • 1 Subnet

  • 1 EC2 instance

…and return your instance’s private IP from outputs.tf.

👀Step 8: Check what Terraform is tracking:

Run: terraform state list

You’ll see the EC2 instance + three resources from the VPC module.

Finally, log in to the AWS Console → EC2 → Instances, and confirm your shiny new instance is running 🎉.


🔎Step 9: Verifying in the AWS Console

After Terraform finishes applying, you don’t just stop there. Let’s confirm visually:

  1. Log in to the AWS Management Console.

  2. In the search bar at the top, type “EC2”.

  3. Click Instances.

  4. You should now see the EC2 instance that Terraform deployed, running inside the VPC and subnet created by your module 🎉.

This step is a nice sanity check — Terraform says it worked, but the AWS Console lets you see the resources alive and breathing in your account.

✅ Testing & Validation

Post-deployment checks:

  • Can you ping/SSH into the EC2?

  • Are security groups restrictive enough?

  • Do tags + naming match your standards?

  • Is cost under control?


🎯 Conclusion

The Hallmarks of a Great Module

Building a Terraform module is more than just wrapping resources in a folder. It's an exercise in design thinking. A great module has:

  • A Clear Purpose: It encapsulates a single, logical concept (like a VPC or an application stack).

  • Clean Interfaces: Well-defined variables and outputs make it intuitive to use and hard to misuse.

  • Security by Default: It enforces secure configurations, like private subnets and closed security groups, unless explicitly told otherwise.

  • Maintainability: Consistent formatting, validation, and tests make it easy to update and scale.

This small project (VPC + EC2) might feel simple, but the principles scale 🧱:

  • Standard layouts (main.tf, variables.tf, outputs.tf)

  • Clean inputs/outputs

  • Proper defaults + validation

  • Automation for formatting, validation, and testing

The journey from a simple script to a reusable module is the journey from a novice to a proficient IaC engineer. By focusing on reusability, security, and maintainability, you build not just infrastructure, but a foundation that accelerates your team and ensures consistency and reliability for years to come. Now go forth and modularize

With these habits, you’re not just writing Terraform code — you’re building infrastructure Lego blocks your team can trust, reuse, and extend without fear.

👉 NOTE: don’t stress if your first module feels clunky. Every pro Terraform engineer once had a messy main.tf with everything jammed inside. The difference is, they kept iterating until they built clean, reusable modules. And now you’re on that same journey 🚀.

💡 Have you built your own Terraform modules before? I’d love to hear how you structure them, what best practices you follow, and even the mistakes you’ve learned from. Drop your thoughts in the comments — let’s share knowledge!