Remote state, locking, and workspaces¶
Play it
Drift Detective: Keep config, state and reality in agreement through drift, imports and locks.
The game links back to this page from: Why remote state, Drift and locking, in practice.
Why remote state¶
The default local backend keeps terraform.tfstate on disk — fine for the
credential-free examples in this repo, but unworkable once more than one person or CI job
applies the same config: two concurrent applys can corrupt state or fight each other's
changes.
A remote backend stores state centrally and (for backends that support it) locks it during an operation:
terraform {
backend "s3" {
bucket = "my-tfstate"
key = "myapp/prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tfstate-lock" # locking, so concurrent applies queue instead of racing
encrypt = true
}
}
terraform init -migrate-state # move existing local state into the new backend
terraform force-unlock <lock-id> # only if a crashed run left a stale lock — verify first
Never edit the state file by hand; use terraform state mv / rm / import for
surgical changes.
Workspaces¶
Workspaces let one config manage multiple, isolated state files (e.g. dev/staging)
without duplicating .tf files:
terraform workspace list
terraform workspace new staging
terraform workspace select staging
terraform apply # applies against staging's own state
resource "aws_instance" "app" {
# differentiate per workspace via terraform.workspace
instance_type = terraform.workspace == "prod" ? "m5.large" : "t3.micro"
}
Workspaces share the same backend and variable set — for environments that need
genuinely different configuration (different accounts, different modules), a separate
directory per environment (each with its own backend config and .tfvars) is usually
clearer than workspaces.
Drift and locking, in practice¶
- Run
terraform planon a schedule (or before every apply) to catch drift — resources changed outside Terraform — before it surprises anapply. - A stuck lock almost always means a previous run was killed mid-apply; confirm no
applyis actually in flight beforeforce-unlock. - Keep state encrypted at rest (
encrypt = trueabove) — it contains resource attributes verbatim, including anything markedsensitivein a variable.