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| Langkah | Apa yang Dilakukan | Output | Kenapa Penting |
|---|---|---|---|
| 1. Baca config | Parse semua .tf file | Representasi internal konfigurasi | Syntax error akan terdeteksi di sini |
| 2. Refresh state | Query ke API provider | Data aktual resource di cloud | Mencerminkan kondisi nyata, bukan hanya state lama |
| 3. Bandingkan | Config vs state aktual | Daftar perbedaan | Menangkap perubahan manual di cloud |
| 4. Dependency graph | Hitung urutan ketergantungan | Urutan operasi | Resource yang bergantung akan dikelola dalam urutan yang benar |
| 5. Tampilkan plan | Format output untuk user | Daftar perubahan | Keputusan 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| Simbol | Arti | Dampak | Risiko |
|---|---|---|---|
+ | Create | Resource baru dibuat | Rendah — tidak ada dampak ke existing |
~ | Update in-place | Atribut berubah tanpa replace | Rendah-Sedang — tergantung atribut |
- | Destroy | Resource dihapus permanen | Tinggi — data hilang |
-/+ | Replace (destroy + create) | Dihapus lalu dibuat ulang | Sangat tinggi — downtime + data hilang |
<= | Read | Data source dibaca | Tidak 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:#1565c0Contoh 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)
# }
| Resource | Atribut yang Memicu Replace | Dampak |
|---|---|---|
aws_instance | ami, instance_type (beberapa kasus) | Instance baru, IP berubah |
aws_db_instance | engine, instance_class (beberapa kasus) | Database baru, data perlu migrasi |
aws_s3_bucket | bucket (nama bucket) | Bucket baru, data lama orphaned |
aws_security_group | Perubahan pada name | SG baru, perlu re-attach |
aws_vpc | cidr_block | VPC 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:#2e7d32Saved 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
| Workflow | Plan | Apply | Risiko |
|---|---|---|---|
| Tanpa saved plan | terraform plan | terraform apply | Kondisi bisa berubah antara plan dan apply |
| Dengan saved plan | terraform plan -out=tfplan | terraform apply tfplan | Tidak ada — apply persis seperti plan |
| CI/CD pipeline | terraform 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 #
| Flag | Fungsi | Kapan Digunakan |
|---|---|---|
-var="key=value" | Override variable | Testing dengan nilai berbeda |
-var-file="file.tfvars" | File variable | Environment-specific config |
-target=resource | Hanya plan resource tertentu | Debugging resource spesifik |
-refresh=false | Skip refresh state | Percepat plan (kurang akurat) |
-out=tfplan | Simpan plan ke file | Production deployment |
-parallelism=N | Batasi concurrent ops | Rate 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
-targetberguna untuk debugging, tapi jangan gunakan secara rutin. Plan dengan-targethanya 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| Situasi | Mengapa Menipu | Solusi |
|---|---|---|
Nilai (known after apply) | Nilai belum diketahui sampai resource dibuat | Normal — tidak bisa dihindari |
Resource random_* atau time_* | Selalu menunjukkan perubahan | Gunakan lifecycle { ignore_changes } |
| Perubahan manual di cloud | State belum di-refresh | Jalankan terraform refresh sebelum plan |
| Data source berubah | Data source di-refresh setiap plan | Gunakan -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 replacementdi output plan.- Gunakan
create_before_destroydi 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.-targetberguna 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.