🧱 Modular Terraform Changed the Game for My AWS Setup


A beginner’s journey from messy .tf
files to reusable, production-grade infrastructure
🚀 Intro – From Working to Maintainable
When I began deploying my Drupal application on AWS using Terraform, I wasn’t thinking about code structure. I just wanted to get it working — and I did.
But as my infrastructure grew — with ECS, RDS, ALB, and now EFS — things started to break. My main.tf
turned into a spaghetti bowl of AWS resources, hardcoded values, and copy-pasted blocks.
That’s when I paused, learned about Terraform modules, and refactored everything.
In this article, I’ll share why modular Terraform isn’t just “nice to have” — it’s essential for anyone building real-world infrastructure.
😵 What My Terraform Setup Looked Like Before
When I first set up my Terraform project, I followed what many beginners do — throw everything into a single directory:
terraform/
├\2500─ main.tf
├\2500─ variables.tf
└\2500─ outputs.tf
It started with a simple ECS task. Then came the cluster, the ALB, the RDS database, the security groups… all jammed into the same files.
At first, it worked. But soon I ran into serious issues:
❌ 1. Everything was tangled together
I had to scroll through hundreds of lines to find and edit anything. One tiny change in the security group could break the ECS config because it was all mixed.
❌ 2. Copy-paste ruled the repo
Whenever I needed a new resource — another SG, subnet, IAM policy — I’d copy an existing block and tweak it. This made the code repetitive and fragile.
❌ 3. Hardcoded values everywhere
Subnets, region names, AMIs, even account IDs were hardcoded. Switching regions or accounts would mean rewriting most of the code.
❌ 4. No separation of concern
If I wanted to destroy just RDS or just reconfigure ECS, I couldn’t. Everything was coupled tightly, so I was stuck with all-or-nothing changes.
My Terraform setup technically worked, but it wasn’t scalable, readable, or safe. I needed something better.
🧩 Why Modular Terraform Matters
That’s when I discovered modular Terraform — a way to break your infrastructure into clean, reusable blocks of code.
At a high level, here’s what modules gave me:
✅ Separation of concerns
Each module focuses on a single part of my setup — network
, ecs
, rds
, efs
, etc. No more tangled resource definitions.
✅ Reusability
I can now reuse the same ecs/
module for other services or projects. It’s just a matter of changing the input variables.
✅ Cleaner organization
Terraform feels like programming again. Each folder is like a class or function. Easier to read, debug, and maintain.
✅ Safer changes
If I change only the ecs
module, I know it won’t affect RDS or VPC unintentionally. This gives me the confidence to experiment and scale.
📁 How I Structured My Project
Here’s how I reorganized my Terraform codebase into modules:
terraform/
├── network/
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── ecs/
│ ├── ecs_service.tf
│ ├── outputs.tf
│ └── variables.tf
├── rds/
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── efs/ # coming soon
├── iam/
│ └── main.tf
├── root main.tf (entry point)
Now, instead of a single giant main.tf
, my root config calls each module with only the necessary variables.
🔗 Connecting Modules Without Hardcoding
This was another game-changer.
To connect resources across modules (e.g., VPC → ECS), I use:
🧠 1. terraform_remote_state
This lets me access outputs from other modules without hardcoding IDs.
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-tf-state-bucket"
key = "network/terraform.tfstate"
region = "ap-south-1"
}
}
🔍 2. Terraform data
sources
For example, to get the current AWS Account ID and avoid hardcoding:
data "aws_caller_identity" "current" {}
output "account_id" {
value = data.aws_caller_identity.current.account_id
}
These tricks make the setup flexible and safer across regions, environments, and teams.
🧠 What I Learned (So Far)
If you’re still writing Terraform in one big file or copy-pasting code, here’s my advice:
Start small, but modularize early
It’s easier to break it down now than laterAvoid hardcoding
Usedata
sources and remote state wherever possibleThink of Terraform like code
Would you write your entire app in one function? No — treat infrastructure the sameDon’t fear remote state
It feels scary at first, but it’s essential for cross-module communication
🔮 What’s Next?
I’m currently working on integrating EFS (Elastic File System) to persist Drupal’s file uploads and settings across ECS task restarts. That will fix the annoying issue where the Drupal install screen keeps reappearing.
Stay tuned — my next article will cover that journey and how I finally made my containers stateful where it mattered.
📝 Summary
I went from a giant main.tf
file to a clean, modular Terraform setup.
It made my AWS infrastructure easier to read, reuse, and scale.
If your infra is growing and your Terraform is breaking — go modular before you go mad.
📇 Want to See the Code?
I’ve pushed my AWS setup to GitHub here:
🔗 github.com/deepakaryan1988/Drupal-AWS
Subscribe to my newsletter
Read articles from Deepak Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
