Drift Detection & Troubleshooting
Detect out-of-band changes with plan/refresh, then read plans carefully and reach for TF_LOG when things get weird.
Drift is when the real world stops matching your state — someone clicked in the console, an autoscaler resized things, a manual hotfix went in. Undetected drift is how "it works on my machine" becomes "the apply deleted the fix."
Detecting drift
terraform plan— a clean plan (no changes) means no drift. Run it on a schedule in CI to catch surprises early.terraform plan -refresh-only— shows how state differs from reality without proposing config changes.
Reading a plan like a pro
+create,-destroy,~update in place.-/+means replace — the scary one. Always find out why before approving.
When you are truly stuck
Set TF_LOG=DEBUG to see the raw provider API calls. It is verbose, but it is how you find out which exact request is failing and why.
Example
# Is reality still matching state? A clean plan = no drift.
terraform plan -detailed-exitcode
# 0 = no changes, 1 = error, 2 = drift/changes present
# Show ONLY how the world drifted from state
terraform plan -refresh-only
# Reconcile state with reality (no config changes)
terraform apply -refresh-only
# Something baffling? Turn on the firehose.
TF_LOG=DEBUG terraform plan 2> debug.logWhen to use it
- An operator runs terraform plan on a schedule every hour to detect out-of-band changes made in the AWS console and alert the team before they cause bigger problems.
- An SRE uses TF_LOG=DEBUG when a plan fails with an opaque provider error, finding the root cause in the detailed API request and response logs.
- A team adds a drift-detection job to their CI pipeline that runs terraform plan -detailed-exitcode and pages on-call if exit code 2 (changes detected) is returned.
More examples
Detect drift with plan
Uses -refresh-only to see only state drift (no config changes) and -detailed-exitcode to signal drift to a calling script.
# Run plan against live infrastructure
terraform plan -refresh-only
# If output shows changes, someone modified infra outside Terraform
# Exit code 2 means drift was found
terraform plan -detailed-exitcode
echo "Exit code: $?" # 0=no changes, 1=error, 2=drift foundEnable verbose logging
Enables DEBUG logging to a file so the full API request/response cycle can be inspected when a plan or apply produces cryptic errors.
# Set log level: TRACE, DEBUG, INFO, WARN, ERROR
export TF_LOG=DEBUG
export TF_LOG_PATH=/tmp/terraform-debug.log
terraform apply
# Inspect the log
grep 'Error\|error\|WARN' /tmp/terraform-debug.log | head -50Scheduled drift detection in CI
Runs a scheduled GitHub Actions job every hour that fails and alerts if Terraform detects out-of-band infrastructure drift.
name: Drift Detection
on:
schedule:
- cron: '0 * * * *' # every hour
jobs:
detect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- name: Check for drift
run: |
set +e
terraform plan -detailed-exitcode -refresh-only
if [ $? -eq 2 ]; then
echo "DRIFT DETECTED" && exit 1
fi
Discussion