BlogIaC
Writing reusable Terraform modules that don't fight you
Design modules with clear inputs and outputs, sane defaults, and versioning so teams reuse them without forking.
A good Terraform module is a small contract: clear inputs, useful outputs, sane defaults, and a version. A bad one leaks its internals and forces every caller to fork it. The difference is mostly discipline at the interface.
The shape of a module
modules/bucket/
modules/bucket/variables.tf # the inputs (the contract)main.tf # the resourcesoutputs.tf # what callers can useREADME.md # examples + the contract in prose
Validate the inputs
Defaults make the common case one line; validation turns a misconfiguration into a plan-time error instead of a 2am incident.
variables.tf
variable "name" {type = stringvalidation {condition = can(regex("^[a-z0-9-]+$", var.name))error_message = "name must be lowercase alphanumeric or dashes."}}variable "versioning" {type = booldefault = true}
Call it with a pinned version
main.tf
module "logs" {source = "app.terraform.io/acme/bucket/aws"version = "~> 2.1"name = "acme-app-logs"}
Never float a module version
An unpinned source means a module release can change your infrastructure on the next plan. Pin with ~> and bump deliberately.