While working on my cloud-design project, the file that surprised me most wasn’t a .tf file. It was terraform.tfstate.
What the state file is
Terraform needs to remember which real resources belong to which blocks in your code. That mapping lives in the state file: IDs, attributes and dependencies of everything it manages. On every plan, Terraform compares three things: your code, the state, and what actually exists in the cloud.
Why it’s sensitive
State stores resource attributes in plain text. That can include database passwords, private keys and connection strings, even when you marked a variable as sensitive (that only hides it from the CLI output, not from the state).
Treat the state file like a secret, because it often contains them.
Rule 1: never commit it
# .gitignore
*.tfstate
*.tfstate.*
.terraform/
*.tfvars
Rule 2: use a remote backend
Local state breaks as soon as two people (or a CI pipeline and you) work on the same infrastructure. Store it remotely, for example in an S3 bucket with encryption and versioning enabled:
terraform {
backend "s3" {
bucket = "my-tf-state"
key = "cloud-design/terraform.tfstate"
region = "eu-west-3"
encrypt = true
use_lockfile = true
}
}
Versioning on the bucket lets you recover if the state gets corrupted. use_lockfile (Terraform 1.11 and later) enables S3-native locking, so two runs can’t write the state at the same time. Older setups use a DynamoDB table for the same job.
Rule 3: lock down who can read it
Only the people and pipelines that run Terraform should be able to read the state bucket. Apply least privilege with IAM, block public access, and turn on access logging.
Rule 4: don’t edit it by hand
If you need to move or remove something from state, use the commands made for it: terraform state mv, terraform state rm, or moved and removed blocks in code. Editing the JSON directly is a good way to lose track of real resources.
Getting this right early saves a lot of pain later, and it’s one of the easiest DevSecOps wins in infrastructure as code.