Eksplorasi fungsi bawaan OpenTofu untuk string, collection, dan filesystem, lalu kuasai meta-argument count, for_each, depends_on, dan lifecycle, dilengkapi for expressions serta ternary conditionals untuk menulis HCL yang ringkas, dinamis, dan ekspresif.

Di episode 7 sebelumnya kita membahas dynamic provider iteration — memuat banyak instansi provider sekaligus memakai for_each untuk multi-region dan multi-account. Kita sudah merasakan bagaimana satu map kecil bisa menggantikan puluhan baris HCL. Hari ini kita naik satu tingkat lagi: membongkar bahasa ekspresi OpenTofu yang membuat HCL terasa seperti bahasa pemrograman sungguhan.
Sering kali engineer terjebak menulis konfigurasi secara statis dan berulang — hardcode daftar subnet, menyalin block resource satu per satu, atau menulis kondisi jika-maka dengan cara yang kaku. Padahal OpenTofu menyediakan ratusan fungsi bawaan, meta-argument perulangan, dan ekspresi kondisional yang bisa memangkas jumlah kode secara drastis.
Di episode ini kita akan membahas empat pilar ekspresi OpenTofu: built-in functions untuk string, collection, dan filesystem; meta-argument count, for_each, dan depends_on; aturan lifecycle; serta for expressions dan ternary conditionals.
Fungsi bawaan di OpenTofu bekerja seperti di bahasa pemrograman — mereka memproses nilai dan mengembalikan hasil. Secara garis besar ada tiga keluarga yang paling sering dipakai:
| Keluarga | Contoh Fungsi | Kegunaan |
|---|---|---|
| String | format, join, split, replace, upper | Manipulasi teks dan pembentukan nama resource |
| Collection | length, lookup, merge, flatten, distinct, setunion | Menggabungkan, menyaring, dan merapikan data |
| Filesystem | file, templatefile, yamldecode, jsondecode | Membaca konfigurasi dari file eksternal |
Berikut contoh pemakaiannya dalam satu blok locals:
locals {
env = "staging"
bucket = join("-", ["app", local.env, "assets"])
subnets = split(",", var.subnet_list)
merged = merge(var.default_tags, { env = local.env })
regions = distinct(var.regions)
db_user = lookup(var.db_config, "user", "admin")
yaml_conf = yamldecode(file("config.yaml"))
safe_value = try(coalesce(var.name, local.env), "default")
}join dan split membentuk dan memecah string dengan pemisah.merge menggabungkan beberapa map — sangat berguna untuk menata tag secara konsisten.distinct menghapus duplikat dari list.lookup mengambil nilai dari map dengan default bila kunci tidak ada.file dan yamldecode membaca file konfigurasi dari disk dan mem-parsing-nya.try dan coalesce menangani nilai kosong secara aman tanpa error.Tip
Jelajahi seluruh daftar fungsi kapan pun dengan mengetik tofu console lalu coba ekspresi seperti join("-", ["a", "b"]) langsung di prompt. Ini playground paling cepat untuk menguji perilaku fungsi sebelum menuliskannya ke kode.
count dan for_eachDua meta-argument ini adalah tulang punggung perulangan di OpenTofu. count membuat resource berdasarkan angka dan mengaksesnya lewat count.index; for_each membuat resource berdasarkan item map atau set dan mengaksesnya lewat each.key dan each.value.
resource "aws_instance" "web" {
count = 3
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "web-${count.index}"
}
}resource "aws_iam_user" "developers" {
for_each = toset(["budi", "sari", "dewi"])
name = each.key
tags = {
Role = each.key == "budi" ? "admin" : "dev"
}
}Perhatikan baris Role di atas: ia memakai ternary — jika each.key sama dengan budi, nilainya admin, selain itu dev. Inilah pola klasik: perulangan untuk menyebar, ternary untuk menyesuaikan.
Warning
Gunakan for_each untuk kumpulan yang punya identitas unik (nama user, kunci map), dan count hanya untuk kumpulan yang benar-benar identik. Menghapus satu item di tengah list count bisa menggeser index dan memicu recreate resource yang tidak diinginkan — for_each jauh lebih aman karena key bersifat eksplisit.
depends_onOpenTofu membangun grafik dependensi secara otomatis dari referensi antar resource. depends_on hanya dibutuhkan saat urutan eksekusi tidak bisa disimpulkan dari referensi — misalnya konfigurasi yang butuh resource lain selesai lebih dulu tanpa referensi langsung:
resource "aws_instance" "app" {
depends_on = [aws_security_group.main]
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
}Note
depends_on sebaiknya menjadi pilihan terakhir. Terlalu banyak depends_on membuat grafik kaku dan lambat, serta menyembunyikan dependensi yang sebenarnya bisa diekspresikan lewat referensi nilai biasa. Biarkan OpenTofu mengerjakan apa yang sudah bisa ia simpulkan sendiri.
Meta-argument lifecycle memberi kendali atas kapan dan bagaimana OpenTofu menciptakan ulang resource. Tiga aturan yang paling sering dipakai:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
lifecycle {
create_before_destroy = true
prevent_destroy = false
ignore_changes = [tags]
}
}create_before_destroy membuat resource baru sebelum menghapus yang lama — wajib untuk meminimalkan downtime.prevent_destroy memblokir tofu destroy — pelindung untuk resource kritis seperti database.ignore_changes mengabaikan atribut yang sering berubah di luar IaC, misalnya tag yang diubah konsol.Warning
prevent_destroy bukan anti-vandalisme mutlak: ia hanya menolak resource dihapus dari kode, tapi tidak mencegah destroy karena perubahan definisi. Untuk resource yang paling kritis, kombinasikan dengan state encryption dan backup. Hapus resource yang di-protect harus lewat tofu plan -destroy setelah menghapus baris prevent_destroy dari kode.
For expression menghasilkan koleksi baru dari koleksi lain — semacam map atau filter dari bahasa pemrograman. Splat [*] adalah jalan pintas untuk mengambil satu atribut dari semua instance. Ternary kondisi ? nilai_benar : nilai_salah memilih nilai berdasarkan kondisi:
locals {
sequence = [for i in range(3) : "web-${i}"]
filtered = [for name in var.names : upper(name) if name != "skip"]
tag_map = { for k, v in var.tags : k => upper(v) }
all_ips = aws_instance.web[*].private_ip
tier = var.environment == "production" ? "m5.large" : "t3.micro"
}sequence membangun list dari range.filtered mentransformasi sekaligus menyaring dengan klausa if.tag_map membangun map dengan ekspresi k => v.all_ips memakai splat untuk mengumpulkan private_ip dari semua instance.tier memilih tipe instance berdasarkan environment — ternary di sini menggantikan seluruh blok bersyarat.Kombinasikan semuanya dengan fungsi bawaan, dan kalian bisa mengekspresikan hampir semua logika konfigurasi tanpa keluar dari HCL.
Pada episode 8 ini kita menguasai bahasa ekspresi OpenTofu:
join, merge, lookup hingga file dan yamldecode.count dan for_each untuk perulangan resource, dengan count.index dan each.key / each.value.depends_on untuk dependensi eksplisit yang tidak bisa disimpulkan OpenTofu.lifecycle — create_before_destroy, prevent_destroy, dan ignore_changes.Dengan bekal ekspresi ini, HCL kalian akan jauh lebih dinamis. Di episode 9 selanjutnya kita akan membahas OpenTofu State Manipulation & Import Workflows — mengimpor infrastruktur eksisting lewat import block dan tofu import, lalu melakukan refactoring tanpa destroy memakai moved block dan tofu state mv. Sampai jumpa!