Terraform Workspaces: Manage Dev, Staging, and Production Environments
Learn Terraform workspaces to manage dev, staging, and production with the same code. Covers workspace commands, terraform.workspace variable
DevOps
Configure Terraform remote state with AWS S3 and DynamoDB locking. Complete setup with encryption, versioning, IAM permissions, and team access patterns.
Store Terraform state in S3 with DynamoDB locking for team collaboration. Create an encrypted, versioned S3 bucket and a DynamoDB table, then configure the backend "s3" block. This prevents state corruption from concurrent runs.
| Feature | Local State | Remote State (S3) |
|---|---|---|
| Team collaboration | ❌ One person only | ✅ Shared access |
| State locking | ❌ No protection | ✅ DynamoDB locking |
| Encryption | ❌ Plaintext on disk | ✅ AES-256 at rest |
| Versioning | ❌ No history | ✅ S3 versioning |
| Backup | ❌ Manual | ✅ Automatic |
| CI/CD support | ❌ Needs file sharing | ✅ Native access |
Create these resources before configuring the backend (use a separate Terraform config or create manually):
# backend-setup/main.tf — run this first
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "terraform_state" {
bucket = "mycompany-terraform-state"
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-state-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}# main.tf — your actual project
terraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "prod/networking/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-locks"
encrypt = true
}
}# Initialize with the new backend
terraform init
# If migrating from local state:
terraform init -migrate-stateOrganize state files by environment and component:
s3://mycompany-terraform-state/
├── prod/
│ ├── networking/terraform.tfstate
│ ├── eks/terraform.tfstate
│ ├── rds/terraform.tfstate
│ └── app/terraform.tfstate
├── staging/
│ ├── networking/terraform.tfstate
│ └── app/terraform.tfstate
└── shared/
├── iam/terraform.tfstate
└── dns/terraform.tfstate# Read another project's state outputs
data "terraform_remote_state" "networking" {
backend = "s3"
config = {
bucket = "mycompany-terraform-state"
key = "prod/networking/terraform.tfstate"
region = "us-east-1"
}
}
resource "aws_instance" "web" {
subnet_id = data.terraform_remote_state.networking.outputs.private_subnet_id
}{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::mycompany-terraform-state/*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::mycompany-terraform-state"
},
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem"],
"Resource": "arn:aws:dynamodb:us-east-1:*:table/terraform-state-locks"
}
]
}If a lock is stuck (crashed apply, lost connection):
# Show the lock info
terraform force-unlock LOCK_ID
# Get the lock ID from the error message:
# "Error locking state: Error acquiring the state lock"
# "Lock Info: ID: xxxx-xxxx-xxxx"| Issue | Fix |
|---|---|
| "Error acquiring state lock" | Another apply is running, or lock is stuck — force-unlock |
| "Access Denied" | Check IAM permissions for S3 and DynamoDB |
| "No such bucket" | Create the S3 bucket first |
| "Backend configuration changed" | Run terraform init -reconfigure |
prevent_destroy on the S3 bucket — accidental deletion loses all stateS3 + DynamoDB is the gold standard for Terraform remote state on AWS. It gives you encryption, versioning, locking, and team collaboration out of the box. Set it up once, organize state files by environment and component, and never worry about state corruption from concurrent runs again.
Learn Terraform workspaces to manage dev, staging, and production with the same code. Covers workspace commands, terraform.workspace variable
Write reusable Terraform modules with proper structure, inputs, outputs, and versioning. Covers local modules, registry modules, module composition
Protect your applications with AWS WAF rules managed by Terraform — rate limiting, IP blocking, and SQL injection prevention.
Manage secrets securely with AWS Secrets Manager and Terraform — rotation, replication, and application integration. Step-by-step guide with code examples an...