Skip to main content

State Locking

State Lock ป้องกัน 2 คนรัน apply พร้อมกัน — ไม่ให้ state corrupt

ปัญหา: Concurrent Apply

ลองคิดดู:

  1. Alice รัน terraform apply — เริ่มสร้าง resource A
  2. Bob รัน terraform apply พร้อมกัน — เริ่มสร้าง resource B
  3. ทั้งคู่อ่าน state เก่า → ทำ change → write state กลับ
  4. คน write ทีหลัง → ทับ ของคนก่อน → state corrupt

→ Lock ป้องกันสถานการณ์นี้

Lock ทำงานยังไง?

Backend ที่รองรับ Locking

BackendLocking
s3✅ DynamoDB (ต้อง config)
gcs✅ built-in
azurerm✅ built-in
remote (Terraform Cloud)✅ built-in
consul✅ built-in
local❌ no lock

S3 + DynamoDB Setup

1. สร้าง DynamoDB Table

resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"

attribute {
name = "LockID"
type = "S"
}
}

ต้องมี:

  • Hash key: LockID (String)
  • ใช้ on-demand billing (cheap)

2. Reference ใน Backend

terraform {
backend "s3" {
bucket = "my-tfstate"
key = "prod/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-locks" # ← lock here
encrypt = true
}
}

Lock ใน Action

$ terraform apply
Acquiring state lock. This may take a few moments...

# ระหว่างนี้คนอื่นรัน apply:
$ terraform apply
Acquiring state lock. This may take a few moments...


│ Error: Error acquiring the state lock

│ Error message: ConditionalCheckFailedException: The conditional request failed
│ Lock Info:
│ ID: abc123
│ Path: my-tfstate/prod/terraform.tfstate
│ Operation: OperationTypeApply
│ Who: alice@laptop
│ Version: 1.9.8
│ Created: 2026-05-07 10:00:00 +0700
│ Info:

→ คนที่ 2 ต้องรอ คนที่ 1 เสร็จก่อน

Force Unlock

ถ้า lock ค้าง (เช่น คน 1 process ถูก kill):

terraform force-unlock <LOCK_ID>

LOCK_ID = ดูจาก error message

ใช้ force-unlock ด้วยความระวัง
  • ❌ อย่าใช้ถ้าไม่แน่ใจว่า apply อื่นเสร็จแล้ว
  • ✅ ใช้เมื่อเครื่องที่รัน apply ตาย/disconnect
  • ✅ Verify ว่าไม่มี process ใครยังรัน

DynamoDB Schema

Lock entry ใน DynamoDB หน้าตา:

{
"LockID": "my-tfstate/prod/terraform.tfstate-md5",
"Info": "{\"ID\":\"abc123\",\"Operation\":\"OperationTypeApply\",\"Who\":\"alice@laptop\"}"
}

ถ้าอยาก unlock manually:

aws dynamodb delete-item \
--table-name terraform-locks \
--key '{"LockID":{"S":"my-tfstate/prod/terraform.tfstate-md5"}}'

Lock Timeout

Default timeout 0 = wait indefinitely

ตั้ง timeout:

terraform apply -lock-timeout=10m

→ รอ lock 10 นาที แล้ว fail

Skip Lock (อันตราย!)

terraform apply -lock=false
-lock=false
  • ❌ ห้ามใช้ใน production
  • ✅ ใช้ได้แค่ตอน lock backend พังจริง (rare!)

CI/CD with Locking

ใน CI ตั้ง timeout เพื่อไม่ให้ pipeline ค้าง:

- name: Apply
run: terraform apply -lock-timeout=5m -auto-approve

ถ้า timeout = pipeline fail ดีกว่าค้าง

State Locking ของ Backend อื่น

GCS

terraform {
backend "gcs" {
bucket = "my-tfstate"
prefix = "prod"
}
}

GCS lock ผ่าน Cloud Storage object generation — ไม่ต้อง config เพิ่ม

Azure Blob

terraform {
backend "azurerm" {
resource_group_name = "tfstate"
storage_account_name = "tfstate"
container_name = "tfstate"
key = "prod.tfstate"
}
}

Lease-based locking — automatic

Terraform Cloud

terraform {
cloud {
organization = "my-org"
workspaces { name = "prod" }
}
}

Built-in locking — ไม่ต้อง config

ตรวจสอบ Lock Status

S3 + DynamoDB

aws dynamodb scan --table-name terraform-locks

Terraform Cloud

ดูใน UI: workspace → States → Locked

ตัวอย่าง: รวม S3 + DynamoDB Bootstrap

bootstrap/main.tf
resource "aws_s3_bucket" "tfstate" {
bucket = "my-company-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_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
}

server_side_encryption {
enabled = true
}

tags = {
Name = "terraform-locks"
}
}

output "backend_config" {
value = {
bucket = aws_s3_bucket.tfstate.id
region = aws_s3_bucket.tfstate.region
dynamodb_table = aws_dynamodb_table.tfstate_lock.id
}
}

Best Practices

✅ DO:
- ใช้ locking ใน production เสมอ
- DynamoDB on-demand billing (cheap, no provisioning)
- ใช้ -lock-timeout ใน CI
- Monitor lock duration (ถ้านานเกิน = บั๊ก)

❌ DON'T:
- ห้ามใช้ -lock=false ใน prod
- ห้าม share lock table หลาย project (ใช้ table เดียวก็ได้แต่อย่า conflict)
- ห้าม force-unlock ถ้าไม่แน่ใจ

สรุป

  • State lock ป้องกัน concurrent apply
  • S3 ใช้ DynamoDB สำหรับ lock, GCS/Azure/TFC มี built-in
  • Lock ID เก็บใน lock table — มีข้อมูล who/when
  • terraform force-unlock <ID> แก้ lock ค้าง (ระวัง)
  • -lock-timeout=10m ใน CI

ต่อไป → Import Existing Resources