Skip to main content

Resource Lifecycle

lifecycle block — fine-tune ว่า Terraform จะ create/destroy/update resource ยังไง — กันลบเผลอ, zero-downtime replace, ฯลฯ

Lifecycle Block

resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t2.micro"

lifecycle {
create_before_destroy = true
prevent_destroy = false
ignore_changes = [tags]
replace_triggered_by = [aws_security_group.web.id]
}
}

มี 4 options หลัก:

1. create_before_destroy

Default: Terraform จะ destroy → create (อาจ downtime) ถ้าตั้ง true: create ใหม่ก่อน → destroy ของเก่า (zero downtime)

resource "aws_launch_template" "web" {
name_prefix = "web-"
image_id = "ami-12345"
instance_type = "t2.micro"

lifecycle {
create_before_destroy = true
}
}

ใช้ตอน:

  • Auto Scaling Group / Launch Template
  • ASG ที่ replace ทุกครั้งที่ launch template เปลี่ยน
  • Resource ที่ "name" unique (ใช้ name_prefix ร่วมด้วย)
ข้อจำกัด
  • ถ้า resource มี unique constraint (เช่น name ซ้ำไม่ได้) → ต้องใช้ name_prefix แทน name
  • ไม่ work ถ้า resource มี dependency ที่ไม่ replace ตามไปด้วย

2. prevent_destroy

ป้องกัน destroy แบบ accident — ใช้กับ resource สำคัญ เช่น database, S3 bucket ที่มีข้อมูลจริง

resource "aws_db_instance" "prod" {
identifier = "prod-db"
engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 100

lifecycle {
prevent_destroy = true
}
}

ถ้ามีคนพยายามลบ → Terraform fail ทันที:

Error: Instance cannot be destroyed
Resource aws_db_instance.prod has lifecycle.prevent_destroy set...
ตอนอยากลบจริง

ถ้าจำเป็นต้องลบจริงๆ — ลบ prevent_destroy = true ออกจาก code → commit → apply ขั้นตอนนี้ทำให้มี audit trail ว่าใครเป็นคนลบ

3. ignore_changes

บอก Terraform ว่า "ไม่ต้องสนใจ argument นี้แม้จะต่างจาก config"

resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = "t2.micro"

tags = {
Name = "web"
}

lifecycle {
ignore_changes = [
tags, # เพิกเฉยทั้ง tags
ami, # ไม่ replace ตอน AMI ใหม่
]
}
}

Use Cases:

  1. Auto-managed tags — tools อื่น (เช่น AWS Backup) เพิ่ม tag เอง
  2. Auto-update — AMI updated โดย AWS auto patching
  3. Initial deploy only — ตั้งค่าเริ่มต้น แล้วปล่อยให้คนแก้ใน console (อาจไม่ดี!)

Ignore ทั้งหมด:

lifecycle {
ignore_changes = all
}
Anti-pattern

ใช้ ignore_changes = all แสดงว่า resource นั้น "ไม่ใช่ Terraform manage แล้ว" — น่าจะ remove ออกจาก state ไปเลยมากกว่า

4. replace_triggered_by

Force replace resource เมื่อ resource อื่นเปลี่ยน (Terraform 1.2+)

resource "aws_appautoscaling_policy" "web" {
# ... config ...

lifecycle {
replace_triggered_by = [
aws_appautoscaling_target.web.id
]
}
}

ใช้ตอน:

  • ASG policy ที่ต้อง re-create ทุกครั้งที่ target เปลี่ยน
  • Lambda function ต้อง re-deploy เมื่อ source code (S3 object) เปลี่ยน

รวม Lifecycle Options

resource "aws_db_instance" "prod" {
identifier = "prod-db"
engine = "postgres"
instance_class = "db.t3.medium"

lifecycle {
create_before_destroy = true # zero-downtime replace
prevent_destroy = true # ป้องกันลบ
ignore_changes = [ # ignore เปลี่ยนแปลงเหล่านี้
password, # password rotated by Vault
tags["LastBackup"], # backup tool เป็นคนอัปเดต
]
}
}

ตัวอย่าง Production Patterns

Pattern 1: ASG with Launch Template

resource "aws_launch_template" "web" {
name_prefix = "web-lt-"
image_id = data.aws_ami.amazon_linux.id
instance_type = var.instance_type

lifecycle {
create_before_destroy = true
}
}

resource "aws_autoscaling_group" "web" {
name_prefix = "web-asg-"
launch_template {
id = aws_launch_template.web.id
version = aws_launch_template.web.latest_version
}
min_size = 2
max_size = 10

lifecycle {
create_before_destroy = true
}
}

Pattern 2: Production Database

resource "aws_db_instance" "prod" {
identifier = "prod-db"
engine = "postgres"
engine_version = "15.4"
instance_class = "db.t3.medium"
allocated_storage = 100

backup_retention_period = 30
deletion_protection = true # double protection จาก AWS เอง

lifecycle {
prevent_destroy = true
ignore_changes = [
engine_version, # AWS auto minor version upgrade
latest_restorable_time,
]
}
}

Pattern 3: Lambda with Auto-Updated Source

resource "aws_lambda_function" "api" {
function_name = "my-api"
role = aws_iam_role.lambda.arn
handler = "index.handler"
runtime = "nodejs20.x"

s3_bucket = aws_s3_bucket.code.id
s3_key = "lambda/api.zip"

lifecycle {
replace_triggered_by = [
aws_s3_object.lambda_code.etag # ทุกครั้งที่ code zip เปลี่ยน
]
}
}

เมื่อไหร่ไม่ใช้ Lifecycle?

  • อย่าใช้ prevent_destroy ทุก resource — ทำให้ลบยาก ทดสอบ destroy ลำบาก
  • อย่าใช้ ignore_changes = all — แปลว่า "ไม่ใช่ Terraform แล้ว"
  • อย่าใช้ create_before_destroy กับ resource ที่ไม่ต้องการ zero-downtime

สรุป

Optionหน้าที่
create_before_destroyZero-downtime replace
prevent_destroyกันลบ resource สำคัญ
ignore_changesIgnore argument บาง field
replace_triggered_byForce replace เมื่อ resource อื่นเปลี่ยน

ใช้ lifecycle เท่าที่จำเป็น — มากเกินไป = code ที่อ่านยาก + behavior ที่ surprise

ต่อไป → Meta Arguments — count, for_each, depends_on, provider