Resource Behavior
Terraform คิดยังไงเวลา plan และ apply — ทำความเข้าใจว่ามันตัดสินใจ create / update / destroy / replace แบบไหน
4 Actions ของ Terraform
ทุกครั้งที่ terraform plan Terraform จะตัดสินว่าแต่ละ resource ต้องทำอะไร:
| Symbol | Action | ความหมาย |
|---|---|---|
+ | Create | สร้างใหม่ (ยังไม่มีใน state) |
~ | Update in-place | แก้ไขแบบไม่สร้างใหม่ |
-/+ | Replace | ลบของเก่า + สร้างใหม่ |
- | Destroy | ลบทิ้ง (ไม่มีใน config แล้ว) |
| (ไม่แสดง) | No-op | ไม่มีการเปลี่ยนแปลง |
ตัวอย่าง Plan Output
Terraform will perform the following actions:
# aws_instance.web will be updated in-place
~ resource "aws_instance" "web" {
id = "i-1234567890abcdef0"
~ tags = {
~ "Name" = "web-old" -> "web-new"
}
}
# aws_s3_bucket.data will be created
+ resource "aws_s3_bucket" "data" {
+ bucket = "my-data"
+ id = (known after apply)
}
# aws_security_group.old will be destroyed
- resource "aws_security_group" "old" {
- id = "sg-12345" -> null
}
Plan: 1 to add, 1 to change, 1 to destroy.
Update vs Replace
ขึ้นอยู่กับ argument ที่แก้:
- บาง argument = แก้ใน-place ได้ → update
- บาง argument = ต้องสร้างใหม่ → replace
ตัวอย่าง: tags = update in-place
# ก่อน
tags = { Name = "web-v1" }
# หลัง
tags = { Name = "web-v2" }
→ AWS อนุญาตให้เปลี่ยน tag โดยไม่ต้องสร้างใหม่ → ~
ตัวอย่าง: instance_type = replace
# ก่อน
instance_type = "t2.micro"
# หลัง
instance_type = "t2.large"
→ AWS ไม่ให้เปลี่ยน instance type ของ instance ที่ running → ต้องสร้างใหม่ → -/+
ตอน plan ดูทุกครั้งว่ามี -/+ ไหม — ถ้ามีอาจหมายถึง downtime สำหรับ resource นั้น
ดูว่า argument ไหนทำให้ replace → ใน plan output จะมี # forces replacement กำกับ:
~ instance_type = "t2.micro" -> "t2.large" # forces replacement
Resource Lifecycle (CRUD)
State as Source of Truth
Terraform เปรียบเทียบ:
Config (.tf) ↔ State (.tfstate) ↔ Reality (cloud)
- Config = State + State = Reality → ✅ no-op
- Config ≠ State → plan แสดงการเปลี่ยนแปลง
- State ≠ Reality (มีคนแก้ในcloud) → drift detected!
Drift Detection
terraform plan -refresh-only
→ ดูว่า reality เปลี่ยนไปจาก state มั้ย (มีคนแก้ใน console)
ตัวอย่าง: Common Behaviors
Argument ที่ "force new"
resource "aws_instance" "web" {
ami = "ami-12345" # ← เปลี่ยน = replace
instance_type = "t2.micro" # ← เปลี่ยน = replace
subnet_id = "subnet-abc" # ← เปลี่ยน = replace
key_name = "my-key" # ← เปลี่ยน = replace
}
Argument ที่ update in-place
resource "aws_instance" "web" {
tags = {...} # ← in-place
associate_public_ip = true # ← in-place (บางกรณี)
monitoring = true # ← in-place
root_block_device { ... } # ← in-place (บาง field)
}
ดูว่า argument ไหนทำอะไร → อ่าน documentation ของ resource ใน Terraform Registry
Manual Control: -replace
Force replace resource เฉพาะตัว:
terraform apply -replace="aws_instance.web"
ใช้เวลา:
- Resource หาย state ไม่ตรงกับ reality
- ต้องการ "re-create" เพื่อ apply config ใหม่ทั้งหมด
(เดิมเรียก terraform taint — deprecated ใน Terraform 0.15+)
Idempotency
Terraform เป็น idempotent:
- Apply ครั้งแรก → สร้าง resource
- Apply อีกครั้ง (ไม่แก้ config) → no-op
$ terraform apply
# (สร้าง resource)
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
$ terraform apply
# (ไม่มี diff)
No changes. Your infrastructure matches the configuration.
เช็คว่า Apply จะทำอะไร — ก่อน Apply!
# Plan → save ลงไฟล์
terraform plan -out=tfplan
# อ่าน plan ในรูปแบบ JSON
terraform show -json tfplan | jq '.resource_changes'
# Apply เฉพาะ plan ที่ save ไว้
terraform apply tfplan
ใน CI/CD ใช้ pattern:
- PR open → run
plan -out=tfplan→ comment ลง PR - Reviewer ดู plan
- PR merge →
apply tfplanตัวเดิม → no surprise
สรุป
- 4 actions: create (
+), update (~), replace (-/+), destroy (-) - บาง argument force replacement → ดูใน plan output
- State = source of truth → drift = state ≠ reality
- ใช้
-refresh-onlyเช็ค drift,-replaceforce replace - Terraform เป็น idempotent — apply ซ้ำได้โดยไม่ทำอะไรเพิ่ม
ต่อไป → Resource Lifecycle