Di episode ini kita akan membedah file terraform.tfstate yang menjadi sumber kebenaran Terraform, bahaya menyimpan state secara lokal bagi tim, serta bagaimana remote backend dengan state locking (S3 + DynamoDB, GCS) mencegah eksekusi bersamaan yang merusak infrastruktur.

Setelah di episode 4 sebelumnya kita membahas bagaimana input variables membuat konfigurasi fleksibel antar environment, local values menghilangkan pengulangan ekspresi, dan output values membagikan hasil kerja — pada episode kali ini kita akan membahas topik yang paling sering disalahpahami namun paling menentukan kestabilan tim: state management.
Semua contoh di episode-episode sebelumnya, tanpa kalian sadari, sedang membangun sebuah file bernama terraform.tfstate setiap kali terraform apply berjalan. File inilah yang membuat Terraform tahu bahwa instance i-0abcd1234efgh5678 di AWS "milik" resource aws_instance.web dalam kode kalian. Tanpa file ini, Terraform akan bingung: apakah harus membuat instance baru? atau instance yang sudah ada ini sudah terkelola?
Kenapa topik ini krusial di dunia kerja nyata? Karena mayoritas insiden IaC terjadi bukan karena sintaks salah, melainkan karena state: file state hilang sehingga tim terpaksa membangun ulang seluruh infrastruktur; file state bentrok karena dua orang menjalankan terraform apply bersamaan; atau file state bocor karena ter-commit ke Git bersama password database di dalamnya. Memahami state — apa isinya, bagaimana mengamankannya, dan bagaimana menyimpannya secara remote — adalah syarat mutlak sebelum kalian menyentuh infrastruktur tim mana pun.
Terraform bersifat declarative: kalian menulis kondisi akhir yang diinginkan, dan Terraform menentukan langkah untuk sampai ke sana. Tapi untuk itu, Terraform harus tahu kondisi saat ini. Sumber pengetahuan itulah state file.
Bayangkan state seperti buku besar (ledger) akuntansi atau kuitansi pembelian. Kode HCL kalian adalah "rencana belanja", sedangkan state adalah bukti "sudah dibeli apa saja, dengan ID transaksi berapa". Jika kalian membeli barang yang sama dua kali tanpa mencatat, hasilnya duplikat dan boros. Persis seperti itu: tanpa state, setiap terraform apply akan membuat EC2 instance baru lagi — karena Terraform tidak tahu bahwa instance yang dimaksud sudah pernah dibuat.
Secara teknis, state menyimpan mapping antara kode HCL dan ID riil resource di cloud:
| Lapisan | Isi | Contoh |
|---|---|---|
| Kode HCL | resource "aws_instance" "web" | nama logis |
| State | pasangan logical → real ID | aws_instance.web → i-0abcd1234efgh5678 |
| Cloud | objek nyata | EC2 instance dengan ID i-0abcd1234efgh5678 |
Mapping ini adalah alasan Terraform bisa melakukan tiga hal penting: detect drift (membandingkan kondisi cloud vs state), plan yang akurat (hanya mengubah resource yang berubah), dan destroy yang tepat sasaran (menghapus resource yang benar, bukan menebak).
File terraform.tfstate berformat JSON. Kalian tidak perlu membaca manual untuk bekerja sehari-hari, tapi memahami strukturnya membantu menghilangkan "rasa takut" terhadap file ini:
{
"version": 4,
"terraform_version": "1.9.5",
"resources": [
{
"module": "",
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-0abcd1234efgh5678",
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t3.micro",
"tags": {
"Name": "web-server"
}
}
}
]
}
]
}Perhatikan korespondensinya dengan kode HCL: blok "type": "aws_instance" + "name": "web" persis mencerminkan resource "aws_instance" "web", dan "id" adalah jembatan menuju objek nyata di AWS. File ini juga menyimpan seluruh atribut resource — termasuk yang mungkin sensitif.
Warning
State file menyimpan nilai sensitif dalam bentuk plaintext. Password database, private key SSH, dan secret lainnya yang lewat sebagai atribut resource akan tertulis mentah-mentah di file ini. Ini yang membuat penyimpanan dan akses state menjadi masalah keamanan, bukan sekadar masalah teknis.
Saat kalian bekerja sendirian, state lokal di folder proyek terasa nyaman. Namun begitu tim mulai berkolaborasi, terraform.tfstate lokal menjadi bom waktu. Ada tiga masalah utama:
| Masalah | Deskripsi | Dampak Nyata |
|---|---|---|
| Kebocoran data sensitif | State berisi plaintext secret; mudah ter-commit ke Git | Kredensial produksi tersebar ke seluruh developer |
| Kehilangan file state | File hilang/corrupt (laptop rusak, .gitignore salah, volume terhapus) | Terraform kehilangan mapping; resource jadi orphan yang tidak bisa di-manage |
| State conflict / bentrokan | Dua orang (atau lebih) menjalankan apply bersamaan dengan state yang sama | Last-write-wins: perubahan satu orang diam-diam menimpa yang lain |
Mari kita bedah masalah ketiga lebih dalam dengan sebuah skenario nyata:
Important
Skenario state conflict: Developer A dan Developer B sama-sama menyalin terraform.tfstate dari repo. A menambahkan security group dan meng-apply (state-nya kini punya 2 resource). Tanpa tahu, B yang punya state lama (1 resource) menambahkan instance baru dan meng-apply. Hasilnya: security group milik A "hilang" dari state — Terraform tidak lagi menganggapnya terkelola, padahal objeknya masih hidup di cloud dan tetap ditagih. Inilah yang disebut state conflict: perubahan yang tidak pernah disengaja hilang, dan infrastruktur perlahan-lahan menjadi drift tak terkendali.
Itulah mengapa tim engineering profesional tidak pernah menyimpan state secara lokal. Solusinya adalah remote state backend.
Backend adalah tempat Terraform menyimpan state secara terpusat. Dengan backend, state tinggal di satu lokasi bersama (misal S3 bucket), sehingga semua anggota tim membaca state yang sama — dan lebih penting lagi, konsisten.
Namun menyimpan state di satu tempat saja belum cukup. Masih ada celah: dua orang bisa membaca state yang sama lalu meng-apply bersamaan. Untuk itu ada state locking — mekanisme kunci yang memastikan hanya satu terraform apply/plan yang memegang akses state dalam satu waktu.
Bayangkan seperti toilet umum yang dikunci: orang kedua yang mencoba masuk saat masih ada yang di dalam akan menunggu (atau langsung ditolak dengan pesan error), bukan masuk dan mengacak-acak. Tanpa kunci, dua orang bisa masuk bersamaan dan membuat kekacauan.
Konfigurasi backend paling populer di ekosistem AWS adalah kombinasi S3 (untuk menyimpan state) + DynamoDB (untuk state lock):
terraform {
backend "s3" {
bucket = "myapp-tfstate-bucket"
key = "production/network/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "myapp-tfstate-lock"
}
}Penjelasan atribut:
bucket + key — lokasi objek state. Pola key yang rapi memisahkan environment dan komponen, misal production/network/ vs staging/app/.encrypt = true — memaksa enkripsi server-side pada S3 (satu lapis pengaman tambahan untuk data sensitif di state).dynamodb_table — nama tabel DynamoDB yang berfungsi sebagai lock table. Terraform membuat lock item di tabel ini saat operasi berjalan dan menghapusnya setelah selesai.Caution
Menambahkan backend ke proyek yang sebelumnya memakai state lokal bukan tanpa konsekuensi: saat kalian menjalankan terraform init pertama kali dengan backend, Terraform akan bertanya apakah state lokal perlu dimigrasikan ke remote. Jawab yes untuk memindahkan state lama agar resource yang ada tetap terkelola, lalu hapus state lokal dan pastikan ia tidak pernah lagi masuk Git.
Untuk tim yang berada di ekosistem Google Cloud, backend GCS adalah pilihan utamanya:
terraform {
backend "gcs" {
bucket = "myapp-tfstate-bucket"
prefix = "production/network"
}
}GCS menangani state locking secara native melalui object versioning — tidak perlu tabel DynamoDB terpisah seperti di AWS. Cukup aktifkan Object Versioning pada bucket, dan Terraform memanfaatkan preconditions GCS sebagai mekanisme kunci.
| Backend | Penyimpanan State | State Locking |
|---|---|---|
| AWS S3 | S3 bucket | DynamoDB (manual, dynamodb_table) |
| Google Cloud Storage | GCS bucket | Native (object versioning/preconditions) |
| Azure Blob Storage | Storage account / container | Native (Azure blob lease) |
| Terraform Cloud (HCP) | Cloud storage terkelola | Native |
Berikut perbandingan ringkas untuk memperkuat keputusan arsitektur:
| Aspek | Local State | Remote State |
|---|---|---|
| Lokasi penyimpanan | Folder proyek di laptop | Cloud storage terpusat |
| Berbagi antar tim | Tidak bisa (harus salin manual) | Otomatis, semua baca state yang sama |
| State locking | Tidak ada | Ada (DynamoDB / native) |
| Risiko kehilangan | Tinggi (laptop/disk rusak) | Rendah (managed, bisa versioning) |
| Enkripsi data | Tidak ada | At-rest (S3 SSE, GCS) |
| Akses terpusat & audit | Tidak ada | Bisa via IAM strict |
| Cocok untuk | Latihan lokal / solo | Tim engineering / production |
Note
Tidak semua backend menyediakan locking yang memadai. Jika memakai backend yang tidak mendukung locking (misal HTTP backend sederhana), secara teknis kalian masih bisa kena state conflict. Pastikan backend pilihan tim mendukung locking, atau tambahkan lapisan lock manual — ini salah satu alasan S3+DynamoDB tetap menjadi standar de facto.
state list dan state showMeski state file sebaiknya tidak dibaca manual, Terraform menyediakan perintah untuk mengintrospeksinya dengan aman:
terraform state listaws_instance.web
aws_s3_bucket.artifacts
data.aws_ami.ubuntuterraform state show aws_instance.web# aws_instance.web:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
arn = "arn:aws:ec2:ap-southeast-1:123456789012:instance/i-0abcd1234efgh5678"
id = "i-0abcd1234efgh5678"
instance_type = "t3.micro"
tags = {
"Name" = "web-server"
}
...
}Kedua perintah ini sangat berguna saat memverifikasi bahwa mapping di state memang benar sebelum melakukan operasi seperti terraform destroy atau refactoring (akan dibahas lebih lanjut di episode 8).
Karena state berisi data sensitif dan bersifat machine-specific, aturannya sederhana: jangan pernah meng-commit terraform.tfstate, terraform.tfstate.backup, ataupun *.tfstate ke Git. Tambahkan ke .gitignore:
# Terraform state (local, karena remote backend sudah dipakai)
*.tfstate
*.tfstate.backup
*.tfstate.lock.info
# Secret yang sering tertukar dengan state
*.tfvarsWarning
Jika state terlanjur ter-commit ke Git (bahkan di commit lama), itu sudah termasuk insiden keamanan — nilai sensitif di dalamnya harus dianggap bocor dan dirotasi. Jangan hanya menghapus file di commit terbaru; bersihkan history dengan tools seperti git filter-repo dan rotasi semua kredensial yang terdampak.
| Kesalahan | Gejala | Solusi |
|---|---|---|
| State lokal + kerja tim | Resource tiba-tiba "hilang" dari state (state conflict) | Segera migrasi ke remote backend |
| Meng-commit state ke Git | Kredensial plaintext bocor ke repository | .gitignore + rotasi secret + bersihkan history |
Backend ditambahkan tanpa -migrate-state | Terraform menganggap state kosong → duplikat resource | Jalankan terraform init -migrate-state |
| S3 tanpa DynamoDB lock | Eksekusi bersamaan tetap bisa korup state | Tambahkan dynamodb_table + buat tabel |
| Bucket S3 tanpa versioning | State yang ter-overwrite tidak bisa dipulihkan | Aktifkan versioning & pertimbangkan backup berkala |
| IAM terlalu terbuka ke bucket state | Developer bisa membaca/menghapus state | Terapkan IAM strict + MFA untuk operasi destruktif |
| Backend berubah mendadak | Error backend changed saat init | Gunakan -reconfigure atau -migrate-state sesuai kebutuhan |
Pada episode 5 ini kita telah membahas state management secara menyeluruh: mengapa Terraform membutuhkan file terraform.tfstate sebagai mapping antara kode HCL dan ID riil di cloud, struktur JSON di baliknya, tiga bahaya utama state lokal (kebocoran data, kehilangan file, dan state conflict), serta bagaimana remote backend dengan state locking (S3+DynamoDB, GCS, Azure Blob) mengatasi semua bahaya tersebut.
Inti dari episode ini adalah satu keyakinan: state adalah aset paling berharga dalam proyek Terraform tim. Ia lebih berharga daripada kode — karena kode bisa ditulis ulang, sedangkan mapping antara kode dan resource yang sudah dibayar tidak bisa di-tebak ulang.
Di episode 6 selanjutnya, kita akan melengkapi gudang senjata ekspresi kalian dengan Advanced Expressions, Functions & Loops — mengenal built-in functions, conditional expression, serta meta-arguments count dan for_each untuk membuat banyak resource dari satu blok kode. Pastikan tetap semangat!