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
Design Terraform Modules for the Right Consumer
As a platform engineer, every Terraform module you publish is a product. Two very different audiences depend on that product:
- Other infrastructure engineers who want building blocks they can wire together however they need.
- Product engineers who just want to ship features and expect a paved path.
If you blur those audiences, you end up with abstractions that satisfy no one. Let’s clarify who you’re supporting and shape your modules accordingly.
Know which audience you’re serving
Start every module with a short README clause: "Primary consumer: product engineers" or "Primary consumer: platform networking". That declaration unlocks two mindsets:
| Consumer | What they need | How your module should respond |
|---|---|---|
| Product engineers | Outcomes, not infrastructure trivia | Hide the knobs, ship strong defaults, and wrap common tasks in opinionated interfaces. |
| Infrastructure engineers | Fine-grained control with guardrails | Expose detailed inputs, but still enforce policy and sensible defaults. |
The clearer you are up front, the easier it becomes to decide what goes inside—and what deserves a separate module.
Designing modules for product engineers
Product engineers shouldn’t learn VPC trivia just to get an HTTPS endpoint. Give them an interface that reads like their intention:
- Ship a real abstraction. Expose the handful of inputs that represent the outcome—
service_name,container_port,domain_name. Everything else should be an opinionated default. - Be boldly opinionated. If 90% of teams should enable access logs, flip them on by default. Document how to opt out instead of forcing every team to make the same decision.
- Duplicate when necessary. It’s okay to publish
ecs-web-serviceandecs-worker-serviceif the ergonomics differ. Avoid the temptation to combine them behindservice_mode = "worker"flags that confuse consumers. - Compose behind the scenes. Build your higher-level module by stitching together smaller primitives (networking, logging, alarms). You can swap pieces later without breaking the contract.
The goal: a product engineer copies 10 lines, sets three variables, and gets a working service on day one.
Designing modules for other infrastructure engineers
When you’re building for peers on the infra team, you can expose more surface area—but discipline still matters.
- Stick to best practices. Enforce tagging, encryption, and guardrails even if you expose granular inputs. Colleagues expect secure defaults, not a raw AWS mirror.
- Favor composable building blocks. Provide modules like
alb,alb-logging, oraurora-clusterthat slot together cleanly. Infra engineers can assemble tailored stacks without cracking the module internals. - Evolve with versioning. Because these modules expose sharper edges, publish clear upgrade guides and semantic version bumps when you add or remove inputs.
Think of these modules as a curated toolbox—flexible, but still safe.
Favor composition over kitchen-sink toggles
Whether you’re targeting product or platform engineers, avoid the boolean flag explosion. Composable modules keep contracts crisp:
module "alb" {
source = "../modules/alb"
name = var.service_name
}
module "alb_logging" {
source = "../modules/alb-logging"
alb_arn = module.alb.arn
enabled = var.enable_logging
}
Wrap up
Great Terraform libraries feel intentional. Decide who you’re serving, shape the abstraction to that audience, and lean on composable building blocks so you can evolve quickly. Do that, and your modules become a force multiplier for every engineering team—not another layer of YAML to debug.
Share your module library with Cora
Ready to see Cora in action? Jump into the product experience tailored to this article.
Share your module library with CoraKeep reading
View allTerraform 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
Stop Building Terraform God Modules
Why modules with 40 inputs and passthrough variables slow teams down—and how to design reusable building blocks instead.
Gabriel Levasseur
Founder
Cora 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