Idempotency #

Idempotency adalah properti di mana menjalankan operasi yang sama berkali-kali menghasilkan hasil yang sama seperti menjalankannya sekali. Di Terraform, ini berarti kamu bisa menjalankan terraform apply berulang kali tanpa khawatir akan membuat resource duplikat, mengubah sesuatu yang tidak perlu, atau merusak kondisi yang sudah benar.

Ini bukan kebetulan — idempotency adalah hasil desain Terraform yang disengaja melalui tiga mekanisme bekerja bersama: state, plan, dan refresh dari kondisi aktual. Memahaminya membantumu mengenali kapan konfigurasi atau penggunaan Terraform menyimpang dari prinsip ini.

Bagaimana Idempotency Bekerja #

flowchart TD
    A["terraform apply\n(dijalankan berkali-kali)"] --> B["1. Baca state\nApa yang sudah ada?"]
    B --> C["2. Baca konfigurasi\nApa yang seharusnya ada?"]
    C --> D["3. Refresh dari API\nApa kondisi aktual?"]
    D --> E{"Bandingkan:\nstate vs konfigurasi\nvs kondisi aktual"}

    E -->|"Sama semua\n(state ≈ config ≈ actual)"| F["✅ 'No changes'\nTidak ada operasi"]
    E -->|"Ada perbedaan"| G["🔄 Hanya perbedaan\nyang dieksekusi"]

    F --> H["Apply kedua, ketiga,\nkeempat...\nhasilnya SELALU sama"]
    G --> I["Perbaiki perbedaan\nState diupdate"]

    style F fill:#e8f5e9,stroke:#2e7d32
    style G fill:#e3f2fd,stroke:#1565c0
    style H fill:#e8f5e9,stroke:#2e7d32
CARA IDEMPOTENCY BEKERJA:

Terraform apply (pertama kali):
  State: kosong
  Konfigurasi: 3 resource
  Plan: +3 create
  Hasil: 3 resource dibuat, state diupdate

Terraform apply (kedua kali, tanpa perubahan):
  State: 3 resource
  Konfigurasi: 3 resource (sama)
  Refresh: kondisi aktual = state = konfigurasi
  Plan: "No changes. Your infrastructure matches the configuration."
  Hasil: tidak ada yang berubah ✅

Terraform apply (ketiga, keempat, kelima...):
  Sama dengan kedua kali — tidak ada perubahan ✅
LangkahMekanismeFungsi
1. Baca stateState fileMengetahui apa yang sudah ada di cloud
2. Baca konfigurasiFile .tfMengetahui apa yang seharusnya ada
3. RefreshAPI cloudMemverifikasi kondisi aktual di cloud
4. BandingkanDiff engineMenemukan perbedaan antara ketiganya
5. EksekusiApply engineHanya menjalankan perubahan yang diperlukan

Perbandingan dengan Pendekatan Non-Idempoten #

flowchart TD
    subgraph "Script Bash (Non-Idempoten)"
        BA["chmod +x deploy.sh\n& ./deploy.sh"] --> BB["Pertama kali:\nberhasil ✅"]
        BA --> BC["Kedua kali:\nERROR atau\nduplikat ❌"]
        BA --> BD["Butuh conditional logic:\nif ! exists, then create"]
    end

    subgraph "Terraform (Idempoten)"
        TA["terraform apply"] --> TB["Pertama kali:\nberhasil ✅"]
        TA --> TC["Kedua kali:\nNo changes ✅"]
        TA --> TD["Ketiga kali:\nNo changes ✅"]
    end

    style BB fill:#e8f5e9,stroke:#2e7d32
    style BC fill:#ffebee,stroke:#c62828
    style BD fill:#fff3e0,stroke:#e65100
    style TB fill:#e8f5e9,stroke:#2e7d32
    style TC fill:#e8f5e9,stroke:#2e7d32
    style TD fill:#e8f5e9,stroke:#2e7d32
# ❌ ANTI-PATTERN: Script bash — tidak idempoten
#!/bin/bash

# Jalankan pertama kali: berhasil
aws ec2 create-security-group \
  --group-name "web-sg" \
  --description "Web security group"

# Jalankan kedua kali: ERROR
# An error occurred (InvalidGroup.Duplicate):
# The security group 'web-sg' already exists

# Butuh conditional logic ekstra untuk membuatnya idempoten:
if ! aws ec2 describe-security-groups --group-names "web-sg" 2>/dev/null; then
  aws ec2 create-security-group --group-name "web-sg" ...
fi
# Dan ini masih tidak menangani race condition, update, dll.
# ✅ BENAR: Terraform — idempoten secara built-in
resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Web security group"
}
# Jalankan 100 kali → hasilnya SELALU sama
# Terraform tahu apakah security group sudah ada
# dan hanya bertindak jika ada perbedaan
AspekScript BashTerraform
Jalankan 2xError atau duplikat“No changes”
Deteksi existing resourceManual (if/else)Otomatis (state + refresh)
Update yang berubahManual (grep, diff)Otomatis (plan engine)
Handle race conditionSusahBuilt-in (state locking)
Kode tambahanBanyak conditional logicTidak perlu

Kasus yang Bisa Merusak Idempotency #

Meskipun Terraform dirancang untuk idempoten, ada beberapa pola yang bisa merusaknya. Kenali pola ini agar konfigurasimu tetap idempoten.

flowchart TD
    A["Kasus yang merusak\nidempotency"] --> B["timestamp() / uuid()\ndalam konfigurasi"]
    A --> C["Provisioner yang\nnon-idempoten"]
    A --> D["Resource dikelola\noleh dua tool"]
    A --> E["Nilai yang berubah\ntiap plan/apply"]

    B --> B1["Nilai berbeda setiap apply\n→ update setiap kali"]
    C --> C1["Script diulang setiap\nresource di-replace"]
    D --> D1["Tool A ubah, Terraform revert\nTool A ubah lagi, dst"]
    E --> E1["random_id, random_password\ntanpa lifecycle ignore"]

    B1 --> F["Solusi: Gunakan\nnilai stabil atau\nlifecycle ignore_changes"]
    C1 --> F
    D1 --> G["Solusi: Satu tool\nsaja yang manage"]
    E1 --> F

    style B fill:#ffebee,stroke:#c62828
    style C fill:#ffebee,stroke:#c62828
    style D fill:#ffebee,stroke:#c62828
    style E fill:#ffebee,stroke:#c62828
    style F fill:#e8f5e9,stroke:#2e7d32
    style G fill:#e8f5e9,stroke:#2e7d32

Kasus 1: timestamp() atau uuid() dalam Konfigurasi #

# ❌ ANTI-PATTERN: Setiap apply menghasilkan nilai berbeda
resource "aws_s3_bucket_object" "config" {
  bucket  = aws_s3_bucket.main.id
  key     = "config.json"
  content = jsonencode({
    generated_at = timestamp()  # ✗ Berbeda setiap apply!
    # Ini menyebabkan resource diupdate SETIAP kali apply dijalankan
  })
}

# ✅ BENAR: Gunakan nilai yang stabil
resource "aws_s3_bucket_object" "config" {
  bucket  = aws_s3_bucket.main.id
  key     = "config.json"
  content = jsonencode({
    environment = var.environment    # Hanya berubah jika variable berubah
    version     = var.app_version    # Hanya berubah jika variable berubah
  })
}

Kasus 2: Provisioner yang Tidak Idempoten #

# ❌ ANTI-PATTERN: Provisioner dijalankan setiap kali resource "baru"
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.micro"

  provisioner "remote-exec" {
    inline = [
      "echo 'Server $(hostname) started at $(date)' >> /var/log/deploy.log"
      # timestamp di sini akan berbeda setiap apply
    ]
  }
}

# ✅ BENAR: Gunakan user_data untuk konfigurasi initial
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.micro"
  user_data     = file("scripts/setup.sh")  # Dijalankan hanya sekali saat launch
}

Kasus 3: Resource Dikelola oleh Dua Tool #

flowchart LR
    A["Terraform\nmanage resource"] -->|"Update ke\ndesired state"| B["Cloud Resource"]
    C["Tool lain\n(kubectl, console)"] -->|"Update ke\nstate berbeda"| B

    B -->|"Terraform detect\ndrift"| A
    B -->|"Tool lain detect\ndrift"| C

    A --> D["Infinite loop:\nTerraform ↔ Tool lain\nsaling menimpa"]

    style D fill:#ffebee,stroke:#c62828
KasusPenyebabSolusi
timestamp() / uuid()Nilai berbeda setiap applyGunakan nilai stabil atau ignore_changes
Provisioner non-idempotenScript diulang setiap replaceGunakan user_data atau Ansible
Dual managementDua tool manage resource samaSatu tool saja yang manage
random_id / random_passwordNilai regenerasilifecycle { ignore_changes = [...] }
file() pada file yang berubahFile konten berubahPastikan file stabil atau pakai filemd5

Memverifikasi Idempotency Konfigurasimu #

Ada cara sederhana untuk memverifikasi bahwa konfigurasimu benar-benar idempoten.

flowchart TD
    A["Tes Idempotency"] --> B["1. terraform apply\n(pertama kali)"]
    B --> C["2. terraform plan\n(tanpa ubah config)"]
    C --> D{"Plan menunjukkan\nperubahan?"}

    D -->|"Tidak: No changes"| E["✅ Idempoten!\nKonfigurasi aman"]
    D -->|"Ya: ada perubahan"| F["❌ Tidak idempoten!\nCari penyebab"]

    F --> G["Cek timestamp(),\nuuid(), random value,\natau external changes"]

    style E fill:#e8f5e9,stroke:#2e7d32
    style F fill:#ffebee,stroke:#c62828
# Tes idempotency manual:

# 1. Apply pertama kali
terraform apply -auto-approve

# 2. Cek tanpa mengubah apapun (lebih cepat dari apply)
terraform plan

# 3. Cek output
# Jika idempoten:
#   "No changes. Your infrastructure matches the configuration."
#
# Jika TIDAK idempoten:
#   Plan: 0 to add, 1 to change, 0 to destroy.
#   → Ada perubahan yang muncul tanpa kamu mengubah config
#   → Cari penyebab: timestamp, random value, atau external change

# Alternatif: Apply kedua kali untuk verifikasi
terraform apply -auto-approve
# Output yang diinginkan:
# Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
LangkahPerintahHasil IdempotenHasil Tidak Idempoten
1. Apply pertamaterraform applyResource dibuatResource dibuat
2. Cekterraform plan“No changes”Ada perubahan
3. Apply keduaterraform apply0 added, 0 changedAda change yang tidak diharapkan

Idempotency dan Immutable Infrastructure #

Idempotency bukan berarti resource tidak pernah berubah — ia berarti hasil dari menjalankan konfigurasi yang sama selalu konsisten. Ini sejalan dengan prinsip immutable infrastructure.

flowchart TD
    subgraph "Mutable (Tradisional)"
        M1["Server dibuat"] --> M2["Dikonfigurasi ulang\nberulang kali"]
        M2 --> M3["Kondisi berubah\ntanpa jejak"]
        M3 --> M4["'Configuration drift'\nSulit diprediksi"]
    end

    subgraph "Immutable (Modern)"
        I1["Server dibuat\ndengan config A"] --> I2["Butuh config B?"]
        I2 --> I3["Server lama dihapus\nServer baru dibuat\ndengan config B"]
        I3 --> I4["Kondisi SELALU\nbisa diprediksi"]
    end

    style M4 fill:#ffebee,stroke:#c62828
    style I4 fill:#e8f5e9,stroke:#2e7d32
AspekMutableImmutable
UpdateUbah di tempatHapus + buat baru
State trackingSulitMudah (selalu fresh)
Rollout riskPerubahan tersembunyiJelas terlihat
Terraform actionupdate in-place (~)destroy and recreate (-/+)
IdempotencyPerlu effort ekstraNatural
TERRAFORM MENDUKUNG KEDUANYA:
  - Update in-place (~): mutable, tapi terkontrol
  - Destroy and recreate (-/+): immutable approach
  - Pilihan tergantung pada jenis resource dan kebutuhan
  - Lifecycle create_before_destroy: transisi yang lebih aman

Ringkasan #

  • Idempotency Terraform bersifat built-in melalui tiga mekanisme: state, plan, dan refresh dari kondisi aktual.
  • “No changes” di plan/apply kedua adalah tanda bahwa konfigurasimu idempoten — ini adalah kondisi yang diinginkan.
  • Hindari timestamp(), uuid(), atau random value dalam konfigurasi resource — ini merusak idempotency karena nilainya berubah setiap apply.
  • Provisioner tidak idempoten secara natural — pertimbangkan user_data atau Ansible sebagai alternatif.
  • Satu tool per resource — jangan kelola resource yang sama dengan Terraform dan tool lain secara bersamaan.
  • Tes idempotency dengan menjalankan terraform plan setelah apply pertama — jika masih ada perubahan, ada sesuatu yang perlu diperbaiki.
  • Idempotency adalah fondasi kepercayaan terhadap konfigurasi Terraform — memungkinkan safe re-run di CI/CD, safe retry setelah kegagalan, dan safe collaboration antar tim.

← Sebelumnya: Drift Detection   Berikutnya: Apa itu Resource? →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact