Execution Plan #
Ketika kamu menjalankan terraform plan, Terraform tidak hanya membuat daftar perubahan secara acak. Di balik layar, ia membangun dependency graph — struktur data yang menentukan urutan eksekusi yang tepat, resource mana yang bisa dijalankan paralel, dan resource mana yang harus menunggu yang lain selesai dulu. Memahami bagaimana execution plan dibangun membantumu menulis konfigurasi yang lebih efisien dan men-debug masalah yang tidak terlihat jelas dari permukaan.
Apa itu Dependency Graph #
Dependency graph adalah struktur data yang Terraform bangun dari konfigurasimu. Setiap resource adalah node dalam graph, dan setiap referensi antar resource adalah edge (arah dependency). Terraform secara otomatis menyimpulkan dependency dari referensi — kamu tidak perlu menentukan urutan eksekusi secara manual.
flowchart TD
A["aws_vpc.main\n(Tanpa dependency\ndieksekusi pertama)"] --> B["aws_subnet.public\n(memerlukan VPC)"]
A --> C["aws_internet_gateway.main\n(memerlukan VPC)"]
A --> D["aws_route_table.public\n(memerlukan VPC)"]
B --> E["aws_instance.web\n(memerlukan subnet)"]
style A fill:#e3f2fd,stroke:#1565c0
style B fill:#e8f5e9,stroke:#2e7d32
style C fill:#e8f5e9,stroke:#2e7d32
style D fill:#e8f5e9,stroke:#2e7d32
style E fill:#fff3e0,stroke:#e65100# Konfigurasi yang menghasilkan graph di atas:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id # ← edge: subnet → VPC
cidr_block = "10.0.1.0/24"
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id # ← edge: IGW → VPC
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id # ← edge: route table → VPC
}
resource "aws_instance" "web" {
subnet_id = aws_subnet.public.id # ← edge: instance → subnet
ami = var.ami_id
}
| Level Dependency | Resource | Bergantung Pada | Bisa Paralel Dengan |
|---|---|---|---|
| Level 0 (root) | aws_vpc.main | Tidak ada | — |
| Level 1 | aws_subnet.public | VPC | internet_gateway, route_table |
| Level 1 | aws_internet_gateway.main | VPC | subnet, route_table |
| Level 1 | aws_route_table.public | VPC | subnet, internet_gateway |
| Level 2 | aws_instance.web | Subnet | Tidak ada di contoh ini |
Cara Terraform menyimpulkan dependency: Setiap kali kamu menulisaws_vpc.main.idatauaws_subnet.public.iddi dalam resource block, Terraform secara otomatis mendeteksi bahwa resource tersebut bergantung pada pemilik atribut tersebut. Tidak perlu deklarasi dependency secara manual.
Paralelisme dalam Execution Plan #
Terraform secara otomatis mengeksekusi resource secara paralel selama tidak ada dependency di antara mereka. Ini membuat Terraform jauh lebih efisien dari script sequential.
gantt
title Perbandingan Eksekusi Sequential vs Paralel
dateFormat X
axisFormat %s
section Sequential (Bash)
VPC :a1, 0, 2s
Subnet :a2, 2, 4s
IGW :a3, 4, 6s
Route Table :a4, 6, 8s
Instance :a5, 8, 10s
section Paralel (Terraform)
VPC :b1, 0, 2s
Subnet :b2, 2, 4s
IGW :b3, 2, 4s
Route Table :b4, 2, 4s
Instance :b5, 4, 6sSEQUENTIAL (script bash):
VPC: ████ (2s)
Subnet: ░░░░████ (4s setelah VPC)
IGW: ░░░░░░░░████ (6s setelah subnet)
RT: ░░░░░░░░░░░░████ (8s setelah IGW)
Instance:░░░░░░░░░░░░░░░░████ (10s setelah RT)
Total: ~14 detik (semua sequential)
PARALEL (Terraform):
VPC: ████ (2s)
Subnet: ░░░░████ (mulai setelah VPC)
IGW: ░░░░████ (mulai bersamaan dengan subnet)
RT: ░░░░████ (mulai bersamaan dengan subnet dan IGW)
Instance:░░░░░░░░████ (tunggu subnet selesai)
Total: ~6 detik (VPC → paralel → instance)
| Aspek | Sequential (Script) | Paralel (Terraform) |
|---|---|---|
| Total waktu (contoh) | ~14 detik | ~6 detik |
| Resource independen | Dijalankan berurutan | Dijalankan bersamaan |
| Default concurrency | 1 per 1 | 10 sekaligus |
| Efisiensi | Rendah | Tinggi |
# Atur paralelisme sesuai kebutuhan
terraform plan # Default: 10 paralel
terraform apply -parallelism=5 # Kurangi jika rate limit
terraform apply -parallelism=20 # Tingkatkan untuk infra besar
Membaca Execution Plan secara Detail #
Output terraform plan menampilkan urutan yang akan Terraform lakukan, tapi tidak secara eksplisit menunjukkan paralelisme. Untuk melihat dependency graph secara visual, gunakan terraform graph.
flowchart TD
A["terraform graph"] --> B["Format DOT (Graphviz)"]
B --> C["Render ke SVG/PNG\n(dot -Tsvg / dot -Tpng)"]
B --> D["Render ke JSON\n(terraform show -json)"]
E["terraform graph -plan=tfplan"] --> F["Graph spesifik\nberdasarkan plan\ntertentu"]
style A fill:#e3f2fd,stroke:#1565c0
style E fill:#e3f2fd,stroke:#1565c0# Generate dependency graph dalam format DOT (Graphviz)
terraform graph
# Output (potongan):
# digraph {
# compound = "true"
# newrank = "true"
# "aws_vpc.main" -> "aws_subnet.public"
# "aws_vpc.main" -> "aws_internet_gateway.main"
# "aws_subnet.public" -> "aws_instance.web"
# }
# Render ke gambar (butuh Graphviz terinstall: brew install graphviz)
terraform graph | dot -Tsvg > graph.svg
terraform graph | dot -Tpng > graph.png
# Untuk plan spesifik (bukan konfigurasi saat ini)
terraform graph -plan=tfplan
Explicit Dependency dengan depends_on
#
Terraform menyimpulkan dependency dari referensi langsung (aws_vpc.main.id). Tapi ada kasus di mana dependency tidak terlihat dari referensi — misalnya ketergantungan pada side effect.
flowchart TD
A["Dependency antar resource"] --> B{"Bisa disimpulkan\ndari referensi?"}
B -->|"Ya: vpc_id = aws_vpc.main.id"| C["✅ Implicit dependency\nOtomatis terdeteksi\nTidak perlu depends_on"]
B -->|"Tidak: side effect\nmisal: IAM policy\nsudah ter-attach"| D["⚠️ Explicit dependency\nButuh depends_on"]
D --> E["depends_on = [\n aws_iam_role_policy_attachment.node_policy\n]"]
C --> F["Maksimal paralelisme\nEfisien"]
E --> G["Mengurangi paralelisme\nResource menunggu\nyang tidak perlu"]
style C fill:#e8f5e9,stroke:#2e7d32
style D fill:#fff3e0,stroke:#e65100
style F fill:#e8f5e9,stroke:#2e7d32
style G fill:#ffebee,stroke:#c62828Kapan depends_on Diperlukan
#
# KASUS: EKS Node Group butuh IAM policy sudah ter-attach ke role
# Dependency ini TIDAK bisa disimpulkan dari referensi resource saja
# karena node_group hanya merujuk ke role ARN, bukan ke policy attachment
resource "aws_iam_role" "node" {
name = "eks-node-role"
# ...
}
resource "aws_iam_role_policy_attachment" "node_policy" {
role = aws_iam_role.node.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}
resource "aws_eks_node_group" "workers" {
cluster_name = aws_eks_cluster.main.name
node_role_arn = aws_iam_role.node.arn
# ✅ depends_on diperlukan di sini karena:
# node_group merujuk ke role ARN (bukan policy attachment)
# Terraform tidak tahu bahwa node_group butuh policy sudah ter-attach
depends_on = [
aws_iam_role_policy_attachment.node_policy
]
}
| Kasus | depends_on? | Penjelasan |
|---|---|---|
Resource B merujuk resource_a.id | ❌ Tidak perlu | Dependency sudah implicit dari referensi |
| Resource B butuh side effect dari A | ✅ Perlu | Misal: IAM policy harus ter-attach dulu |
| Resource B butuh A selesai duluan | ❌ Cek dulu | Mungkin bisa implicit dari referensi |
Anti-Pattern: depends_on Berlebihan
#
# ❌ ANTI-PATTERN: depends_on yang tidak perlu
resource "aws_subnet" "public" {
depends_on = [aws_vpc.main] # ✗ Tidak perlu!
vpc_id = aws_vpc.main.id # Referensi ini sudah cukup
cidr_block = "10.0.1.0/24"
}
# ✅ BENAR: Biarkan referensi menentukan dependency otomatis
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id # ✓ Dependency sudah tersirat
cidr_block = "10.0.1.0/24"
}
flowchart TD
A["depends_on berlebihan"] --> B["Resource menunggu\nresource yang sebenarnya\nbisa paralel"]
B --> C["Paralelisme berkurang\nApply lebih lama"]
B --> D["Dependency graph\nlebih kompleks\ndari yang perlu"]
E["Best practice"] --> F["Gunakan referensi langsung\nuntuk implicit dependency"]
E --> G["Gunakan depends_on\nhanya untuk side effect"]
E --> H["Dokumentasikan alasan\nsetiap depends_on"]
style A fill:#ffebee,stroke:#c62828
style C fill:#ffebee,stroke:#c62828
style E fill:#e8f5e9,stroke:#2e7d32Execution Plan dalam Format JSON #
Untuk integrasi dengan tooling eksternal (CI/CD, policy checker, notifikasi), execution plan bisa diekspor dalam format JSON. Ini memungkinkan pemeriksaan programatik terhadap rencana perubahan.
flowchart TD
A["terraform plan\n-out=tfplan"] --> B["terraform show\n-json tfplan > plan.json"]
B --> C["Analisis dengan jq\natau script custom"]
B --> D["Policy check\n(OPA, Sentinel)"]
B --> E["Notification\nSlack, Teams, dll"]
B --> F["Approval gate\nCI/CD pipeline"]
C --> G["Cek jumlah destroy\nCek resource type\nCek blast radius"]
D --> H["Validasi compliance\nsebelum apply"]
E --> I["Tim tahu apa\nyang akan berubah"]
F --> J["Manual approval\nsebelum apply"]
style B fill:#e3f2fd,stroke:#1565c0
style G fill:#e8f5e9,stroke:#2e7d32
style H fill:#e8f5e9,stroke:#2e7d32# Generate plan dan export ke JSON
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
# Struktur JSON plan (potongan):
# {
# "format_version": "1.2",
# "resource_changes": [
# {
# "address": "aws_instance.web",
# "change": {
# "actions": ["create"],
# "before": null,
# "after": {
# "ami": "ami-0abcdef1234567890",
# "instance_type": "t3.micro"
# }
# }
# }
# ]
# }
# Contoh: Cek apakah ada resource yang akan di-destroy
terraform show -json tfplan | \
jq '[.resource_changes[] | select(.change.actions[] == "delete")] | length'
# Output: 0 (tidak ada yang akan dihapus)
# Contoh: Hitung jumlah resource yang akan berubah
terraform show -json tfplan | \
jq '[.resource_changes[] | select(.change.actions[] != "no-op")] | length'
# Output: 5 (ada 5 resource yang akan berubah)
# Contoh: List semua resource yang akan di-create
terraform show -json tfplan | \
jq -r '.resource_changes[] | select(.change.actions[] == "create") | .address'
# Output:
# aws_vpc.main
# aws_subnet.public
# aws_instance.web
| Query jq | Kegunaan | Cocok Untuk |
|---|---|---|
select(.actions[] == "delete") | Deteksi resource yang akan dihapus | Safety check sebelum apply |
select(.actions[] != "no-op") | Hitung total perubahan | Blast radius assessment |
select(.actions[] == "create") | List resource baru | Review resource baru |
select(.actions[] == "update") | List resource yang berubah | Review perubahan |
Ringkasan #
- Dependency graph dibangun otomatis dari referensi antar resource — kamu tidak perlu menentukan urutan eksekusi secara manual.
- Resource independen dieksekusi paralel — Terraform secara default menjalankan hingga 10 operasi bersamaan untuk efisiensi maksimal.
terraform graphmenghasilkan visualisasi dependency graph — berguna untuk memahami konfigurasi yang kompleks. Render ke SVG/PNG dengan Graphviz.depends_onuntuk side-effect dependency — gunakan hanya ketika dependency tidak bisa disimpulkan dari referensi langsung (misal: IAM policy attachment).- Jangan over-gunakan
depends_on— setiapdepends_oneksplisit mengurangi paralelisme dan bisa memperlambat apply.- Export plan ke JSON (
terraform show -json tfplan) untuk integrasi dengan CI/CD — memungkinkan pemeriksaan programatik seperti “tidak ada resource yang di-destroy” atau “blast radius assessment”.