Terraform State Backend Design Prompt
Design Terraform state backend — S3+DynamoDB, GCS, Azure Blob, encryption, locking, versioning, cross-account access.
- Target user
- Platform engineers managing Terraform state at scale
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior platform engineer who has set up Terraform state backends across cloud providers — secure, locked, versioned, with cross-team access patterns. I will provide: - The cloud provider - Team / multi-env structure - Current state setup (local or remote) - Symptom (lost state, no locking, version drift) Your job: 1. **Backend choice**: - **`s3` + DynamoDB** — AWS; encryption + locking standard - **`gcs`** — GCP; locking via object metadata - **`azurerm`** — Azure Blob; locking via lease - **Terraform Cloud / Enterprise** — managed; RBAC, runs, policy - **`http`** — GitLab managed; locking built-in 2. **For S3 backend essentials**: - **Versioning ON** — recover from accidental writes - **Encryption** (SSE-S3 or SSE-KMS) - **Block public access** - **DynamoDB table** for state locking (PK: `LockID`) - **Server-side replication** for DR 3. **For state path organization**: - Per-env: `env/prod/network/terraform.tfstate` - Per-team: `teams/payments/...` - One state file per logical boundary - **Smaller states = faster plans, lower blast radius** 4. **For workspaces**: - Suffix on state path - Limited; prefer directory structure for true env separation 5. **For cross-account**: - Assume role in backend config - Or read-only state across accounts 6. **For locking semantics**: - DynamoDB lock: TTL, force-unlock for stuck - GCS: similar via metadata - Lock during plan AND apply 7. **For IAM design**: - Minimum: S3 list/get/put on path; DynamoDB get/put/delete - State contains secrets — encryption key control matters 8. **For migration**: - `terraform init -migrate-state` to move - Backup before - Coordinate downtime Mark DESTRUCTIVE: deleting state bucket (unrecoverable), DynamoDB table delete during apply, broad IAM on state. --- Cloud: [AWS / GCP / Azure / TFC] Team structure: [DESCRIBE] Current setup: [DESCRIBE] Symptom: [DESCRIBE]
Why this prompt works
State is Terraform’s most critical resource. This prompt walks setup.
How to use it
- Backend with locking from day 1.
- Versioning ON.
- Encryption at rest.
- One state per boundary.
Patterns
S3 + DynamoDB
terraform {
backend "s3" {
bucket = "myorg-terraform-state-prod"
key = "network/vpc/terraform.tfstate"
region = "us-east-1"
encrypt = true
kms_key_id = "arn:aws:kms:us-east-1:123:key/abc"
dynamodb_table = "terraform-state-locks"
# Cross-account assume role
role_arn = "arn:aws:iam::987654321:role/TerraformBackend"
}
}
Backend setup (one-time, bootstrap)
# bootstrap/main.tf (run once, manually)
resource "aws_s3_bucket" "state" {
bucket = "myorg-terraform-state-prod"
}
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 = "aws:kms"
kms_master_key_id = aws_kms_key.state.arn
}
}
}
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"
}
server_side_encryption { enabled = true }
}
GCS backend
terraform {
backend "gcs" {
bucket = "myorg-tfstate"
prefix = "env/prod/network"
# Optional: encryption via CMEK
encryption_key = "base64-encoded-key"
}
}
State path strategy
myorg-terraform-state-prod/
├── network/
│ ├── vpc/terraform.tfstate
│ └── transit-gateway/terraform.tfstate
├── platform/
│ ├── eks-cluster/terraform.tfstate
│ └── rds-shared/terraform.tfstate
└── apps/
├── payments/terraform.tfstate
└── web/terraform.tfstate
Smaller states = faster plans, lower blast radius.
Common findings this catches
- No versioning → enable; backup history.
- No locking → two concurrent applies corrupt state.
- Plain text state → enable encryption.
- Monolithic state → split by team / system.
- Workspaces masquerading as envs → switch to dirs.
- State exposed publicly → block public access.
- Cross-account IAM too broad → scope.
When to escalate
- State migration at scale — coordinated.
- Multi-region DR — strategic.
- Terraform Cloud adoption — eval.
Related prompts
-
Terraform Multi-Environment Design Prompt
Design Terraform for multiple environments — dev/staging/prod separation, tfvars patterns, account boundaries, promotion workflow.
-
Terraform State Locking Debug Prompt
Diagnose Terraform state lock issues — DynamoDB / GCS / Azure lease locks, stuck locks, force-unlock, multi-runner contention.
-
Terraform State Surgery & Import Prompt
Perform Terraform state operations — terraform state mv/rm/import, replace, large-scale imports via import block.