Terraform remote state for multi-account AWS: complete setup
Local state is a trap. Two engineers run apply simultaneously and state diverges. Here's the complete remote state setup.
Architecture
S3 Bucket (management account)
project-alpha/prod/terraform.tfstate
project-alpha/staging/terraform.tfstate
DynamoDB Table: terraform-state-locks (LockID key)
Bootstrap (run once per management account)
resource "aws_s3_bucket" "state" {
bucket = "my-org-terraform-state-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_bucket_versioning" "state" {
bucket = aws_s3_bucket.state.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_server_side_encryption_configuration" "state" {
bucket = aws_s3_bucket.state.id
rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } }
}
resource "aws_s3_bucket_public_access_block" "state" {
bucket = aws_s3_bucket.state.id
block_public_acls = true; block_public_policy = true
ignore_public_acls = true; restrict_public_buckets = true
}
resource "aws_dynamodb_table" "locks" {
name = "terraform-state-locks"; billing_mode = "PAY_PER_REQUEST"; hash_key = "LockID"
attribute { name = "LockID"; type = "S" }
point_in_time_recovery { enabled = true }
}
Backend configuration per project
terraform {
backend "s3" {
bucket = "my-org-terraform-state-111111111111"
key = "project-alpha/prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
role_arn = "arn:aws:iam::111111111111:role/terraform-state-access"
}
}
Common pitfalls
-
Key collisions: Use
<project>/<environment>/terraform.tfstateconsistently -
Forgetting
encrypt = true: Bucket encryption alone doesn't protect in-transit operations - Lock table region mismatch: DynamoDB must be in the same region as S3
- No versioning: State corruption happens — versioning is your rollback
Step2Dev provisions a remote state backend automatically for every new project.
Top comments (0)