Infrastructure as code Required
Keep state remote with locking and encryption
Keep the state file in a remote backend that locks, encrypts and keeps old versions. Only the deploy roles may read it. Never keep state in git or on a laptop.
How to do it
The state file holds every attribute of every resource in plain text, sometimes with passwords. Store it in a bucket with versioning, encryption and public access blocked. In the S3 backend, set use_lockfile = true. It needs Terraform 1.11 or OpenTofu 1.10 or later, and the older DynamoDB lock table is deprecated. OpenTofu can also encrypt state and plan files on the client with its encryption block and a key from a key service. Set enforced = true, so an unencrypted file is refused. Pulumi encrypts secret values in state with a secrets provider.
Example
terraform {
backend "s3" {
bucket = "example-shop-state"
key = "production/network.tfstate"
region = "eu-north-1"
encrypt = true
use_lockfile = true
}
}
# OpenTofu only: state and plan files are encrypted before they leave the runner
terraform {
encryption {
key_provider "aws_kms" "state" {
kms_key_id = "alias/example-shop-state"
region = "eu-north-1"
key_spec = "AES_256"
}
method "aes_gcm" "state" {
keys = key_provider.aws_kms.state
}
state {
method = method.aes_gcm.state
enforced = true
}
plan {
method = method.aes_gcm.state
enforced = true
}
}
}
aws s3api get-bucket-versioning --bucket example-shop-state # "Status": "Enabled"
aws s3api get-public-access-block --bucket example-shop-state # all four settings true
How to check
git ls-files '*.tfstate*' prints nothing. Start two plans on the same state at once, and the second stops with a lock error. The bucket reports versioning Enabled and public access blocked. Its policy grants read access to the deploy roles only.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers infrastructure playbook: https://agentwranglers.se/playbook/infrastructure/infrastructure-as-code/remote-state.