Terraform Tutorial — State Best Practices
Following state best practices prevents data loss, security breaches, and team coordination issues.
1. Always Use Remote State
Local state works for learning. For teams and production, use a remote backend:
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "projects/web-app/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
| Feature | Local | Remote (S3) |
|---|---|---|
| Team access | ❌ | ✅ |
| Locking | File-based | DynamoDB |
| Backup | Manual | S3 versioning |
| Encryption | ❌ | ✅ SSE-S3/KMS |
| CI/CD | Difficult | Easy |
2. Enable Encryption
State contains secrets. Always encrypt at rest:
backend "s3" {
encrypt = true # SSE-S3 encryption
}
For stricter requirements, use KMS:
backend "s3" {
encrypt = true
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abc-def-123"
}
3. Enable Versioning on the State Bucket
S3 versioning lets you recover previous state versions:
resource "aws_s3_bucket_versioning" "state" {
bucket = aws_s3_bucket.state.id
versioning_configuration {
status = "Enabled"
}
}
4. Never Edit State Manually
Use Terraform commands instead:
| Need | Wrong Approach | Right Approach |
|---|---|---|
| Rename resource | Edit JSON | terraform state mv |
| Remove resource | Delete from JSON | terraform state rm |
| Add existing resource | Write JSON | terraform import |
| View resource | Read JSON | terraform state show |
5. Add State Files to .gitignore
# .gitignore
*.tfstate
*.tfstate.*
.terraform/
*.tfvars # if it contains secrets
6. Separate State per Environment
Don’t put dev and prod in the same state file:
projects/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ └── backend.tf ← key = "dev/terraform.tfstate"
│ ├── staging/
│ │ ├── main.tf
│ │ └── backend.tf ← key = "staging/terraform.tfstate"
│ └── prod/
│ ├── main.tf
│ └── backend.tf ← key = "prod/terraform.tfstate"
└── modules/
└── vpc/
What’s Next?
Practice what you’ve learned about state with the exercises.