Lifecycle #

Secara default, Terraform mengikuti siklus sederhana untuk setiap resource: buat jika belum ada, ubah jika berbeda, hapus jika tidak lagi dikonfigurasi. Tapi default ini tidak selalu tepat untuk semua jenis resource. Database production tidak boleh dihapus karena kesalahan ketik. Load balancer tidak boleh down sedetikpun saat update. Beberapa atribut dikelola oleh sistem lain dan tidak boleh ditimpa Terraform. Lifecycle block memberimu kontrol untuk menangani semua kasus ini.

Default Lifecycle Terraform #

Sebelum melihat cara mengkustomisasi lifecycle, penting untuk memahami perilaku defaultnya.

DEFAULT LIFECYCLE UNTUK SETIAP RESOURCE:

  CREATE  → Jika resource belum ada di state
  UPDATE  → Jika resource ada tapi konfigurasi berbeda
            (dua kemungkinan: update in-place atau replace)
  DESTROY → Jika resource ada di state tapi tidak lagi di konfigurasi

Urutan saat perlu replace (destroy + create):
  1. Destroy resource lama
  2. Create resource baru

Ini berarti ada periode di mana resource tidak ada sama sekali
— berpotensi menyebabkan downtime untuk resource yang aktif melayani traffic.

create_before_destroy #

create_before_destroy membalik urutan operasi replace: buat dulu resource baru, baru hapus yang lama. Ini meminimalkan downtime untuk resource yang tidak boleh down.

# SKENARIO: Update AMI pada instance yang melayani traffic production

# ANTI-PATTERN: Default behavior — downtime saat replace
resource "aws_instance" "web" {
  ami           = var.ami_id  # Ubah AMI → forces replacement
  instance_type = "t3.micro"
  # Urutan default:
  # 1. Destroy instance lama → traffic mati
  # 2. Create instance baru  → traffic hidup kembali
  # Downtime: ~20-30 detik
}

# BENAR: create_before_destroy — zero downtime replace
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.micro"

  lifecycle {
    create_before_destroy = true
    # Urutan baru:
    # 1. Create instance baru → running
    # 2. Destroy instance lama → tidak ada downtime
    # (pastikan ada mekanisme untuk shift traffic dulu,
    # misal: load balancer attachment)
  }
}
# create_before_destroy sangat berguna untuk SSL certificate
resource "aws_acm_certificate" "main" {
  domain_name       = var.domain_name
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
    # Sertifikat baru dibuat dan divalidasi sebelum yang lama dihapus
    # Tidak ada periode di mana domain tidak punya sertifikat valid
  }
}

prevent_destroy #

prevent_destroy membuat Terraform menolak menghapus resource — apply akan error sebelum operasi destroy sempat berjalan. Ini adalah lapisan perlindungan terakhir untuk resource kritis.

# Resource production yang tidak boleh dihapus secara tidak sengaja
resource "aws_rds_instance" "production" {
  identifier        = "production-db"
  engine            = "postgres"
  engine_version    = "15.3"
  instance_class    = "db.t3.medium"
  allocated_storage = 100
  storage_encrypted = true

  lifecycle {
    prevent_destroy = true
  }
}

# Apa yang terjadi jika ada yang mencoba terraform destroy:
# ╷
# │ Error: Instance cannot be destroyed
# │
# │   on main.tf line 1, in resource "aws_rds_instance" "production":
# │    1: resource "aws_rds_instance" "production" {
# │
# │ Resource aws_rds_instance.production has lifecycle.prevent_destroy
# │ set to true. To allow this object to be destroyed, remove or disable
# │ this attribute.
# ╵
prevent_destroy hanya melindungi dari operasi yang diinisiasi Terraform. Ia tidak mencegah penghapusan langsung dari console AWS atau CLI. Untuk perlindungan penuh, gunakan juga termination protection bawaan resource (misalnya deletion_protection = true pada RDS) dan IAM policy yang membatasi akses delete.

ignore_changes #

ignore_changes memberitahu Terraform untuk mengabaikan perbedaan pada atribut tertentu — tidak akan memicu update meskipun nilainya berubah. Berguna untuk atribut yang dikelola di luar Terraform.

resource "aws_autoscaling_group" "web" {
  name         = "web-asg"
  min_size     = 2
  max_size     = 10
  desired_capacity = 2

  launch_template {
    id      = aws_launch_template.web.id
    version = "$Latest"
  }

  lifecycle {
    ignore_changes = [
      # desired_capacity dikelola oleh auto scaling policy — abaikan
      desired_capacity,

      # tag "LastDeployedAt" diupdate oleh deployment pipeline — abaikan
      tag,
    ]
  }
}
# ignore_changes = all — abaikan SEMUA perubahan pada atribut resource
# Gunakan dengan sangat hati-hati — ini berarti Terraform tidak akan
# pernah mengupdate resource ini setelah dibuat
resource "aws_instance" "immutable" {
  ami           = var.ami_id
  instance_type = "t3.micro"

  lifecycle {
    ignore_changes = all
    # Berguna untuk resource "immutable" yang hanya dibuat sekali
    # dan tidak pernah diubah — perubahan konfigurasi hanya berlaku
    # untuk instance baru, bukan yang sudah ada
  }
}

replace_triggered_by #

replace_triggered_by memaksa resource untuk di-replace ketika resource atau atribut lain berubah — meskipun tidak ada perubahan langsung pada resource itu sendiri.

# SKENARIO: Instance harus di-replace setiap kali launch template berubah
resource "aws_launch_template" "web" {
  name_prefix   = "web-"
  image_id      = var.ami_id
  instance_type = "t3.micro"
}

resource "aws_instance" "web" {
  # Instance ini tidak langsung bergantung pada launch template versi,
  # tapi kita ingin ia di-replace setiap kali launch template berubah

  launch_template {
    id      = aws_launch_template.web.id
    version = aws_launch_template.web.latest_version
  }

  lifecycle {
    replace_triggered_by = [
      aws_launch_template.web  # Replace instance jika launch template berubah
    ]
  }
}

Kombinasi Lifecycle Rules #

Lifecycle rules bisa dikombinasikan sesuai kebutuhan.

resource "aws_db_instance" "main" {
  identifier        = "app-database"
  engine            = "mysql"
  instance_class    = "db.t3.medium"
  allocated_storage = 50
  storage_encrypted = true

  # Password dirotasi oleh secrets manager — jangan timpa
  password = data.aws_secretsmanager_secret_version.db_password.secret_string

  lifecycle {
    # Buat DB baru sebelum hapus yang lama saat replace
    create_before_destroy = true

    # Lindungi dari destroy tidak sengaja
    prevent_destroy = true

    # Abaikan perubahan password — dikelola oleh secrets manager
    ignore_changes = [password]
  }
}

Ringkasan #

  • create_before_destroy = true untuk resource yang tidak boleh down — buat yang baru dulu sebelum hapus yang lama. Sangat penting untuk server, certificate, dan load balancer.
  • prevent_destroy = true untuk resource kritis seperti production database — Terraform akan error sebelum sempat menghapusnya.
  • prevent_destroy tidak melindungi dari penghapusan di luar Terraform — kombinasikan dengan termination protection bawaan resource dan IAM policy.
  • ignore_changes untuk atribut yang dikelola di luar Terraform — desired_capacity pada ASG, password yang dirotasi, tag yang diupdate pipeline.
  • ignore_changes = all untuk resource benar-benar immutable — gunakan dengan sangat hati-hati, Terraform tidak akan pernah mengupdate resource ini.
  • replace_triggered_by untuk memaksa replace berdasarkan perubahan resource lain yang tidak terdeteksi secara otomatis.

← Sebelumnya: Apa itu Resource?   Berikutnya: Dependency →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact