Use this page for any infrastructure change. If a resource is not in code yet, the change starts by importing it. Clicking in the console is reserved for break-glass, and even then it is followed by a code change the same day.
The flow
flowchart LR B[Branch] --> C[Change .tf] C --> F[fmt + validate + lint] F --> M[Open MR] M --> P[CI or Atlantis runs plan, posts it] P --> R[Human reviews plan] R --> A[Apply from MR or after merge] A --> V[Verify, close ticket]
Tools that run plan and apply from the MR: Atlantis, Spacelift, Terraform Cloud or Enterprise, env0, or a plain CI pipeline. Pick one and use it for every stack.
Before opening the MR
terraform fmt -recursive
terraform init -backend=false && terraform validate
tflint --recursive # optional linter
checkov -d . --quiet # or trivy config . ; policy and security checks
- One logical change per MR. Do not mix a refactor with a behaviour change.
- MR description: what, why, blast radius, rollback, ticket link.
Read a plan like a reviewer
- Count:
Plan: X to add, Y to change, Z to destroy. Any destroy needs a sentence explaining it. -
-/+(replace) on stateful resources (databases, volumes, buckets, load balancers) is a red flag. Find out which attribute forces it. - Changes to IAM, security groups, KMS key policies and route tables get a second reviewer.
- Unexpected changes mean drift. Stop and investigate before applying.
In Atlantis the comments are:
atlantis plan -p <project>
atlantis apply -p <project>
Handle drift
# Read-only drift check: exit code 2 means changes exist
terraform plan -detailed-exitcode -lock=false -refresh=true
| Drift cause | Fix |
|---|---|
| Someone changed it in the console | Decide which is right; update code or re-apply code |
| Provider default changed | Pin the attribute explicitly |
| Resource deleted outside Terraform | Remove from state or recreate via MR |
| Resource never in code | Import it |
Schedule a nightly drift plan per stack and report non-zero results to the team; that is good automation for an agent (see automation blueprint).
Import existing resources
import {
to = aws_s3_bucket.logs
id = "<bucket-name>"
}
terraform plan -generate-config-out=generated.tf # review and tidy the generated code
State hygiene
- Remote state with locking (for example S3 with native lockfile or a DynamoDB table), encrypted, versioned, and access-limited.
- One state per environment per component. Small states plan fast and fail small.
-
terraform state mvandstate rmare changes too: do them in an MR usingmoved {}andremoved {}blocks where possible. - Never edit a state file by hand. Never commit state to git: it often contains secrets.
Rollback
Revert the MR and apply the revert through the same flow. For destroys, rollback means re-creating, which may lose data: that is why destroys of stateful resources need backups and a second approval (see backups and restore testing).
Pitfalls
terraform applyfrom a laptop "just this once". It bypasses review and the lock may conflict with CI.-targetas a habit. It hides the rest of the plan and leaves drift.prevent_destroymissing on databases and buckets that hold data.- Secrets in
.tfvarscommitted to git. Read them from a secret manager data source instead.