Dependency #
Infrastruktur cloud jarang berdiri sendiri. Sebuah EC2 instance butuh subnet, subnet butuh VPC, VPC butuh internet gateway untuk akses publik. Terraform perlu tahu urutan yang benar untuk membuat dan menghapus semua resource ini. Cara Terraform mengelola dependency — secara otomatis maupun eksplisit — adalah salah satu fitur yang paling membedakannya dari pendekatan scripting tradisional.
Implicit Dependency #
Implicit dependency adalah dependency yang Terraform simpulkan secara otomatis dari referensi antar resource. Ini adalah cara yang paling umum dan paling direkomendasikan untuk mendefinisikan dependency.
flowchart TD
A["aws_vpc.main"] --> B["aws_subnet.public\nvpc_id = aws_vpc.main.id"]
A --> C["aws_internet_gateway.main\nvpc_id = aws_vpc.main.id"]
B --> D["aws_instance.web\nsubnet_id = aws_subnet.public.id"]
style A fill:#e3f2fd,stroke:#1565c0
style D fill:#e8f5e9,stroke:#2e7d32resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
# Referensi ke aws_vpc.main.id secara otomatis membuat
# Terraform tahu bahwa subnet ini bergantung pada VPC
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
resource "aws_internet_gateway" "main" {
# IGW juga bergantung pada VPC
vpc_id = aws_vpc.main.id
}
resource "aws_instance" "web" {
ami = var.ami_id
# Instance bergantung pada subnet
subnet_id = aws_subnet.public.id
}
# Dependency graph yang terbentuk:
#
# aws_vpc.main
# │
# ┌────┴────┐
# │ │
# ▼ ▼
# subnet igw
# │
# ▼
# instance
Terraform menghitung graph ini secara otomatis. Kamu tidak perlu menulis satu baris pun kode tambahan untuk mendefinisikan urutan.
Explicit Dependency dengan depends_on #
Ada kasus di mana dependency tidak bisa disimpulkan dari referensi langsung. Ini terjadi ketika resource bergantung pada efek samping dari resource lain, bukan pada atributnya.
flowchart TD
A["aws_iam_role.node"] --> B["aws_iam_role_policy_attachment\n.eks_worker_node"]
A --> C["aws_iam_role_policy_attachment\n.eks_cni"]
D["aws_eks_cluster.main"] --> E["aws_eks_node_group.workers"]
B --> E
C --> E
E -.->|"node_role_arn =\naws_iam_role.node.arn"| A
E -.->|"depends_on: butuh policy\nsudah TER-ATTACH"| B
E -.->|"depends_on: butuh policy\nsudah TER-ATTACH"| C
style E fill:#fff3e0,stroke:#e65100
style B fill:#e8f5e9,stroke:#2e7d32
style C fill:#e8f5e9,stroke:#2e7d32# KASUS NYATA: EKS Node Group bergantung pada IAM policy yang sudah ter-attach
resource "aws_iam_role" "node" {
name = "eks-node-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy_attachment" "eks_worker_node" {
role = aws_iam_role.node.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}
resource "aws_iam_role_policy_attachment" "eks_cni" {
role = aws_iam_role.node.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
}
resource "aws_eks_node_group" "workers" {
cluster_name = aws_eks_cluster.main.name
node_role_arn = aws_iam_role.node.arn # Referensi ke role ada
subnet_ids = aws_subnet.private[*].id
scaling_config {
desired_size = 2
max_size = 5
min_size = 1
}
# Tanpa depends_on ini, node group mungkin mulai dibuat sebelum
# semua policy selesai di-attach ke role.
# aws_iam_role.node.arn tidak menjamin policy sudah ter-attach.
depends_on = [
aws_iam_role_policy_attachment.eks_worker_node,
aws_iam_role_policy_attachment.eks_cni,
]
}
Kapan Menggunakan depends_on #
GUNAKAN depends_on JIKA:
✓ Resource bergantung pada efek samping resource lain
(bukan pada nilai atributnya)
✓ Urutan yang benar tidak bisa disimpulkan dari referensi
✓ Provider documentation eksplisit menyebutkan dependency tertentu
✓ Ada race condition yang tercatat karena timing IAM propagation
JANGAN GUNAKAN depends_on JIKA:
✗ Dependency sudah tersirat dari referensi langsung
(tambahan depends_on hanya menambah noise)
✗ Mencoba "memperlambat" eksekusi — ini bukan cara yang tepat
✗ Tidak yakin mengapa dibutuhkan — cari root cause yang sebenarnya
Anti-Pattern: Over-Using depends_on #
# ANTI-PATTERN: depends_on yang tidak perlu
resource "aws_subnet" "public" {
# Referensi vpc_id sudah membuat dependency implisit
# depends_on di bawah ini redundan dan hanya menambah kebingungan
depends_on = [aws_vpc.main] # ✗ Tidak perlu
vpc_id = aws_vpc.main.id # ✓ Dependency sudah ada di sini
cidr_block = "10.0.1.0/24"
}
# ANTI-PATTERN: depends_on ke seluruh module tanpa alasan spesifik
module "database" {
source = "./modules/database"
depends_on = [module.networking] # ✗ Terlalu broad
# Dependency yang terlalu luas mencegah paralelisme yang seharusnya bisa terjadi
# Sebutkan resource spesifik jika memang perlu
}
Dependency Antar Module #
Ketika menggunakan modul, dependency bisa melintasi batas modul.
flowchart TD
V["module.vpc"] --> E["module.eks\nvpc_id = module.vpc.vpc_id"]
V --> R["module.rds\nvpc_id = module.vpc.vpc_id"]
E --- P1["PARALEL\nsetelah VPC selesai"]
R --- P2["PARALEL\nsetelah VPC selesai"]
style V fill:#e3f2fd,stroke:#1565c0
style E fill:#e8f5e9,stroke:#2e7d32
style R fill:#e8f5e9,stroke:#2e7d32module "vpc" {
source = "./modules/vpc"
cidr = "10.0.0.0/16"
}
module "eks" {
source = "./modules/eks"
# Referensi ke output module vpc — implicit dependency
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
# EKS module akan dibuat setelah VPC module selesai
}
module "rds" {
source = "./modules/rds"
# RDS juga bergantung pada VPC — tapi independen dari EKS
# Terraform akan membuat RDS dan Eks secara PARALEL
# setelah VPC selesai
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.database_subnet_ids
}
Melihat Dependency Graph #
# Generate dan visualisasikan dependency graph
terraform graph | dot -Tsvg > dependency-graph.svg
# Filter hanya resource tertentu (butuh grep/awk)
terraform graph | grep -A2 "aws_instance"
# Graph untuk plan yang sudah disimpan
terraform graph -plan=tfplan
Implicit vs Explicit Dependency #
Terraform secara otomatis mendeteksi dependency dari referensi di argument. Tapi kadang kamu perlu mendeklarasikan dependency secara eksplisit.
# IMPLICIT DEPENDENCY (otomatis terdeteksi):
resource "aws_instance" "web" {
subnet_id = aws_subnet.public.id
# Terraform otomatis tahu: web depends_on subnet
# Tidak perlu deklarasi eksplisit
}
# EXPLICIT DEPENDENCY (perlu dideklarasikan):
resource "aws_instance" "web" {
ami = "ami-12345"
subnet_id = aws_subnet.public.id
depends_on = [
aws_route_table_association.public, # Pastikan routing selesai dulu
aws_security_group.web_sg, # Pastikan SG terpasang
]
# Berguna ketika dependency tidak bisa dilihat dari argument
}
flowchart TD
subgraph IMPLICIT["Implicit Dependency"]
I1["aws_subnet.public"] --> I2["aws_instance.web
(subnet_id = subnet.id)"]
end
subgraph EXPLICIT["Explicit Dependency"]
E1["aws_route_table_association"] --> E2["aws_instance.web
depends_on = [...]"]
E3["aws_security_group"] --> E2
end
style I1 fill:#e3f2fd,stroke:#1565c0
style I2 fill:#e8f5e9,stroke:#2e7d32
style E1 fill:#fff3e0,stroke:#e65100
style E3 fill:#fff3e0,stroke:#e65100
style E2 fill:#e8f5e9,stroke:#2e7d32Dependency Anti-Pattern: Over-using depends_on #
# ANTI-PATTERN: depends_on pada semuanya
resource "aws_instance" "web" {
depends_on = [
aws_vpc.main,
aws_subnet.public,
aws_security_group.web_sg,
aws_iam_role.app_role,
aws_s3_bucket.data_bucket,
]
# Masalah:
# 1. Mencegah parallelisasi — resource harus tunggu semua dependency
# 2. Dependency tidak akurat — tidak semua benar-benar dependency
# 3. Menghilangkan implicit dependency yang lebih tepat
}
# BENAR: Biarkan Terraform deteksi implicit dependency
resource "aws_instance" "web" {
subnet_id = aws_subnet.public.id # → implicit dependency
vpc_security_group_ids = [aws_security_group.web_sg.id] # → implicit
iam_instance_profile = aws_iam_instance_profile.app.name # → implicit
# depends_on TIDAK diperlukan — referensi sudah cukup
}
Debugging Dependency Graph #
Ketika Terraform melakukan sesuatu yang tidak kamu harapkan, dependency graph adalah kunci untuk memahami mengapa.
# Generate dependency graph
terraform graph > graph.dot
# Visualisasi dengan Graphviz
terraform graph -type=plan | dot -Tpng > plan-graph.png
terraform graph -type=plan | dot -Tsvg > plan-graph.svg
# Filter hanya resource tertentu
terraform graph -draw-cycles -type=plan | dot -Tpng > cycles.png
flowchart TD
subgraph CLUSTER["Dependency Graph"]
VPC["aws_vpc.main"]
PUB_SUB["aws_subnet.public"]
PRI_SUB["aws_subnet.private"]
IGW["aws_internet_gateway"]
RT["aws_route_table"]
NAT["aws_nat_gateway"]
SG["aws_security_group"]
EC2["aws_instance.web"]
RDS["aws_db_instance.main"]
VPC --> PUB_SUB
VPC --> PRI_SUB
VPC --> IGW
VPC --> RT
PUB_SUB --> NAT
PUB_SUB --> EC2
PRI_SUB --> RDS
SG --> EC2
NAT --> PRI_SUB
end
style VPC fill:#e3f2fd,stroke:#1565c0
style EC2 fill:#e8f5e9,stroke:#2e7d32
style RDS fill:#e8f5e9,stroke:#2e7d32# Analisis cycle (siklus dependency)
# Jika ada cycle, Terraform akan error:
# "Error: Cycle: resource_a, resource_b, resource_a"
# Solusi: gunakan -target untuk break cycle, atau refactor dependency
Circular Dependency Resolution #
Circular dependency terjadi ketika dua atau lebih resource saling bergantung satu sama lain.
# Error message saat circular dependency:
# Error: Cycle: aws_security_group.a, aws_security_group.b, aws_security_group.a
# CONTOH CIRCULAR DEPENDENCY:
# SG-A allow ingress dari SG-B
# SG-B allow ingress dari SG-A
# → Terraform tidak bisa create keduanya!
# SOLUSI 1: Gabungkan menjadi satu security group
resource "aws_security_group" "combined" {
name = "combined-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
self = true # Allow dari diri sendiri = allow dari SG yang sama
}
}
# SOLUSI 2: Gunakan CIDR block bukan SG reference
resource "aws_security_group" "a" {
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["10.0.2.0/24"] # CIDR subnet B
}
}
resource "aws_security_group" "b" {
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["10.0.1.0/24"] # CIDR subnet A
}
}
Ringkasan #
- Implicit dependency adalah default — selalu prioritaskan referensi langsung untuk mendefinisikan dependency, bukan
depends_on.depends_onuntuk side-effect dependency — ketika resource bergantung pada efek samping (seperti IAM policy attachment) bukan pada nilai atribut.- Jangan over-use
depends_on— setiapdepends_onyang tidak perlu mengurangi paralelisme dan memperlambat apply.- Dependency melintasi module terbentuk dari referensi ke output module — mekanismenya sama dengan dependency antar resource biasa.
- Resource independen dieksekusi paralel — Terraform memaksimalkan efisiensi dengan menjalankan operasi yang tidak saling bergantung secara bersamaan.
terraform graphuntuk visualisasi — gunakan saat konfigurasi kompleks dan kamu perlu memahami urutan eksekusi yang sebenarnya.