Terraform Style
Terraform can create, replace, or delete real infrastructure. Keep plans predictable and blast radius small.
Source baseline: docs/src/style/terraform.mdx. Source: docs/src/style/terraform.mdx
Defaults
Every root module: remote backend, locking enabled, explicit Terraform and provider versions, saved plan reviewed before apply, default tags on all resources.
State Boundaries
Split state by ownership and failure domain: networking, IAM, service stacks, data stacks. One key per service and environment. No monolithic state files. A single change should not require planning unrelated infrastructure.
Layout
iac/
├── modules/ # Reusable units
├── stacks/ # Deployable root modules
└── tfvars/ # Env-specific inputs and backend config
Variables and Data Sources
No hardcoded mutable identifiers (AMI IDs, subnet IDs, VPC IDs). Use variables for configuration and data sources for discovery. Hardcoded values acceptable for stable contracts like AWS managed policy ARNs.
Plan and Apply
Always use saved plans: terraform plan -out=tfplan, then terraform apply -input=false tfplan. Review plan output before apply. Apply production from CI/CD only. Require approval gates on protected environments. No -auto-approve when applying a saved tfplan.
Version Pinning
Set Terraform and provider constraints in every root. Commit .terraform.lock.hcl for CI/CD roots.
Tagging
Provider-level default tags: Environment, ManagedBy, Project, Owner. Add resource-specific tags as needed.
Multi-Cloud
Default rule: one state per cloud. Keep AWS, GCP, and Azure in separate stacks unless resources need atomic cross-cloud changes.
Infrastructure Contract
| Concern | Default | Banned pattern | Proof |
|---|---|---|---|
| State | Split by ownership and failure domain | One monolithic state for unrelated resources | Plan touches only intended stack |
| Apply | Saved plan reviewed before apply | Direct production apply or -auto-approve |
Stored tfplan plus approval trace |
| Versions | Terraform and provider constraints in every root | Floating provider behavior | Lockfile and init/validate |
| Inputs | Variables/data sources for mutable identifiers | Hardcoded mutable AMI/subnet/VPC ids | Plan review |
| Tags | Provider defaults plus resource-specific tags | Untagged managed resources | Plan or policy check |
Proof
Terraform proof is the reviewed plan, not the config diff. A style-compliant change shows which stack owns the resources, what the plan will create/update/delete, and why the blast radius is limited to the intended failure domain.
Style Rule Contract
This page follows Style Rule Contract: name the default pattern, banned pattern, reason, exception, proof, and codification path clearly enough that the next agent can apply the rule without asking Kevin again. If a local repo has a stricter rule, follow the local rule and update the wiki when the decision becomes durable.
Timeline
- 2026-07-01 | Aligned this page with Style Rule Contract so style guidance names default behavior, banned patterns, exceptions, proof, and codification expectations. Source: User request, 2026-07-01
- 2026-07-01 | Added an infrastructure contract and proof rule for state boundaries, saved plans, version locks, inputs, tags, and blast-radius review. Source: User request, 2026-07-01