First, the question that unlocks everything: why does state exist at all? Couldn't Terraform just look at AWS and figure it out?
Early Terraform prototypes attempted exactly that, using cloud tags to mark "this resource is mine." It failed: not all resources support tags, and not all providers have them. So the config-to-real-world mapping is explicit, in state.
Three more reasons keep it there: state remembers dependencies (so it knows the
destroy order even after you delete the blocks from config), it acts as a performance
cache (refreshing every resource on every plan is too slow at scale — that's why
-refresh=false exists), and it's the sync point for teams (one shared
memory, locked during runs).
terraform apply
proposes to undo their change. Drift is never silently "accepted." To accept it, you must
update state (apply -refresh-only) and make the config agree.
Delete the resource block → next apply
deletes the real resource. The block is the claim of ownership.
terraform state rm (or a removed
block, TF ≥1.7) → Terraform stops managing it, real resource lives on. Inverse: import.
Six scenarios, each a real-world trap. Reason from the three pictures plus the config-wins rule.
terraform.tfstate — find the mapping
(real ID), the cached attributes, and the dependencies array.terraform_data input), then run terraform plan -refresh-only —
drift report, no infra changes proposed.terraform plan — watch it propose to revert the drift.terraform state rm on a resource, then plan — it wants to
create a duplicate. Re-adopt it with terraform import (or an import block).