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:#2e7d32
resource "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:#2e7d32
module "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:#2e7d32

Dependency 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_on untuk side-effect dependency — ketika resource bergantung pada efek samping (seperti IAM policy attachment) bukan pada nilai atribut.
  • Jangan over-use depends_on — setiap depends_on yang 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 graph untuk visualisasi — gunakan saat konfigurasi kompleks dan kamu perlu memahami urutan eksekusi yang sebenarnya.

← Sebelumnya: Operation   Berikutnya: Lifecycle →

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