Version Terraform Modules Properly
Tag module releases, pin module sources to exact versions, keep inputs documented, and use a lock file so plans are reproducible.
Task
Adopt versioned Terraform modules with pinned sources and a committed lock file.
When to use this
Modules are referenced by a moving branch, so a change upstream silently alters an environment on the next apply and plans are not reproducible.
Prerequisites
- Terraform 0.14+ for dependency lock files
- A registry or Git repo for modules
Steps
1. Tag module releases semantically and reference exact tags.
Expected output: A given config always uses the same module code.
2. Commit the dependency lock file.
Expected output: Providers and modules resolve reproducibly.
3. Document inputs and outputs and validate with terraform-docs or validate.
Expected output: Consumers know the contract.
4. Test plans in a staging workspace before applying to production.
terraform init -upgrade && terraform providers lockExpected output: Changes are seen before they land.
Troubleshooting
Problem
Upgrade unexpectedly changes resources
Solution
Pin to an exact tag and upgrade deliberately with a reviewed plan, not by tracking a branch.
Problem
Lock file not committed
Solution
Commit .terraform.lock.hcl; without it each machine may pick different provider versions.
Limitations
- Pinning modules means upgrades are manual and can lag behind security fixes if not scheduled.
- Lock files are platform-sensitive; teams on mixed OSes should use the providers lock command.
Context
Adopt versioned Terraform modules with pinned sources and a committed lock file. Modules are referenced by a moving branch, so a change upstream silently alters an environment on the next apply and plans are not reproducible.
Steps
- Tag module releases semantically and reference exact tags. Expected: A given config always uses the same module code.
- Commit the dependency lock file. Expected: Providers and modules resolve reproducibly.
- Document inputs and outputs and validate with terraform-docs or validate. Expected: Consumers know the contract.
- Test plans in a staging workspace before applying to production. Expected: Changes are seen before they land.
module "network" {
source = "git::https://github.com/acme/network.git?ref=v2.3.1"
cidr = var.cidr
}
# commit .terraform.lock.hcl
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64
Verify
- A given config always uses the same module code.
- Providers and modules resolve reproducibly.
- Consumers know the contract.
- Changes are seen before they land.
Limits
- Pinning modules means upgrades are manual and can lag behind security fixes if not scheduled.
- Lock files are platform-sensitive; teams on mixed OSes should use the providers lock command.