A Layered Troubleshooting Method

Scenario: terraform apply fails with a confusing error, and under time pressure the team's first instinct is to delete .terraform, state, and the lock file and start fresh - destroying exactly the evidence that would have shown which layer was actually at fault.

By now you've met almost every layer where a Terraform failure can originate, across earlier modules - this section is about diagnosing which one you're actually looking at, systematically, rather than guessing. Work from the outside in: is it a CLI/configuration syntax problem (Module 2's validate)? An initialization or dependency problem (Module 3's provider/module resolution)? A backend, state, or locking problem (Module 5)? A provider authentication problem (Module 3)? A provider API or network-reachability problem? A resource-level precondition or quota rejection from the real API? Or a post-create consistency issue, where the object was created but some attribute isn't yet reflecting reality?

Reproduce the failure with the smallest, safest command that still triggers it (often plan, not a full apply), read the entire error message rather than skimming just the first line - Terraform errors frequently wrap a provider's own more specific error underneath, and that deeper message usually names the exact field or permission that was actually rejected. Compare Terraform and provider versions against what's expected, inspect state with the read-only state show/state list commands rather than touching it, and change exactly one variable at a time so you can tell which change actually mattered. TF_LOG=DEBUG remains your most detailed diagnostic tool when everything else is ambiguous, with the same handling caution from Module 3: it can contain credentials and full API payloads.

Analogy: A competent doctor takes a careful history and checks vital signs before ever reaching for a scalpel. Deleting caches, force-unlocking a lock, or wiping state at the first sign of trouble are all procedures - and like any procedure performed before an actual diagnosis, they can make things measurably worse rather than better.

A worked configuration

terraform version
terraform providers
terraform validate
terraform state list
TF_LOG=DEBUG TF_LOG_PATH=terraform-debug.log terraform plan

Keep incident notes that clearly separate symptoms (what you observed), hypotheses (what you think might be causing it), actions taken (what you actually tried), and outcomes (what happened as a result) - this separation is what lets someone else pick up your investigation partway through without repeating steps you've already ruled out, and it's what turns one incident into a reusable playbook for the next one.

Common mistakes in production

Goal: Debug logs can contain credentials and complete API request/response payloads. Enable TF_LOG only for the specific duration you're actively debugging, restrict who can read the resulting file, sanitize it before sharing with anyone, then delete it once the investigation is complete, per your team's incident-handling policy. Reinforce this lesson in the tf-state-drift-recovery Terraform lab. Open /labs/terraform and choose slug tf-state-drift-recovery; the lab runs real Terraform against the offline FakeCloud provider.