Terraform Directory Patterns: Organize by Application, Not Resource Type
Why stuffing all DNS or networking into one folder slows teams down—and how to restructure around the applications that use them.
Gabriel Levasseur
Founder
Terraform Directory Patterns: Organize by Application, Not Resource Type
Terraform's "just a folder of .tf files" flexibility tempts teams to create directories like dns/, networking/, or sensitive/ so every similar resource lives together. It feels tidy—until the folders balloon, plans slow to a crawl, and engineers edit two distant places just to ship a feature. The sustainable alternative is to group resources by the application or product surface they serve so every related change lives in one place.
The resource-bucket anti-pattern
- Huge folders, tiny intent: A
dns/directory owning every record buries critical entries for production APIs next to staging experiments. Reviewers have to scan massive diffs to understand a simple change. - Split-brain workflows: Shipping a new endpoint means updating the app stack and the central DNS folder. Every deploy is a scavenger hunt across the repo.
- Slow, risky plans: Terraform must evaluate hundreds of unrelated resources because they're co-located. What should be a quick plan for
orders-servicenow touches unrelated records. - Hidden ownership debt: When everything "networking" sits under one team, product squads stop feeling responsible for the infrastructure their features need.
If shipping a feature requires touching two directories "because that's where DNS lives," you've organized around technology, not the customer experience.
Organize directories around applications
Group Terraform code by the slice of the platform a team ships: an application, a product surface, or a lifecycle boundary. Each directory owns the infrastructure that feature needs—compute, storage, DNS, queues, certificates, observability.
infra/
orders-service/
main.tf
dns.tf
autoscaling.tf
outputs.tf
accounts-service/
main.tf
dns.tf
shared/
vpc/
logging/
With this shape:
- Engineers change one directory per feature.
- Plans stay scoped to the infrastructure the feature owns.
- Reviews align with customer impact, not provider taxonomy.
- Ownership is obvious: the team behind
orders-servicemaintains its full stack.
Keep shared building blocks deliberate
You still need shared primitives—VPCs, base networking, central logging. Isolate them in clear shared/ or platform/ directories that rarely change. Application directories depend on them through outputs, not by editing shared code in place.
Migrating away from resource buckets
- Inventory consumers: List which apps rely on the central
dns/ornetworking/folders. - Carve out vertical slices: For each app, move the records, load balancer listeners, and IAM policies into a new
app-name/directory, wiring outputs back to shared networking as needed. - Introduce app-level pipelines: Run plans and applies per app directory so teams deploy autonomously.
- Leave a rump module: Keep a thin
dns/wrapper that exposes shared zones or top-level records, but forbid app-specific changes from landing there.
Guardrails that keep the pattern healthy
- Require a
README.mdin every app directory describing context, owners, and dependencies. - Lint for cross-app references that bypass outputs.
- Visualize relationships in Cora to ensure apps consume only the shared primitives they truly need.
By structuring Terraform around the applications customers touch, you shorten review cycles, reduce coordination overhead, and keep plans focused. When every feature can ship from a single directory, your platform scales with your product instead of fighting it.
Model your Terraform graph in Cora
Ready to see Cora in action? Jump into the product experience tailored to this article.
Model your Terraform graph in CoraKeep reading
View allCora Fall 2025 Roadmap: What's Shipping Next
How we're building the definitive Terraform visualization platform, from blast radius analysis to compliance dashboards and beyond.
Gabriel Levasseur
Founder
Terraform Complex Data Structures Are a Smell—Use Modules Instead
Why giant JSON inputs plus `for_each` loops hurt teams, and how to replace them with composable Terraform modules.
Gabriel Levasseur
Founder
Design Terraform Modules for the Right Consumer
Avoid fake abstractions by tailoring modules to the engineers who actually use them—and handling edge cases the right way.
Gabriel Levasseur
Founder