Di episode ini kita akan membuat konfigurasi Terraform menjadi fleksibel dan reusable melalui input variables, menyederhanakan ekspresi berulang dengan local values, serta menampilkan informasi penting pasca-apply lewat output values.

Setelah di episode 3 sebelumnya kita membahas bagaimana provider menjadi jembatan ke cloud API, resource menciptakan infrastruktur, dan data source membaca infrastruktur yang sudah ada — pada episode kali ini kita akan mengubah konfigurasi yang tadinya "kaku" menjadi fleksibel dan dapat dipakai ulang lewat tiga mekanisme kunci: input variables, local values, dan output values.
Bayangkan kalian menulis konfigurasi untuk membuat EC2 instance. Sekarang kalian perlu membuat instance serupa untuk environment staging dan production. Apakah kalian akan menyalin-paste seluruh file dan mengganti beberapa nilai di dalamnya? Itulah awal mula mimpi buruk duplicated code — dan justru masalah yang paling sering memicu kesalahan di dunia nyata. Variabel hadir untuk memecahkan masalah ini: satu set kode, banyak variasi nilai.
Tapi mengapa sampai tiga mekanisme? Karena masing-masing punya peran berbeda: variable untuk menerima input dari luar (fleksibilitas antar environment), locals untuk menghitung/menyimpan nilai di dalam konfigurasi (prinsip DRY), dan output untuk menampilkan/berbagi hasil ke manusia atau modul lain. Memahami kapan memakai yang mana adalah tanda seorang IaC practitioner yang matang, bukan sekadar pemula yang hafal sintaks.
Input variable adalah cara Terraform menerima nilai dari luar file konfigurasi. Analogikan seperti kolom isian pada formulir: kode kalian adalah formulirnya, variabel adalah kolomnya, dan nilai yang diisi bisa berbeda-beda tergantung siapa yang mengisi dan untuk keperluan apa.
Blok variable didefinisikan dengan sintaks berikut:
variable "nama_variabel" {
type = tipe_data
default = nilai_bawaan
description = "penjelasan untuk manusia"
validation {
condition = ekspresi_kondisi
error_message = "pesan error saat kondisi gagal"
}
}Contoh nyata dalam file variables.tf yang rapi dan production-grade:
variable "project_name" {
type = string
default = "myapp"
description = "Nama proyek yang dipakai sebagai prefix penamaan resource"
}
variable "environment" {
type = string
description = "Environment deployment (dev, staging, prod)"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Nilai environment harus salah satu dari: dev, staging, prod."
}
}
variable "instance_type" {
type = string
default = "t3.micro"
description = "Tipe instance EC2"
validation {
condition = can(regex("^t[0-9]", var.instance_type))
error_message = "Untuk workload ringan gunakan family instance t-series."
}
}
variable "tags" {
type = map(string)
default = {}
description = "Tag tambahan yang akan digabung ke semua resource"
}Poin penting dari contoh di atas:
type — memaksa jenis data. Salah tipe (misal mengisi angka untuk string) akan langsung ditolak Terraform sebelum apply.default — nilai bawaan jika tidak ada nilai yang diberikan dari luar. Kolom tanpa default adalah kolom wajib isi; jika tidak diisi, Terraform akan bertanya secara interaktif — yang mengganggu di lingkungan otomatis seperti CI/CD.validation — guardrail yang menolak nilai tidak masuk akal lebih awal. Ini jauh lebih baik daripada error runtime yang baru muncul di tengah-tengah terraform apply.Important
Setiap variabel wajib memiliki description. Ini bukan sekadar estetika — dokumentasi ini yang akan dilihat rekan setim dan dipakai terraform-docs untuk meng-generate dokumentasi modul. Variabel tanpa deskripsi adalah tanda kode yang tidak siap untuk tim.
Terraform mendukung delapan tipe dasar yang wajib kalian kuasai:
| Tipe | Bentuk Nilai | Contoh |
|---|---|---|
string | Teks | "t3.micro" |
number | Angka | 2 |
bool | Boolean | true |
list(<tipe>) | Koleksi berurutan | ["ap-southeast-1a", "ap-southeast-1b"] |
set(<tipe>) | Koleksi unik (urutan tidak penting) | ["web", "db"] |
map(<tipe>) | Pasangan kunci-nilai | { Name = "myapp" } |
object({...}) | Struktur dengan field bertipe | { region = "ap-southeast-1", count = 3 } |
tuple([...]) | Koleksi dengan tipe berbeda per posisi | ["sg", 2, true] |
Tip
Aturan praktis yang dipakai banyak tim: gunakan list untuk daftar hal yang sejenis, map untuk label/tag, set untuk hal yang harus unik, dan object ketika data punya struktur campuran — misalnya konfigurasi database yang punya engine, version, dan allocated_storage sekaligus.
Nilai variabel bisa diberikan melalui tiga jalur utama. Ketiganya menghasilkan hal yang sama — hanya berbeda "dari mana nilainya datang":
project_name = "myapp"
environment = "production"
instance_type = "t3.large"Penjelasan tiap metode:
.tfvars — cara paling umum dan paling rapi untuk environment-based config. File terraform.tfvars dibaca otomatis oleh Terraform tanpa perlu flag tambahan. Untuk nilai yang spesifik per developer atau CI, pakai file *.auto.tfvars agar terbaca otomatis tanpa perlu flag eksplisit.-var — cocok untuk nilai ad-hoc yang tidak ingin disimpan di file, misalnya nilai rahasia yang sudah ada di environment CI.TF_VAR_ — pola paling aman untuk rahasia: nilai tetap tinggal di environment variable / secrets manager, kode hanya berisi nama variabelnya.Note
Karena file terraform.tfvars dan *.auto.tfvars sering berisi nilai rahasia (password database, API key), pastikan keduanya masuk .gitignore. Gunakan terraform.tfvars.example sebagai template yang boleh di-commit, lalu copy menjadi terraform.tfvars lokal.
Bagaimana jika nilai yang sama diberikan lewat beberapa metode sekaligus? Terraform memiliki aturan prioritas yang konsisten dan deterministik, dari prioritas terendah ke tertinggi:
| Prioritas | Sumber Nilai |
|---|---|
| 1 (terendah) | Environment variable TF_VAR_* |
| 2 | File terraform.tfvars |
| 3 | File terraform.tfvars.json |
| 4 | File *.auto.tfvars / *.auto.tfvars.json (urut abjad) |
| 5 (tertinggi) | CLI flag -var / -var-file |
Jadi jika TF_VAR_environment=dev, terraform.tfvars berisi environment = "staging", dan kalian menjalankan terraform apply -var="environment=production", maka yang menang adalah production (CLI flag). Sebaliknya, jika hanya TF_VAR_ yang terisi, nilai env itulah yang dipakai.
Warning
Urutan ini sering membuat kejutan. Kesalahan paling umum di tim: terraform.tfvars berisi nilai untuk production, lalu seseorang menjalankan apply di laptop tanpa sadar memakai nilai tersebut. Pola aman yang dianjurkan banyak tim: file terraform.tfvars hanya berisi placeholder kosong, dan nilai sebenarnya di-inject via environment/CI sesuai environment.
Jika variable menerima nilai dari luar, maka locals menyimpan nilai yang dihitung atau didefinisikan di dalam konfigurasi. Fungsinya menghilangkan pengulangan (DRY — Don't Repeat Yourself): ekspresi yang sama ditulis sekali, lalu dipakai di banyak resource.
Contoh klasik: banyak resource memerlukan pola prefix-name-environment dan sekumpulan tag yang sama:
locals {
name_prefix = "${var.project_name}-${var.environment}"
common_tags = {
Name = local.name_prefix
Environment = var.environment
ManagedBy = "terraform"
}
}Lalu nilai tersebut dipakai di banyak tempat tanpa mengulang logikanya:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = merge(var.tags, local.common_tags)
}
resource "aws_s3_bucket" "artifacts" {
bucket = "${local.name_prefix}-artifacts"
tags = merge(var.tags, local.common_tags)
}Perhatikan dua hal:
local.name_prefix dan local.common_tags direferensikan dengan kata kunci local — dan kini kedua resource otomatis sinkron. Jika aturan penamaan berubah, kalian cukup mengubah satu tempat.merge() menggabungkan tag dari variabel input dengan tag bawaan — contoh kecil bagaimana function (akan kita dalami di episode 6) bekerja sama dengan locals.Caution
locals tidak bisa di-override dari luar (tidak seperti variable), dan tidak bisa diekspor ke modul lain (hanya output yang bisa). Jika sebuah nilai harus bisa berbeda antar environment, jadikan ia variable, bukan locals. Jika nilainya murni turunan dari variabel lain di dalam konfigurasi, pakai locals.
Setelah terraform apply selesai, bagaimana kalian mengetahui IP public instance yang baru dibuat, atau endpoint database? Dua pilihan: login ke console cloud (lambat dan rawan salah), atau — jauh lebih baik — deklarasikan output value.
output adalah mekanisme Terraform untuk menampilkan informasi penting pasca-apply dan mengekspos nilai untuk dikonsumsi modul lain. Sintaksnya sederhana:
output "instance_public_ip" {
description = "IP public server web"
value = aws_instance.web.public_ip
}
output "database_endpoint" {
description = "Endpoint koneksi database RDS"
value = aws_db_instance.main.endpoint
sensitive = true
}
output "website_url" {
description = "URL akses aplikasi"
value = "http://${aws_instance.web.public_ip}"
}Cara membacanya setelah apply:
terraform outputinstance_public_ip = "54.169.120.11"
website_url = "http://54.169.120.11"terraform output instance_public_ip
terraform output -jsonPoin penting:
value bisa berupa ekspresi, bukan sekadar atribut statis — lihat website_url yang merangkai string.sensitive = true menyembunyikan nilai dari output terminal dan log, untuk hal seperti kredensial database. Catatan: ini hanya menyembunyikan tampilan, bukan enkripsi penyimpanan — pembahasan keamanan state file akan kita dalami di episode 15.Untuk melihat gambaran besarnya, berikut konfigurasi lengkap yang sekarang bisa dipakai untuk staging maupun production hanya dengan mengubah nilai variabel:
variable "environment" {
type = string
description = "Environment deployment"
}
variable "instance_type" {
type = string
default = "t3.micro"
}locals {
name_prefix = "myapp-${var.environment}"
}resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = local.name_prefix
Environment = var.environment
}
}
output "instance_public_ip" {
value = aws_instance.web.public_ip
}Untuk environment berbeda, cukup gunakan file tfvars berbeda:
terraform apply -var-file="staging.tfvars"terraform apply -var-file="production.tfvars"Tip
Inilah esensi Infrastructure as Code yang baik: konfigurasi (kode) berbeda dari environment (data). Tim bisa men-review kode satu kali, sementara nilai environment dikelola terpisah — jauh lebih aman dan dapat diaudit daripada menyalin-paste file konfigurasi.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Lupa memberikan nilai variabel tanpa default | Terraform meminta input interaktif — menyangkut di CI/CD | Berikan default atau inject nilai lewat TF_VAR_/-var |
| Salah tipe data | Error Inappropriate value for attribute | Gunakan type yang tepat, misal number bukan string |
Lupa description | Kode sulit dipahami, dokumentasi modul tidak ter-generate | Selalu isi description |
Salah memakai variable vs locals | Nilai yang seharusnya konstan bisa berubah antar environment | locals untuk turunan internal, variable untuk input eksternal |
Nilai rahasia ditulis di terraform.tfvars yang ter-commit | Kebocoran kredensial ke repository | Masukkan ke .gitignore, pakai env variable |
Mengira sensitive mengenkripsi data | Nilai masih tersimpan plaintext di state | Pahami bahwa sensitive hanya menyembunyikan tampilan |
Referensi local/output salah ketik | Error Reference to undeclared | Verifikasi nama blok; jalankan terraform validate |
Pada episode 4 ini kita telah membahas tiga mekanisme yang membuat kode Terraform benar-benar hidup: input variables untuk menerima nilai dari luar (.tfvars, -var, TF_VAR_) dengan urutan prioritas yang jelas, local values untuk menghilangkan pengulangan ekspresi sesuai prinsip DRY, dan output values untuk menampilkan serta membagikan hasil kerja pasca-apply.
Kemampuan membedakan kapan memakai variable, locals, dan output adalah salah satu pembeda utama antara kode yang sekadar "berfungsi" dan kode yang "siap dipakai tim". Dengan fondasi ini, satu set konfigurasi kalian kini bisa melayani banyak environment tanpa mengorbankan keamanan.
Di episode 5 selanjutnya, kita akan membahas topik yang sering dianggap menakutkan namun wajib dikuasai: State Management (Local vs Remote State) — mengapa Terraform menyimpan file terraform.tfstate, mengapa menyimpannya secara lokal berbahaya bagi tim, dan bagaimana remote backend dengan state locking menyelamatkan tim dari bencana. Pastikan tetap semangat!