State Management
State is Terraform's mapping from configuration to real-world resources. Master terraform.tfstate, remote state, locking, and the terraform_remote_state pattern for team infrastructure.
What is this?
Terraform stores a record of every resource it manages in terraform.tfstate. The state maps configuration addresses to real-world resource IDs, stores attribute values (including sensitive ones), and records dependencies. Every plan diffs the config against state to decide what to create, change or destroy.
Why do you need it?
- It maps logical config names to real resource IDs, which is essential for resources whose IDs are randomly generated.
- It records attribute values so Terraform doesn't have to re-query every API on each run.
- It captures dependencies, enabling correct creation and destruction ordering.
- Remote state with locking lets a whole team work safely on the same infrastructure.
- terraform_remote_state lets one workspace read another's outputs without duplicating resources.
Mental model
A home inventory ledger
Terraform state
The ledger records exactly what exists in the house — the couch in the living room, the lamp on the desk. You consult it before buying anything so you don't buy a duplicate. Terraform consults state before planning so it doesn't recreate what already exists.
How it works
- During apply, Terraform creates or updates resources and writes their IDs and attributes into the state file.
- On the next plan, Terraform reads state, refreshes resource data from the provider API, and diffs config against state.
- Each state entry holds the resource's address, ID, attributes and metadata such as the provider version.
- Remote backends store state in S3, Azure Blob or Terraform Cloud and add locking so concurrent runs can't corrupt it.
- Other workspaces read it via data "terraform_remote_state" to consume outputs like VPC IDs.
How state drives the plan/apply loop.
Key commands
terraform state listList every resource address tracked in state.
terraform state show aws_instance.webShow the full recorded attributes of one resource.
terraform state mv aws_instance.old aws_instance.newRename a resource address when refactoring config.
terraform state rm aws_instance.webRemove a resource from state without destroying it.
terraform state pullDownload the current state to stdout as JSON.
terraform state pushUpload a local state file to the backend.
terraform force-unlock <LOCK_ID>Release a stale state lock after verifying no run is active.
Examples
Remote state for a VPC, read by an app workspace
terraform { backend "s3" { bucket = "my-org-terraform-state" key = "prod/network/terraform.tfstate" region = "us-east-1" dynamodb_table = "terraform-locks" encrypt = true }} data "terraform_remote_state" "network" { backend = "s3" config = { bucket = "my-org-terraform-state" key = "prod/network/terraform.tfstate" region = "us-east-1" }} output "vpc_id" { value = data.terraform_remote_state.network.outputs.vpc_id}The network workspace stores its state in S3 with DynamoDB locking. The app workspace reads it with terraform_remote_state and uses the published outputs, so the VPC is created once and reused everywhere.
In production
- State lives in a remote backend (S3 + DynamoDB, Azure Blob, GCS) so teams share one source of truth.
- Separate state per environment keeps dev problems from affecting prod.
- terraform_remote_state shares VPC and networking outputs across application workspaces.
- state mv and state rm support safe refactors like renaming resources or adopting existing ones.
Best practices
- Never commit state; always use a remote backend once more than one person works on the infra.
- Enable locking (DynamoDB table for S3, blob leases for Azure).
- Use one state file per environment and per service boundary.
- Enable server-side encryption on the state bucket and enable versioning for recovery.
- Review state changes as part of plan output before applying.
Common mistakes
- Committing terraform.tfstate to git, leaking secrets into history.
- Sharing a single state file across dev, staging and prod.
- Hand-editing or deleting state instead of using terraform state commands.
- Skipping encryption or versioning on the remote state bucket.
Common issues
Error acquiring the state lockAnother plan/apply holds the lock, or a crashed run left it behind.
Check for an active run; if stale, run terraform force-unlock <LOCK_ID>.
Backend configuration changedThe backend block was edited after the last terraform init.
Re-run terraform init -reconfigure to reload the backend configuration.
State file locked: The state file is currently locked by another processConcurrent runs from CI or another engineer are using the same state.
Wait for the run to finish or release a stale lock after verifying nothing is active.
Debugging tips
- terraform state list compared against your config reveals resources that were removed from config but still exist.
- terraform state pull gives you raw JSON to inspect suspicious values.
- terraform show displays the last known state in readable form.
- Use terraform plan -refresh-only to reconcile drifted state without changing resources.
Interview checkpoint
Hands-on lab
- Inspect state with list and show
- Refactor a resource using state mv
- Untrack a resource with state rm and re-import it
- Understand the danger of a stale or deleted state
Master Terraform State
30 min · intermediate
Quick quiz
Which command removes a resource from state without deleting the real resource?
The state file may contain plaintext secrets such as database passwords.
Which of these prevents two engineers from corrupting the same remote state?
Cheat sheet
terraform state listList managed resourcesterraform state show ADDRShow one resource's attributesterraform state mv A BMove/rename an addressterraform state rm ADDRUntrack without destroyingterraform state pullDownload state as JSONterraform force-unlock IDClear a stale lock