Plan #

terraform plan adalah safety net utama di Terraform. Sebelum satu pun resource berubah di cloud, kamu punya kesempatan untuk melihat persis apa yang akan terjadi. Tapi output plan bukan sekadar daftar yang harus di-scroll — ada detail penting yang perlu kamu pahami untuk bisa membaca plan secara kritis, bukan hanya menerimanya begitu saja.

Cara Plan Bekerja #

Sebelum menghasilkan output, terraform plan melakukan serangkaian operasi internal. Memahami urutan ini membantu kamu tahu mengapa plan kadang menampilkan perubahan yang tidak kamu harapkan.

flowchart TD
    A["terraform plan"] --> B["1. Baca semua file .tf\nParse konfigurasi HCL"]
    B --> C["2. Refresh state\nQuery aktual ke provider API"]
    C --> D["3. Bandingkan konfigurasi vs state\nIdentifikasi perbedaan"]
    D --> E["4. Hitung dependency graph\nTentukan urutan operasi"]
    E --> F["5. Tampilkan execution plan\nPerubahan apa yang akan dilakukan"]

    C -.->|"Perubahan manual\ndi cloud"| G["Tercatat sebagai\nperubahan di plan"]
    D -.->|"Ada perubahan?"| H["Ya → tampilkan\nTidak → no changes"]

    style A fill:#e3f2fd,stroke:#1565c0
    style F fill:#e8f5e9,stroke:#2e7d32
    style G fill:#fff3e0,stroke:#e65100
LangkahApa yang DilakukanOutputKenapa Penting
1. Baca configParse semua .tf fileRepresentasi internal konfigurasiSyntax error akan terdeteksi di sini
2. Refresh stateQuery ke API providerData aktual resource di cloudMencerminkan kondisi nyata, bukan hanya state lama
3. BandingkanConfig vs state aktualDaftar perbedaanMenangkap perubahan manual di cloud
4. Dependency graphHitung urutan ketergantunganUrutan operasiResource yang bergantung akan dikelola dalam urutan yang benar
5. Tampilkan planFormat output untuk userDaftar perubahanKeputusan akhir: apply atau tidak

Langkah refresh di nomor 2 berarti plan selalu mencerminkan kondisi aktual di cloud — bukan hanya apa yang tersimpan di state file lokal. Jika seseorang mengubah resource langsung di console cloud (misalnya mengganti instance type di AWS Console), plan akan mendeteksinya.

Membaca Output Plan #

Output plan menggunakan simbol untuk menunjukkan tipe operasi pada setiap resource. Kemampuan membaca output ini dengan cepat adalah keterampilan krusial.

flowchart TD
    A["Simbol di Output Plan"] --> B["+ CREATE\nResource baru akan dibuat"]
    A --> C["~ UPDATE IN-PLACE\nResource dimodifikasi tanpa replace"]
    A --> D["- DESTROY\nResource akan dihapus"]
    A --> E["-/+ REPLACE\nDihapus lalu dibuat ulang"]
    A --> F["<= READ\nData source akan dibaca"]

    B --> B1["✅ Relatif aman\nTidak ada dampak ke existing"]
    C --> C1["✅ Biasanya aman\nAtribut tertentu berubah"]
    D --> D1["⚠️ Hati-hati\nData hilang permanen"]
    E --> E1["🔴 Paling berbahaya\nDowntime + data hilang"]

    style B fill:#e8f5e9,stroke:#2e7d32
    style C fill:#e3f2fd,stroke:#1565c0
    style D fill:#ffebee,stroke:#c62828
    style E fill:#ffebee,stroke:#c62828
    style F fill:#f3e5f5,stroke:#7b1fa2
    style B1 fill:#e8f5e9,stroke:#2e7d32
    style C1 fill:#e3f2fd,stroke:#1565c0
    style D1 fill:#fff3e0,stroke:#e65100
    style E1 fill:#ffebee,stroke:#c62828
SimbolArtiDampakRisiko
+CreateResource baru dibuatRendah — tidak ada dampak ke existing
~Update in-placeAtribut berubah tanpa replaceRendah-Sedang — tergantung atribut
-DestroyResource dihapus permanenTinggi — data hilang
-/+Replace (destroy + create)Dihapus lalu dibuat ulangSangat tinggi — downtime + data hilang
<=ReadData source dibacaTidak ada — read-only
$ terraform plan

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
  - destroy
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.web will be created
  + resource "aws_instance" "web" {
      + ami                    = "ami-0abcdef1234567890"
      + id                     = (known after apply)
      + instance_type          = "t3.micro"
      + public_ip              = (known after apply)
      + tags                   = {
          + "Name" = "web-server"
        }
    }

  # aws_security_group.web will be updated in-place
  ~ resource "aws_security_group" "web" {
        id   = "sg-0abcdef1234567890"
        name = "web-sg"
      ~ ingress = [
          - {
              - from_port   = 22
              - protocol    = "tcp"
              - to_port     = 22
            },
          + {
              + from_port   = 443
              - protocol    = "tcp"
              + to_port     = 443
            },
        ]
    }

Plan: 1 to add, 1 to change, 0 to destroy.

Summary Line di Akhir Plan #

Di akhir output plan, Terraform menampilkan summary yang memberikan gambaran cepat:

Plan: 1 to add, 1 to change, 0 to destroy.

# Contoh lain:
# Plan: 5 to add, 0 to change, 2 to destroy.
# → 5 resource baru, tidak ada perubahan, 2 yang akan dihapus

# No changes. Your infrastructure matches the configuration.
# → Tidak ada perubahan — konfigurasi sudah sesuai kondisi aktual

Waspada dengan Replace (-/+) #

Replace adalah operasi yang paling sering menjebak developer Terraform. Beberapa perubahan atribut memicu replace, bukan update in-place. Kamu bisa mendeteksinya dari komentar # forces replacement di output plan.

flowchart TD
    A["Perubahan atribut\nresource"] --> B{"Tipe atribut\nyang berubah?"}

    B -->|"Atribut yang bisa\ndi-update"| C["Update in-place (~)\nResource tetap jalan"]
    B -->|"Atribut yang forces\nreplacement"| D["Replace (-/+)\nResource dihapus + dibuat ulang"]

    D --> E["Apa yang terjadi?"]
    E --> F["1. Instance lama dihapus"]
    F --> G["2. Instance baru dibuat"]
    G --> H["⚠️ Semua data di instance\nlama hilang!"]

    D --> I["Cegah dengan\nlifecycle block"]

    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#ffebee,stroke:#c62828
    style H fill:#ffebee,stroke:#c62828
    style I fill:#e3f2fd,stroke:#1565c0

Contoh Perubahan yang Memicu Replace #

# Contoh perubahan yang memicu REPLACE (bukan update):

resource "aws_instance" "web" {
  # ANTI-PATTERN: Mengubah AMI di instance yang berjalan
  ami = "ami-NEW"  # Mengganti AMI → forces replacement
                   # Instance lama dihapus, instance baru dibuat
                   # Semua data di disk instance lama hilang
}

# Perubahan yang memicu replace ditandai dengan "# forces replacement"
# di output plan:
#
# -/+ resource "aws_instance" "web" {
#     ~ ami = "ami-OLD" -> "ami-NEW" # forces replacement
#     - id  = "i-0abcdef1234567890"
#     + id  = (known after apply)
#   }
ResourceAtribut yang Memicu ReplaceDampak
aws_instanceami, instance_type (beberapa kasus)Instance baru, IP berubah
aws_db_instanceengine, instance_class (beberapa kasus)Database baru, data perlu migrasi
aws_s3_bucketbucket (nama bucket)Bucket baru, data lama orphaned
aws_security_groupPerubahan pada nameSG baru, perlu re-attach
aws_vpccidr_blockVPC baru, semua subnet perlu di-recreate

Solusi: create_before_destroy #

# BENAR: Gunakan lifecycle create_before_destroy untuk meminimalkan downtime
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.micro"

  lifecycle {
    create_before_destroy = true
    # Buat instance baru dulu sebelum instance lama dihapus
    # Minimalkan downtime, tapi butuh resource naming yang unik
  }
}
flowchart LR
    subgraph "Tanpa create_before_destroy"
        OLD1["Instance lama\ni-0abc123"] -->|"destroy"| NOTHING["❌ Downtime\nsampai instance\nbaru siap"]
        NOTHING -->|"create"| NEW1["Instance baru\ni-0def456"]
    end

    subgraph "Dengan create_before_destroy"
        OLD2["Instance lama\ni-0abc123"] -.->|"masih berjalan"| BOTH["✅ Instance baru\nsiap dulu"]
        NEW2["Instance baru\ni-0def456"] -->|"create"| BOTH
        BOTH -->|"destroy"| DONE["Instance lama\ndihapus"]
    end

    style NOTHING fill:#ffebee,stroke:#c62828
    style BOTH fill:#e8f5e9,stroke:#2e7d32
    style DONE fill:#e8f5e9,stroke:#2e7d32

Saved Plan untuk Deployment Aman #

Ada celah antara waktu kamu menjalankan plan dan waktu kamu menjalankan apply. Dalam rentang waktu itu, kondisi cloud bisa berubah. Untuk deployment production, gunakan saved plan.

flowchart LR
    subgraph "Tanpa Saved Plan ⚠️"
        P1["terraform plan\nJam 10:00"] -->|"Waktu berlalu...\nkondisi berubah"| A1["terraform apply\nJam 10:30"]
        A1 --> R1["⚠️ Apply berbeda\ndari yang di-review!"]
    end

    subgraph "Dengan Saved Plan ✅"
        P2["terraform plan\n-out=tfplan\nJam 10:00"] -->|"tfplan tersimpan\nsecara biner"| A2["terraform apply\ntfplan\nJam 10:30"]
        A2 --> R2["✅ Apply persis\nseperti yang di-review"]
    end

    style R1 fill:#fff3e0,stroke:#e65100
    style R2 fill:#e8f5e9,stroke:#2e7d32
# Simpan plan ke file
terraform plan -out=tfplan

# Apply persis dari plan yang disimpan
# Terraform tidak akan generate plan baru — langsung eksekusi yang sudah disimpan
terraform apply tfplan

# Keuntungan:
# - Apa yang di-review adalah persis yang di-apply
# - Tidak ada kejutan dari perubahan kondisi antara plan dan apply
# - Cocok untuk workflow GitOps: plan di PR, apply setelah merge

# Untuk melihat isi saved plan dalam format yang bisa dibaca:
terraform show tfplan
WorkflowPlanApplyRisiko
Tanpa saved planterraform planterraform applyKondisi bisa berubah antara plan dan apply
Dengan saved planterraform plan -out=tfplanterraform apply tfplanTidak ada — apply persis seperti plan
CI/CD pipelineterraform plan -out=tfplan (di PR)terraform apply tfplan (setelah merge)Minimal — sudah di-review
File tfplan mengandung seluruh konfigurasi dan state dalam format biner. Ia bisa mengandung nilai sensitif. Jangan commit file ini ke Git, dan hapus setelah selesai digunakan.

Flag Berguna untuk Plan #

FlagFungsiKapan Digunakan
-var="key=value"Override variableTesting dengan nilai berbeda
-var-file="file.tfvars"File variableEnvironment-specific config
-target=resourceHanya plan resource tertentuDebugging resource spesifik
-refresh=falseSkip refresh statePercepat plan (kurang akurat)
-out=tfplanSimpan plan ke fileProduction deployment
-parallelism=NBatasi concurrent opsRate limit API
# Plan dengan variable override
terraform plan -var="environment=production"
terraform plan -var-file="production.tfvars"

# Plan hanya untuk resource tertentu (target planning)
terraform plan -target=aws_instance.web
terraform plan -target=module.vpc

# Skip refresh state (lebih cepat, tapi mungkin tidak reflect kondisi aktual)
terraform plan -refresh=false

# Tampilkan rencana dalam format JSON (untuk parsing programatik)
terraform plan -out=tfplan && terraform show -json tfplan | jq .

# Plan dengan jumlah concurrent operation yang dibatasi
terraform plan -parallelism=5  # default: 10
-target berguna untuk debugging, tapi jangan gunakan secara rutin. Plan dengan -target hanya melihat sebagian dependency graph — resource yang tidak di-target mungkin punya dependency yang tidak terlihat, menyebabkan state tidak konsisten setelah apply.

Kapan Plan Bisa Menipu #

Plan tidak selalu 100% akurat. Ada beberapa kondisi di mana apa yang ditampilkan plan tidak sepenuhnya mencerminkan apa yang akan terjadi.

flowchart TD
    A["Plan bisa menipu saat..."] --> B["1. Nilai computed\nbelum diketahui"]
    A --> C["2. Perubahan tidak\nbisa diprediksi"]
    A --> D["3. External changes\nyang belum ter-sync"]
    A --> E["4. Order of operations\nbisa berubah"]

    B --> B1["(known after apply)\nIP address, ID, dll.\nbaru diketahui setelah apply"]
    C --> C1["Contoh: random_password,\ntimestamp, UUID\nSelalu berubah di setiap plan"]
    D --> D1["Jika seseorang mengubah\nresource di cloud\ntanpa lewat Terraform"]
    E --> E1["Dalam kasus langka,\nurutan bisa berbeda\ndari yang diharapkan"]

    style B1 fill:#fff3e0,stroke:#e65100
    style C1 fill:#fff3e0,stroke:#e65100
    style D1 fill:#ffebee,stroke:#c62828
    style E1 fill:#fff3e0,stroke:#e65100
SituasiMengapa MenipuSolusi
Nilai (known after apply)Nilai belum diketahui sampai resource dibuatNormal — tidak bisa dihindari
Resource random_* atau time_*Selalu menunjukkan perubahanGunakan lifecycle { ignore_changes }
Perubahan manual di cloudState belum di-refreshJalankan terraform refresh sebelum plan
Data source berubahData source di-refresh setiap planGunakan -refresh=false untuk percepat
# Contoh: Resource random yang selalu berubah di plan
resource "random_password" "db" {
  length = 16
  # Setiap kali plan dijalankan, ia menunjukkan "1 to change"
  # karena random_password menghasilkan nilai baru setiap kali

  # SOLUSI: Ignore perubahan setelah pertama kali dibuat
  lifecycle {
    ignore_changes = [result]
  }
}

Ringkasan #

  • Plan selalu refresh state sebelum menghitung perubahan — mencerminkan kondisi aktual di cloud, bukan hanya state file lokal.
  • Lima simbol: + create (aman), ~ update in-place (biasanya aman), - destroy (hati-hati), -/+ replace (paling berbahaya), <= read (aman).
  • -/+ adalah yang paling berbahaya — resource dihapus dan dibuat ulang, berarti potensi downtime dan kehilangan data. Cari tanda # forces replacement di output plan.
  • Gunakan create_before_destroy di lifecycle block untuk meminimalkan downtime saat replace tidak bisa dihindari.
  • Gunakan saved plan (-out=tfplan) untuk production — pastikan apa yang di-review adalah persis yang di-apply.
  • -target berguna untuk debugging, tapi hindari penggunaan rutin karena bisa menyembunyikan dependency.
  • Resource random/time selalu menunjukkan perubahan — gunakan lifecycle { ignore_changes } untuk menghindari noise di plan.

← Sebelumnya: Init   Berikutnya: Apply →

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