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
| Backend | Native locking? | Notes |
|---|---|---|
local | No | Single machine only — fine for experiments |
s3 + DynamoDB | Yes | The default I recommend for AWS shops |
gcs | Yes | Locking is built in, no extra table needed |
http | Depends | Only 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
planyou 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.