← All writing

Terraform state, without the tears

Most Terraform pain is not about HCL. It is about state — who wrote it, when, and what happens when two people touch the same stack at once. Here are the rules I hold teams to.

1. Never keep state on a laptop

Local state files are fine for a throwaway experiment. The moment more than one person (or one CI job) needs it, move it remote:

terraform {
  backend "s3" {
    bucket         = "acme-terraform-state"
    key            = "platform/prod/terraform.tfstate"
    region         = "eu-west-1"
    dynamodb_table = "terraform-locks"   # optimistic locking
    encrypt        = true
  }
}

The dynamodb_table is the part people skip and then regret. Without it, two concurrent applies can both read the same state and one silently overwrites the other.

2. Locking is a feature, not an afterthought

BackendNative locking?Notes
localNoSingle machine only — fine for experiments
s3 + DynamoDBYesThe default I recommend for AWS shops
gcsYesLocking is built in, no extra table needed
httpDependsOnly if your server implements it

If you are on GCP, this is the easy path — locking comes with the backend and there is nothing extra to provision.

3. Make drift visible before it bites

Drift is inevitable; surprise drift is what hurts. Run a plan in CI on every change and treat non-empty output as a signal:

terraform init -backend=false
terraform plan -out=tfplan
# fail the pipeline if there is anything to do
if terraform show tfplan | grep -qE '^\+|^~'; then
  echo "drift detected" >&2
  exit 1
fi

Rule of thumb: if a plan you did not expect shows up, someone (or something) changed the cloud behind your back. Find out who before it becomes an incident.

Get these three right and state stops being the scary part of Terraform — which is exactly where it should be.