AWS


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.html

1. 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_lockfile
scope:
    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 pages

2. 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-1
rules:
    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 keys
why 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 name

3. 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.tfstate
key 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 object

5. 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 = true

Initialize with:

terraform init -backend-config=backend.hcl
why 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.hcl

6. 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 = true

uat#

bucket       = "acme-tfstate-123456789012-ap-east-1"
key          = "order/uat/network/terraform.tfstate"
region       = "ap-east-1"
encrypt      = true
use_lockfile = true

prod#

bucket       = "acme-tfstate-123456789012-ap-east-1"
key          = "order/prod/network/terraform.tfstate"
region       = "ap-east-1"
encrypt      = true
use_lockfile = true

7. 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 object
notes:
    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 key

8. 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-state

Verify:

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