Anti-Pattern #
State adalah komponen Terraform yang paling mudah dirusak dan paling sulit diperbaiki. Kesalahan dalam pengelolaan state sering kali tidak terlihat dampaknya secara langsung, tapi menumpuk menjadi masalah besar ketika sudah terlambat ā drift yang tidak terdeteksi, resource orphan yang membuang biaya, atau state corrupt yang membuat seluruh infrastruktur tidak bisa dikelola. Artikel ini mengompilasi anti-pattern yang paling sering ditemukan di lapangan.
flowchart LR
A["š Edit State\nSecara Manual"] -->|mengakibatkan| B["š„ State\nCorrupt"]
C["š Tidak Ada\nLocking"] -->|mengakibatkan| D["ā ļø Race\nCondition"]
E["š State\ndi Git"] -->|mengakibatkan| F["š Secret\nBocor"]
style A fill:#e74c3c,stroke:#c0392b,color:#fff
style B fill:#c0392b,stroke:#96281b,color:#fff
style C fill:#e67e22,stroke:#d35400,color:#fff
style D fill:#d35400,stroke:#a04000,color:#fff
style E fill:#f1c40f,stroke:#d4ac0d,color:#000
style F fill:#d4ac0d,stroke:#b7950b,color:#000Anti-Pattern 1: State di Git #
Ini adalah anti-pattern yang paling berbahaya dan masih sangat umum ditemukan, terutama di tim yang baru mulai menggunakan Terraform.
# ANTI-PATTERN: Commit terraform.tfstate ke Git
git add terraform.tfstate
git commit -m "update state"
git push
# Masalah yang ditimbulkan:
# 1. SECRET BOCOR
# State bisa mengandung password, API key, private key dalam plaintext.
# Siapapun dengan akses repo bisa membacanya.
# Bahkan setelah dihapus dari commit terbaru, secret masih ada di Git history.
# 2. MERGE CONFLICT YANG TIDAK BISA DISELESAIKAN
# State file adalah JSON yang di-generate otomatis.
# Merge conflict di state tidak bisa diselesaikan secara manual
# tanpa risiko merusak struktur JSON yang diharapkan Terraform.
# 3. TIDAK ADA LOCKING
# Git tidak menyediakan mekanisme locking untuk mencegah
# dua orang push state secara bersamaan.
# BENAR: Gunakan remote backend dan exclude state dari Git
echo "*.tfstate" >> .gitignore
echo "*.tfstate.backup" >> .gitignore
Anti-Pattern 2: Edit State File Secara Manual #
State file adalah file JSON, dan text editor bisa membukanya. Tapi mengeditnya secara manual adalah resep untuk state corrupt.
# ANTI-PATTERN: Edit terraform.tfstate dengan text editor
vim terraform.tfstate # ā Jangan pernah lakukan ini
# Risiko:
# - Typo yang merusak struktur JSON ā state tidak bisa dibaca
# - Lupa update "serial" ā Terraform menolak state karena dianggap stale
# - Referensi yang tidak konsisten antar resource
# BENAR: Gunakan subcommand terraform state untuk semua operasi
terraform state list # Lihat semua resource
terraform state show aws_instance.web # Lihat detail resource
terraform state rm aws_instance.web # Hapus dari state (tanpa destroy)
terraform state mv aws_instance.web aws_instance.web_server # Rename
terraform state pull > backup.tfstate # Backup state
terraform state push modified.tfstate # Push state (hati-hati)
Anti-Pattern 3: State Monolitik untuk Infrastruktur Besar #
Menyimpan seluruh infrastruktur ā production, staging, networking, compute, database ā dalam satu state file adalah pola yang tidak skalabel.
MASALAH STATE MONOLITIK:
Performance:
Plan harus query SEMUA resource ke provider API
ā Semakin banyak resource, semakin lambat plan
ā 200+ resource bisa butuh 5-10 menit hanya untuk plan
Blast Radius:
Satu apply yang gagal di tengah jalan bisa mempengaruhi
semua resource ā networking, compute, database sekaligus
ā Risiko lebih tinggi, dampak lebih luas
Kolaborasi:
Hanya satu orang yang bisa apply pada satu waktu
(karena locking)
ā Bottleneck di tim yang aktif
Keamanan:
Semua orang butuh akses ke satu state
ā Tidak bisa membatasi akses per komponen
SOLUSI: Pecah berdasarkan layer atau komponen
state/networking/ ā VPC, subnet, routing
state/compute/ ā EC2, ASG, load balancer
state/database/ ā RDS, ElastiCache
state/security/ ā IAM, security groups
flowchart TD
subgraph MONO["ā State Monolitik"]
direction TB
M1["šļø Satu State File\nuntuk Semua"]
M2["Networking\n(VPC, Subnet)"]
M3["Compute\n(EC2, ASG)"]
M4["Database\n(RDS)"]
M5["Security\n(IAM, SG)"]
M1 --- M2
M1 --- M3
M1 --- M4
M1 --- M5
end
subgraph SPLIT["ā
State Terpisah"]
direction LR
S1["state/networking/\nš Team Jaringan"]
S2["state/compute/\nš Team Backend"]
S3["state/database/\nš Team DBA"]
S4["state/security/\nš Team Security"]
end
MONO -->|"Pecah per\nlayer/komponen"| SPLIT
style MONO fill:#ffebee,stroke:#c62828
style SPLIT fill:#e8f5e9,stroke:#2e7d32
style S1 fill:#e3f2fd,stroke:#1565c0
style S2 fill:#fff3e0,stroke:#e65100
style S3 fill:#f3e5f5,stroke:#6a1b9a
style S4 fill:#fce4ec,stroke:#c62828Anti-Pattern 4: Tidak Ada State Locking #
Menjalankan Terraform tanpa state locking di lingkungan tim adalah bom waktu.
# ANTI-PATTERN: Backend S3 tanpa DynamoDB untuk locking
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
# dynamodb_table tidak dikonfigurasi ā TIDAK ADA LOCKING
}
}
# Konsekuensi:
# Developer A dan B apply bersamaan
# ā Keduanya baca state lama
# ā Keduanya tulis state baru yang berbeda
# ā Salah satu menimpa yang lain ā state tidak konsisten
# BENAR: Selalu konfigurasi locking
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "terraform-state-lock" # ā Wajib ada
}
}
Anti-Pattern 5: Mengabaikan Resource Orphan #
Resource orphan adalah resource yang ada di cloud tapi tidak ada di state Terraform ā biasanya karena dibuat di luar Terraform, atau karena state pernah dimodifikasi secara tidak tepat.
# Cara mendeteksi resource orphan:
# 1. List semua resource di state
terraform state list > state-resources.txt
# 2. List semua resource aktual di cloud (contoh untuk AWS)
aws ec2 describe-instances \
--query 'Reservations[].Instances[?State.Name==`running`].InstanceId' \
--output text > cloud-resources.txt
# 3. Bandingkan ā resource di cloud tapi tidak di state = orphan
# Dampak resource orphan:
# - Membuang biaya karena tidak terdeteksi dan dibiarkan berjalan
# - Tidak ada governance ā siapapun bisa membuat resource tanpa review
# - Drift yang tidak terlihat ā state tidak mencerminkan realita
# SOLUSI: Import resource orphan ke Terraform
terraform import aws_instance.recovered_server i-0abcdef1234567890
Anti-Pattern 6: Sensitive Values dalam Output yang Tidak Dilindungi #
State menyimpan semua output values ā termasuk yang sensitif ā dalam plaintext jika tidak ditandai dengan benar.
# ANTI-PATTERN: Output sensitif tanpa tanda sensitive = true
output "database_password" {
value = aws_db_instance.main.password
# Password tersimpan plaintext di state dan tampil di terminal
}
# BENAR: Tandai output sensitif
output "database_password" {
value = aws_db_instance.main.password
sensitive = true
# Tersembunyi di output terminal
# Tapi tetap tersimpan di state ā pastikan state file diamankan
}
Checklist State Management yang Sehat #
STATE MANAGEMENT CHECKLIST:
STORAGE:
ā” State disimpan di remote backend (S3, GCS, Terraform Cloud)
ā” Enkripsi at rest diaktifkan
ā” Versioning diaktifkan di S3 bucket
LOCKING:
ā” State locking dikonfigurasi (DynamoDB untuk S3)
ā” CI/CD pipeline menggunakan concurrency control
KEAMANAN:
ā” terraform.tfstate tidak ada di Git repository
ā” *.tfstate ada di .gitignore
ā” Akses ke state dibatasi dengan IAM policy
ā” Output sensitif ditandai dengan sensitive = true
ORGANISASI:
ā” State dipecah per environment (dev/staging/production)
ā” State dipecah per komponen jika infrastruktur besar
ā” Tidak ada resource orphan yang tidak terkelola
OPERASIONAL:
ā” Backup state sebelum operasi berisiko
ā” Tidak ada edit manual pada file state
ā” Semua operasi state melalui terraform state subcommand
Anti-Pattern 7: State Drift yang Diabaikan #
State drift terjadi ketika kondisi aktual di cloud berbeda dengan apa yang tercatat di state. Drift bisa terjadi karena perubahan manual di console, auto-scaling, atau bug di provider. Mengabaikan drift adalah resep untuk insiden production.
flowchart TD
A["State Terraform\n(instance_type: t3.micro)"] --> B{"Drift?"}
B -->|"Tidak ada drift"| C["terraform plan\n= No changes ā
"]
B -->|"Ada drift"| D["Cloud Reality\n(instance_type: t3.large)"]
D --> E["terraform plan\ndeteksi perubahan"]
D --> F["Drift diabaikan ā"]
F --> G["State makin tidak\nkonsisten"]
G --> H["Insiden Production š„"]
style A fill:#e3f2fd,stroke:#1565c0
style D fill:#fff3e0,stroke:#e65100
style F fill:#ffebee,stroke:#c62828
style H fill:#c0392b,stroke:#96281b,color:#fff# Deteksi drift dengan refresh-only plan
terraform plan -refresh-only
# Membandingkan state dengan realita cloud TANPA membuat rencana perubahan
# Output menunjukkan apa yang berbeda
# Contoh output drift detection:
# aws_instance.web have changed
# ~ instance_type: "t3.micro" => "t3.large"
# Artinya: seseorang mengubah instance type di AWS Console
# Opsi penanganan drift:
# 1. Terima perubahan (update state untuk mencerminkan realita)
terraform apply -refresh-only
# 2. Kembalikan ke konfigurasi (overwrite perubahan manual)
terraform apply # Akan mengembalikan ke t3.micro
# Automasi drift detection di CI/CD (jalankan setiap jam/jadwal)
Anti-Pattern 8: terraform state push yang Sembrono
#
terraform state push adalah perintah yang paling berbahaya di seluruh Terraform CLI. Ia bisa menimpa state aktif tanpa validasi apapun.
# ANTI-PATTERN: Push state tanpa validasi
terraform state push backup.tfstate
# Langsung menimpa state aktif ā tidak ada konfirmasi
# Jika backup sudah usang, resource yang baru dibuat hilang dari state
# RISIKO:
# - State lama menimpa state baru ā resource baru menjadi orphan
# - State dari environment lain ter-push ke production
# - Serial conflict tidak dicek dengan benar
# YANG LEBIH BAHAYA: Push dengan -force
terraform state push -force backup.tfstate
# Mengabaikan semua safety check termasuk serial dan lineage
# Bisa memaksakan state dari workspace lain ke workspace saat ini
# BENAR: Hanya gunakan state push setelah verifikasi ketat
# 1. Backup state saat ini
terraform state pull > current-state-backup.tfstate
# 2. Verifikasi backup yang akan di-push masih relevan
cat backup.tfstate | jq '.serial'
cat current-state-backup.tfstate | jq '.serial'
# Pastikan serial backup > serial saat ini ATAU kamu sengaja rollback
# 3. Verifikasi lineage sama
cat backup.tfstate | jq '.lineage'
cat current-state-backup.tfstate | jq '.lineage'
# Lineage HARUS sama ā beda lineage = beda workspace
# 4. Push dengan verbose output
terraform state push backup.tfstate
# Verifikasi: terraform plan harusnya "No changes"
Anti-Pattern 9: Menggunakan -target Sebagai Solusi Rutin
#
-target memang powerful untuk mengatasi masalah spesifik, tapi menggunakannya secara rutin menciptakan state yang tidak konsisten.
# ANTI-PATTERN: Selalu menggunakan -target untuk apply
terraform apply -target=aws_instance.web
# Hanya apply satu resource ā resource lain yang berubah diabaikan
# State sekarang hanya ter-update untuk resource yang di-target
# Resource lain yang seharusnya berubah tetap di state lama
# Dampak kumulatif jika dilakukan berulang:
# State campuran: beberapa resource ter-update, beberapa tidak
# terraform plan terus menunjukkan perubahan yang tidak pernah selesai
# Drift antara state dan konfigurasi semakin besar
# BENAR: Gunakan -target hanya untuk:
# 1. Debugging ā ingin lihat plan untuk resource spesifik
terraform plan -target=aws_instance.web
# 2. Recovery ā resource spesifik perlu di-apply duluan karena dependency
terraform apply -target=aws_security_group.web_sg
terraform apply # Apply sisanya setelah dependency terpenuhi
# 3. BUKAN sebagai cara menghindari error di resource lain
# ā Fix error-nya, jangan skip dengan -target
Ringkasan #
- State di Git adalah anti-pattern paling berbahaya ā secret bocor, tidak ada locking, merge conflict yang tidak bisa diselesaikan.
- Jangan edit state secara manual ā gunakan selalu
terraform statesubcommand untuk semua operasi.- State monolitik tidak skalabel ā plan lambat, blast radius besar, kolaborasi terhambat. Pecah berdasarkan layer atau komponen.
- Locking bukan opsional ā tanpa DynamoDB table di backend S3, race condition dan state corrupt hanya soal waktu.
- Resource orphan membuang biaya dan menciptakan drift yang tidak terdeteksi ā audit secara berkala dan import yang ditemukan.
- Tandai output sensitif dengan
sensitive = trueā meskipun tetap tersimpan di state, setidaknya tidak tampil di terminal.- State drift harus dipantau aktif ā gunakan
terraform plan -refresh-onlysecara terjadwal untuk mendeteksi perubahan manual di cloud.terraform state pushadalah perintah paling berbahaya ā selalu verifikasi serial, lineage, dan backup sebelum push.- Jangan gunakan
-targetsebagai solusi rutin ā gunakan hanya untuk debugging dan recovery, bukan untuk menghindari error.