Links#
https://developer.hashicorp.com/terraform/language/backend/s3
https://developer.hashicorp.com/terraform/cli/commands/init
https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html1. Important Points#
Terraform AWS S3 backend:
S3 bucket:
stores terraform.tfstate
state key:
path inside the bucket, for example order/prod/network/terraform.tfstate
use_lockfile:
enables S3-native state locking
creates a .tflock object while Terraform is running
deletes the .tflock object when the run finishes
correct option is use_lockfile, not user_lockfilescope:
this page only covers S3 naming convention and backend config
AWS IAM policy / bucket policy / backend bootstrap resources are separate topics
GCP and Azure should have their own backend pages2. S3 Bucket Naming#
S3 bucket name must be globally unique and DNS-compatible. Add AWS account ID and region to avoid collision.
recommended pattern:
<org>-tfstate-<aws_account_id>-<region>
example:
acme-tfstate-123456789012-ap-east-1rules:
use lowercase letters, numbers, and hyphen
include AWS account ID
include AWS region
do not include secret/project codename
do not include environment if one bucket stores many environment state keyswhy not env in bucket name:
one bucket can store:
order/dev/network/terraform.tfstate
order/uat/network/terraform.tfstate
order/prod/network/terraform.tfstate
environment belongs in state key
account/region belongs in bucket name3. State Key Naming#
State key is the object path inside the S3 bucket.
recommended pattern:
<system>/<env>/<component>/terraform.tfstate
examples:
order/dev/network/terraform.tfstate
order/prod/ecs/terraform.tfstate
order/prod/rds/terraform.tfstate
platform/prod/landing-zone/terraform.tfstatekey rules:
one key = one Terraform root module state
split by ownership boundary and blast radius
keep env in the key
keep component in the key
do not put every AWS resource into one giant state file| Root Module | State Key |
|---|---|
| network | order/prod/network/terraform.tfstate |
| ECS service | order/prod/ecs/terraform.tfstate |
| RDS | order/prod/rds/terraform.tfstate |
| platform baseline | platform/prod/landing-zone/terraform.tfstate |
4. backend.tf#
Use this when the backend config is fixed in the root module.
terraform {
backend "s3" {
bucket = "acme-tfstate-123456789012-ap-east-1"
key = "order/prod/network/terraform.tfstate"
region = "ap-east-1"
encrypt = true
use_lockfile = true
}
}fields:
bucket:
S3 bucket that stores state
key:
object path for this root module state
region:
S3 bucket region
encrypt:
asks S3 backend to use server-side encryption
use_lockfile:
enables S3-native locking with a .tflock object5. backend.hcl#
Use backend.hcl when bucket/key/region differ by environment or repository.
bucket = "acme-tfstate-123456789012-ap-east-1"
key = "order/prod/network/terraform.tfstate"
region = "ap-east-1"
encrypt = true
use_lockfile = trueInitialize with:
terraform init -backend-config=backend.hclwhy backend.hcl:
backend block does not support normal Terraform variables
backend.hcl keeps env-specific backend values outside reusable module code
do not put AWS credentials in backend.hcl6. Per Environment Examples#
dev#
bucket = "acme-tfstate-123456789012-ap-east-1"
key = "order/dev/network/terraform.tfstate"
region = "ap-east-1"
encrypt = true
use_lockfile = trueuat#
bucket = "acme-tfstate-123456789012-ap-east-1"
key = "order/uat/network/terraform.tfstate"
region = "ap-east-1"
encrypt = true
use_lockfile = trueprod#
bucket = "acme-tfstate-123456789012-ap-east-1"
key = "order/prod/network/terraform.tfstate"
region = "ap-east-1"
encrypt = true
use_lockfile = true7. use_lockfile#
terraform plan/apply starts:
create lock object:
order/prod/network/terraform.tfstate.tflock
another terraform process starts:
sees the lock object
fails/waits instead of writing state at the same time
terraform plan/apply finishes:
deletes the .tflock objectnotes:
use_lockfile is the Terraform backend option
user_lockfile is not a Terraform backend option
lockfile is stored in the same S3 bucket as state
lockfile name is based on the state key8. Migration From Local State#
If a project already has local terraform.tfstate, migrate it after the S3 backend exists.
terraform init -backend-config=backend.hcl -migrate-stateVerify:
terraform state list
aws s3api list-object-versions \
--bucket acme-tfstate-123456789012-ap-east-1 \
--prefix order/prod/network/rollback:
keep a local backup of terraform.tfstate before migration
do not delete the local backup until remote state is verified
if migration fails, fix backend config and rerun terraform init -migrate-state