How-to
How to Detect Terraform Drift
Detect Terraform drift by refreshing state, saving a plan, and classifying the diff — then add cloud inventory for resources Terraform does not manage. A practical scan loop you can run in CI.
Detect Terraform drift · reference
Detecting Terraform drift means answering one question on a schedule: does live infrastructure still match what this workspace declares?
You do not need a new product to start. You need a refreshed view of state, a saved plan, and a rule for what to do with the diff. What infrastructure drift is is the vocabulary. This page is the scan.
1. Refresh before you compare
A plan against stale state lies.
terraform plan -refresh-only -out=refresh.tfplan
terraform show -json refresh.tfplan
-refresh-only updates Terraform's view of remote objects without proposing configuration changes. If this step fails (permissions, state lock, provider error), stop. A “clean” plan after a failed refresh is not a clean bill of health.
OpenTofu accepts the same flags. Pin the CLI in CI so local laptops are not the detector of record.
2. Save a normal plan as the artefact
terraform plan -out=drift.tfplan
terraform show -no-color drift.tfplan
The plan file — not a screenshot of the terminal — is what you store, diff over time, and attach to a ticket. terraform show -json is what policy engines and custom classifiers should read.
Watch for:
- in-place updates on security groups, IAM, and encryption flags,
- destroy/recreate on resources with poor
for_eachkeys (see for_each vs count), - noise from tags and computed fields you have already accepted.
3. Classify before anyone “fixes” it
A raw plan is not a drift program. Use a coarse rubric:
| Signal in the plan | Treat as |
|---|---|
| Ingress, IAM trust, public access, encryption off | Security-critical |
| DNS, load balancers, database parameters | Reliability-critical |
| Tags, names, sizes within policy | Hygiene |
| Known autoscaling / vendor fields | Expected — do not page |
The drift triage playbook covers import vs reconcile vs accept once you have this label.
4. Look for what Terraform cannot see
terraform plan only reasons about addresses in state.
Add a second pass: list security groups, buckets, or IAM roles from the cloud API and subtract objects that have a state address. Leftovers are unmanaged resources — often the most dangerous drift because the next apply will not touch them until someone imports or deletes them.
You do not need to inventory the entire account on day one. Start with the resource types that have burned you.
5. Put the scan on a clock
- Run refresh + plan on a schedule against the production workspace.
- Fail the job on provider errors, not only on “changes present.”
- Open one issue per classified finding, not one issue per resource attribute.
- Never auto-apply a drift plan unless the change is in an allow-listed, low-risk class.
CI structure for the validate/plan half lives in IaC validation pipelines.
When more than one team shares the account
You still classify, then import, reconcile, or accept. You also need one report and an apply that cannot skip policy. Drift detection and approvals are that loop without a pile of one-off scripts.
