Skip to main content

State Versioning

เก็บ history ของ state file — เพื่อ recovery ถ้าเสีย, audit ใครเปลี่ยนอะไร

ทำไมต้อง Version?

State file = source of truth ของ infrastructure

  • ถ้าเสีย = พังหมด
  • ถ้าเปลี่ยนผิด = recover ยาก

Versioning = backup automatic

S3 Versioning

ตั้งค่าใน bootstrap:

resource "aws_s3_bucket" "tfstate" {
bucket = "my-tfstate"
}

resource "aws_s3_bucket_versioning" "tfstate" {
bucket = aws_s3_bucket.tfstate.id

versioning_configuration {
status = "Enabled" # ⭐
}
}

ทุกครั้งที่ Terraform write state → S3 เก็บ version ใหม่ + เก็บเก่าไว้

List Versions

aws s3api list-object-versions \
--bucket my-tfstate \
--prefix prod/terraform.tfstate
{
"Versions": [
{
"Key": "prod/terraform.tfstate",
"VersionId": "abc123",
"LastModified": "2026-05-07T10:30:00Z",
"IsLatest": true
},
{
"Key": "prod/terraform.tfstate",
"VersionId": "def456",
"LastModified": "2026-05-07T09:15:00Z",
"IsLatest": false
}
]
}

Restore Version

# Download version เก่า
aws s3api get-object \
--bucket my-tfstate \
--key prod/terraform.tfstate \
--version-id def456 \
state-old.json

# Push กลับ (ระวัง!)
terraform state push state-old.json
ระวังการ Restore

Restore state เก่า = drift กับ reality ทำเฉพาะตอน:

  • State เพิ่ง corrupt (เพิ่งกี่นาที)
  • รู้แน่ว่า cloud reality ยังไม่เปลี่ยน

Lifecycle Policy

ป้องกัน S3 cost จาก version เยอะ:

resource "aws_s3_bucket_lifecycle_configuration" "tfstate" {
bucket = aws_s3_bucket.tfstate.id

rule {
id = "expire-old-versions"
status = "Enabled"

filter {}

noncurrent_version_expiration {
noncurrent_days = 90 # ลบ version เก่ากว่า 90 วัน
}

noncurrent_version_transition {
noncurrent_days = 30
storage_class = "STANDARD_IA" # ย้ายไป IA หลัง 30 วัน
}
}
}

Backup ทุก Apply

Terraform local มี .tfstate.backup:

project/
├── terraform.tfstate # current
└── terraform.tfstate.backup # before last apply

ถ้าอยากกู้ครั้งล่าสุด:

mv terraform.tfstate terraform.tfstate.bad
mv terraform.tfstate.backup terraform.tfstate

(แค่ครั้งล่าสุด — ใช้ remote versioning ดีกว่า)

Terraform Cloud Versioning

Terraform Cloud / Enterprise — มี state versioning ในตัว + UI

  • ดู state ทุก version ผ่าน web UI
  • Compare diff ระหว่าง version
  • Restore in 1 click

Audit Trail

ใช้ S3 versioning + CloudTrail:

aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=my-tfstate

→ เห็น:

  • ใคร PUT state file
  • ตอนไหน
  • จาก IP ไหน

State Snapshot ก่อน Risky Operation

ก่อนทำการ risky (เช่น state mv, state rm):

# Manual backup
terraform state pull > backup-$(date +%Y%m%d-%H%M%S).tfstate

# ถ้าเกิดอะไรผิด → restore
terraform state push backup-20260507-143000.tfstate

Version State + Lock Together

bootstrap.tf
# S3 with versioning
resource "aws_s3_bucket" "tfstate" {
bucket = "my-tfstate"
}

resource "aws_s3_bucket_versioning" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_server_side_encryption_configuration" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_s3_bucket_lifecycle_configuration" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
rule {
id = "manage-old-versions"
status = "Enabled"
filter {}
noncurrent_version_expiration {
noncurrent_days = 90
}
}
}

# DynamoDB for lock + PITR
resource "aws_dynamodb_table" "tfstate_lock" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
point_in_time_recovery {
enabled = true # ⭐
}
}

DynamoDB Lock Table PITR

Point-in-Time Recovery:

point_in_time_recovery {
enabled = true
}

→ Lock table backup automatic 35 วัน

Cross-Region Replication

สำหรับ DR:

resource "aws_s3_bucket_replication_configuration" "tfstate" {
role = aws_iam_role.replication.arn
bucket = aws_s3_bucket.tfstate.id

rule {
id = "replicate-to-dr"
status = "Enabled"

destination {
bucket = aws_s3_bucket.tfstate_dr.arn # bucket ใน region อื่น
storage_class = "STANDARD_IA"
}
}
}

→ DR region มี copy ของ state ตลอดเวลา

Best Practices

✅ DO:
- Enable S3 versioning ตั้งแต่ bootstrap
- Lifecycle policy เพื่อ cost control
- Manual backup ก่อน state mv/rm
- DynamoDB PITR + bucket replication ใน prod
- CloudTrail logging

❌ DON'T:
- ห้าม disable versioning
- ห้าม delete S3 bucket ที่มี state (ใช้ MFA delete protection)
- ห้าม push state เก่าโดยไม่ verify reality

MFA Delete (Extra Protection)

aws s3api put-bucket-versioning \
--bucket my-tfstate \
--versioning-configuration Status=Enabled,MFADelete=Enabled \
--mfa "arn:aws:iam::123456789012:mfa/user 123456"

→ ลบ version ต้องใช้ MFA — ป้องกัน accidental deletion

สรุป

  • Versioning = backup automatic ทุกครั้งที่ write state
  • S3 ใช้ versioning + lifecycle policy
  • DynamoDB ใช้ PITR สำหรับ lock table
  • Restore ผ่าน aws s3api get-object + terraform state push
  • Cross-region replication สำหรับ DR
  • MFA Delete ป้องกัน accidental deletion

ต่อไป → Sensitive Data in State