Resource Lifecycle di OpenTofu
Kendalikan siklus hidup resource dengan meta-argument lifecycle: prevent_destroy, create_before_destroy, ignore_changes, replace_triggered_by, dan validasi kustom.
Daftar Isi
Secara default, OpenTofu mengelola siklus hidup resource (resource lifecycle) menggunakan aturan standar:
- Pembaruan di tempat (In-place Update): Jika ada perubahan atribut yang didukung oleh API target, OpenTofu akan mengubahnya langsung tanpa mematikan resource.
- Hapus lalu buat ulang (Destroy-then-Create): Jika suatu perubahan membutuhkan pembuatan ulang (misalnya mengganti image dasar atau subnet), OpenTofu akan menghancurkan resource lama terlebih dahulu, baru kemudian membuat resource baru.
- Rekonsiliasi Drift: Jika ada modifikasi manual di sisi server di luar kode OpenTofu, OpenTofu akan menimpanya kembali agar sesuai dengan kode
.tf. - Penghapusan Total: Perintah
tofu destroyakan menghapus seluruh resource yang terdaftar tanpa kecuali.
Namun, dalam sistem produksi nyata, aturan default ini tidak selalu ideal. Menghapus database secara tidak sengaja, mengalami downtime saat memperbarui layanan web, atau konflik dengan perubahan yang dibuat oleh autoscaler adalah risiko nyata.
Di sinilah meta-argument lifecycle berperan. Blok ini disematkan langsung di dalam blok resource untuk mengintervensi dan mengubah perilaku standar OpenTofu.
Anatomi Blok lifecycle
Blok lifecycle diletakkan langsung di dalam blok resource:
resource "<RESOURCE_TYPE>" "<LOCAL_NAME>" {
# ... atribut resource ...
lifecycle {
prevent_destroy = true
create_before_destroy = true
ignore_changes = [ ... ]
replace_triggered_by = [ ... ]
}
}
Mari kita bedah masing-masing argumen utama beserta skenario penggunaannya di dunia nyata.
1. prevent_destroy (Rem Darurat Penghapusan)
Fungsinya
prevent_destroy bertindak sebagai safety guardrail atau rem darurat. Jika bernilai true, OpenTofu akan menolak keras dan langsung membatalkan eksekusi jika rencana (plan atau apply) berpotensi menghapus resource tersebut.
Kapan Digunakan?
- Disk penyimpanan persisten (
docker_volume, AWS EBS volume, GCS Bucket). - Database utama (production database).
- Komponen jaringan inti yang jika terhapus akan memutus seluruh akses sistem.
Contoh Implementasi
resource "docker_volume" "db_data" {
name = "pgdata-production"
lifecycle {
prevent_destroy = true
}
}
Uji Coba: Apa yang Terjadi Saat Dihapus?
Jika Anda atau anggota tim secara tidak sengaja menjalankan:
tofu destroy
OpenTofu akan langsung menolak dan menghentikan seluruh proses dengan pesan error berikut:
Error: Instance cannot be destroyed
on main.tf line 1, in resource "docker_volume" "db_data":
1: resource "docker_volume" "db_data" {
Resource docker_volume.db_data has lifecycle.prevent_destroy set, but the plan
calls for this resource to be destroyed. To safely destroy this resource,
remove the prevent_destroy block or set it to false and run tofu apply.
Cara Menghapusnya Secara Sengaja: Jika resource memang sudah tidak digunakan dan perlu dihapus, Anda harus secara sadar mengubah
prevent_destroy = false(atau menghapus bloknya), menjalankantofu applysekali, baru kemudian menjalankantofu destroy.
2. create_before_destroy (Pembaruan Tanpa Downtime)
Masalah pada Perilaku Default
Secara default, jika suatu atribut mengharuskan resource dibuat ulang (replacement), OpenTofu melakukan:
- Stop & Destroy: Menghancurkan resource lama.
- Create: Membuat resource baru.
Proses ini menimbulkan celah waktu (downtime) di mana layanan Anda offline sebelum versi baru siap melayani trafik.
Solusi create_before_destroy
Dengan menyetel create_before_destroy = true, urutannya dibalik:
- OpenTofu membuat resource baru terlebih dahulu hingga statusnya sehat (healthy).
- Memperbarui rujukan ketergantungan (misalnya load balancer mengalihkan trafik ke resource baru).
- Menghancurkan resource lama dengan aman.
Perilaku Default:
[Resource Lama] ──► Hapus ──► (DOWNTIME) ──► Buat [Resource Baru]
Dengan create_before_destroy:
[Resource Baru] Dibuat & Siap ──► Alihkan Trafik ──► Hapus [Resource Lama]
Contoh Implementasi
resource "docker_container" "web" {
name = "web-server"
image = "nginx:alpine"
lifecycle {
create_before_destroy = true
}
}
Catatan Penting: Resource yang memiliki batasan nama unik (seperti container Docker di host yang sama atau resource cloud dengan nama statis) akan memicu konflik nama jika menggunakan
create_before_destroy. Solusinya adalah menggunakan nama dinamis (misalnya dengan awalanname_prefixatau menambahkan random hash unik).
3. ignore_changes (Mengabaikan Perubahan Spesifik)
Masalah Drift Eksternal
Setelah resource dibuat, sering kali ada atribut yang nilainya dimodifikasi oleh sistem luar di luar kendali OpenTofu, misalnya:
- Tag atau metadata yang diinjeksikan secara otomatis oleh bot audit cloud.
- Kapasitas replika yang dinaikkan/diturunkan oleh autoscaler (HPA / Auto Scaling Group).
- Sertifikat SSL atau password yang diperbarui secara otomatis oleh proses rotasi rahasia internal.
Jika tidak diantisipasi, setiap kali Anda menjalankan tofu apply, OpenTofu akan mencoba mengembalikan nilai tersebut sesuai kode .tf, memicu perubahan yang tidak diinginkan.
Solusi ignore_changes
ignore_changes menginstruksikan OpenTofu untuk mengabaikan perbedaan (drift) pada atribut-atribut yang ditentukan:
resource "docker_container" "app" {
name = "backend-api"
image = "nginx:alpine"
# Kita definisikan label dasar pada kode
labels {
label = "managed_by"
value = "opentofu"
}
lifecycle {
# Abaikan jika ada label tambahan yang diinjeksi oleh agen monitoring luar
ignore_changes = [
labels,
]
}
}
Jika Anda ingin OpenTofu mengabaikan seluruh perubahan yang terjadi di luar konfigurasi setelah resource pertama kali dibuat:
lifecycle {
ignore_changes = all
}
4. replace_triggered_by (Trigger Buat Ulang Berdasarkan Dependensi)
Masalah Pembaruan Tidak Terdeteksi
Terkadang, sebuah container atau server perlu dibuat ulang bukan karena konfigurasinya sendiri berubah, melainkan karena ada berkas eksternal atau konfigurasi pendukung yang berubah (misalnya: berkas konfigurasi nginx.conf atau skrip inisialisasi).
Sebelumnya, praktisi IaC sering mengakalinya dengan teknik taint manual. Di OpenTofu modern, Anda cukup menggunakan replace_triggered_by.
Contoh Implementasi dengan terraform_data
Gunakan resource bawaan terraform_data untuk memantau perubahan hash konten berkas lokal, lalu jadikan pemicu pembuatan ulang container:
# 1. Pantau perubahan berkas konfigurasi lokal
resource "terraform_data" "nginx_config" {
input = filebase64sha256("${path.module}/nginx.conf")
}
# 2. Container otomatis dibuat ulang saat hash file konfigurasi berubah
resource "docker_container" "web" {
name = "web-service"
image = "nginx:alpine"
lifecycle {
replace_triggered_by = [
terraform_data.nginx_config
]
}
}
Ketika Anda mengedit isi file nginx.conf, OpenTofu akan mendeteksi perubahan hash pada terraform_data.nginx_config dan secara otomatis menandai docker_container.web untuk diganti (replace) pada tofu plan berikutnya.
5. Validasi Kustom: precondition & postcondition
Dalam OpenTofu modern, blok lifecycle juga mendukung validasi tingkat lanjut untuk memastikan resource hanya dibuat jika kondisi lingkungan terpenuhi:
precondition: Mengevaluasi kondisi sebelum resource dieksekusi.postcondition: Mengevaluasi kondisi setelah resource dibuat untuk memastikan hasil sesuai harapan.
resource "docker_container" "web" {
name = "web-server"
image = "nginx:alpine"
lifecycle {
# Validasi sebelum membuat container: pastikan container bukan di environment produksi jika memakai tag alpine
precondition {
condition = var.environment != "production" || !can(regex("alpine", var.nginx_image))
error_message = "Image varian Alpine tidak diizinkan untuk lingkungan production."
}
}
}
Ringkasan Cepat Blok lifecycle
| Argumen | Efek Utama | Contoh Kasus Penggunaan |
|---|---|---|
prevent_destroy | Membatalkan eksekusi jika resource berpotensi terhapus. | Disk persisten, database utama production. |
create_before_destroy | Membuat resource pengganti sebelum mematikan yang lama. | Mencegah downtime pada web server/microservice. |
ignore_changes | Mengabaikan drift pada atribut tertentu saat rekonsiliasi. | Tag dinamis dari cloud agent, jumlah replika autoscaler. |
replace_triggered_by | Memaksa pembuatan ulang saat resource lain berubah. | Refresh container saat berkas config atau secret berubah. |
precondition / postcondition | Validasi aturan logika sebelum / sesudah resource dibuat. | Guardrail keamanan arsitektur dan kepatuhan standar. |
Contoh Proyek Terpadu
Berikut contoh berkas main.tf terpadu yang menggabungkan seluruh konsep siklus hidup di atas:
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 4.6.0"
}
}
}
provider "docker" {
host = "unix:///var/run/docker.sock"
}
# 1. Volume persisten dengan perlindungan anti-hapus
resource "docker_volume" "app_storage" {
name = "app-production-data"
lifecycle {
prevent_destroy = true
}
}
# 2. Pemantau konfigurasi
resource "terraform_data" "app_version" {
input = "v1.0.0"
}
# 3. Container dengan lifecycle gabungan
resource "docker_container" "app_server" {
name = "app-server"
image = "nginx:alpine"
labels {
label = "managed_by"
value = "opentofu"
}
lifecycle {
# Buat container pengganti terlebih dahulu jika terjadi perubahan
create_before_destroy = true
# Jangan timpa label jika ditambahkan oleh agen pemantau luar
ignore_changes = [
labels,
]
# Buat ulang container jika versi aplikasi berubah
replace_triggered_by = [
terraform_data.app_version
]
}
}
Rangkuman & Langkah Berikutnya
Menguasai meta-argument lifecycle memberi Anda kendali penuh atas bagaimana infrastruktur diubah, diperbarui, dan dilindungi:
- Gunakan
prevent_destroyuntuk mengunci aset kritis dari bencana human error. - Gunakan
create_before_destroyuntuk merilis pembaruan layanan dengan zero downtime. - Gunakan
ignore_changesuntuk hidup berdampingan secara damai dengan autoscaler atau agen pihak ketiga. - Gunakan
replace_triggered_byuntuk otomatisasi siklus rilis berbasis dependensi eksternal.
Di bagian berikutnya, kita akan mendalami State Management & Remote Backend—mempelajari bagaimana tim berkolaborasi secara aman pada state yang sama menggunakan object storage (seperti S3 atau MinIO) lengkap dengan mekanisme state locking.