Loading...

About the Terraform Module Scaffolder

A Terraform module is a directory with a conventional shape rather than anything special: resources in one file, inputs in another, outputs in a third. The convention matters because it is what makes a module readable to someone who did not write it — they know where to look for the interface without reading the implementation.

The interface is the part worth thinking about. Every variable should have a type and a description, and defaults belong only on genuinely optional inputs. A variable with a default that callers are expected to override is a trap, because forgetting to set it produces a silently wrong resource rather than an error.

Outputs are how modules compose, and they are usually too sparse. If a caller has to construct an ARN by string concatenation, or reach into a resource the module owns, the module has not exposed enough. Output the identifiers other configurations will genuinely need, and keep them stable — an output rename is a breaking change to every consumer.

Frequently asked questions

Should every variable have a default?

No. Defaults belong on inputs that are genuinely optional. A required input with a default fails silently when someone forgets to set it, creating a resource with the wrong configuration instead of an error at plan time. Omitting the default makes Terraform demand a value, which is exactly what you want for anything that must be decided deliberately.

Where do provider configurations belong?

In the root module, not in child modules. A module that configures its own provider cannot be used more than once with different settings — for example in two regions — and produces confusing errors when composed. Declare required providers and their version constraints in the module; configure them at the root.

How should I version and publish a module?

Tag releases and have consumers pin to a version constraint rather than a branch. Consuming a branch means an upstream change alters someone else's plan with no action on their part, which is how a routine apply becomes an incident. Semantic versioning with a changelog is enough; the registry is optional.

How granular should modules be?

Coarse enough to be worth calling. A module wrapping a single resource with no added logic is indirection without benefit — callers can use the resource directly. Modules earn their existence when they encode a decision: naming, tagging, sensible defaults, or several resources that must be configured consistently together.

Should I use count or for_each?

for_each in nearly all cases. count indexes by position, so removing an item from the middle of a list shifts every subsequent resource's address and Terraform plans to destroy and recreate them. for_each keys by a stable identifier, so removing one item affects only that resource — which is the difference between a safe change and a surprise.

Need this managed for you, not just automated?

We're also a hands-on DevOps consultancy — Kubernetes, CI/CD, and cloud infrastructure.

Explore Our Services