Blog · Drift

Terraform Drift Detection: The Complete Guide

What Terraform drift is, why it happens, and four ways to detect it — from a manual terraform plan to automated PR-time detection.

9 min read

What is Terraform drift?

Terraform drift is any gap between the infrastructure your .tf files declare and the infrastructure that actually exists in your cloud account. Terraform's whole model assumes it is the single source of truth — every resource it manages should only ever change through a Terraform apply. Drift is what happens when that assumption breaks: someone edits a security group in the AWS console, a Lambda's memory setting gets bumped by an incident-response script, or a separate CI pipeline touches the same S3 bucket your Terraform config also owns.

Left alone, drift compounds. The next terraform plan either silently reverts someone's manual fix (bad if it was intentional) or produces a plan diff nobody can explain (bad either way), because the plan is comparing the declared config against a state file that no longer matches reality.

Why Terraform drift happens

In practice, drift almost always comes from one of five sources:

  • Manual console changes — someone fixes something urgently in the AWS/GCP/Azure console instead of through a PR, usually during an incident.
  • Other automation touching the same resources — a separate deployment tool, a Lambda-based auto-remediation script, or a second Terraform workspace managing overlapping resources.
  • Provider-side defaults changing — a cloud provider changes a default value (e.g. a new AMI becomes the default) and the next refresh picks up a diff you didn't cause.
  • Out-of-band cleanup or cost-saving scripts — something that stops unused resources or deletes orphaned ones without going through Terraform.
  • Stale or partially applied state — a failed terraform apply that partially succeeded, leaving the state file out of sync with what was actually created.

Four ways to detect Terraform drift

1. Run terraform plan manually. The baseline method: run terraform plan and read the diff. It works, but it depends on someone remembering to run it, having the right credentials configured locally, and being able to tell real drift apart from an expected, in-progress change.

2. Scheduled drift-detection jobs. A cron job (GitHub Actions, Jenkins, whatever you have) runs terraform plan on a schedule and posts the output somewhere — Slack, a dashboard, an issue. This catches drift eventually, but with a lag, and it doesn't attach the finding to whichever pull request is about to touch the same resources.

3. Standalone drift-detection tools. Open-source tools scan your cloud account and your state file independently, then report unmanaged or drifted resources. Useful for a point-in-time audit; less useful as a continuous gate on new changes.

4. PR-time drift detection. The state is checked the moment a Terraform pull request opens, using short-lived read-only credentials against the live cloud account, and the result is posted directly on that PR — before anyone merges a change on top of resources that have already drifted.

What PR-time drift detection looks like

DriftGuard takes the fourth approach: it reads live resource state through short-lived, read-only credentials — AWS via STS AssumeRole, GCP via Workload Identity Federation, Azure via federated workload identity — and compares it against the Terraform plan attached to the pull request. No long-lived cloud keys are stored. Setup is a role ARN and a state location:

# In your repo settings (DriftGuard dashboard)
aws_role_arn: arn:aws:iam::123456789:role/DriftGuardReadOnly
state_bucket: my-tf-state-bucket
state_key: prod/terraform.tfstate
aws_region: eu-west-1

If you'd rather not connect a cloud account, DriftGuard falls back to comparing the plan against the committed terraform.tfstate — narrower coverage, but still useful. See the drift detection docs for the full setup.

Preventing drift, not just detecting it

  • Restrict console write access in accounts managed by Terraform — reserve it for break-glass incident response only.
  • Require every infrastructure change to go through a pull request, including "quick" fixes.
  • Run drift detection on every PR, not just on a schedule, so it's checked at the moment someone is about to build on top of the same resources.
  • Keep one Terraform workspace as the sole owner of a given resource — avoid two pipelines managing overlapping infrastructure.
  • Alert on failed applies immediately; a partially applied state is one of the most common causes of silent drift.

FAQ

What is Terraform drift?

Terraform drift is any mismatch between what your Terraform configuration declares and what actually exists in your cloud account. It happens whenever a resource is created, changed, or deleted outside of Terraform — for example, through the cloud console, a CLI command, another automation tool, or a provider-side default that changed.

Does terraform plan detect drift?

Yes, partially. terraform plan refreshes the state against the real infrastructure and shows a diff if something changed outside Terraform — but only for the resources already in that state file, only when someone remembers to run it, and only against whatever provider credentials happen to be configured locally. It does not run continuously or block a pull request on its own.

How is PR-time drift detection different from a scheduled drift scan?

A scheduled scan (e.g. a nightly terraform plan cron job) tells you drift happened after the fact, often hours or days later, with no PR to attach it to. PR-time detection reads the live resource state when a Terraform pull request opens and posts the delta directly on that PR, before it merges — so drift is caught at the moment someone is about to change the same resources.

See drift detection on your own Terraform pull requests.

Terraform Drift Detection: The Complete Guide | DriftGuard · DriftGuard