Root vs Child Module #

Setiap konfigurasi Terraform yang kamu jalankan dengan terraform apply adalah root module — direktori tempat kamu berada. Root module adalah orkestrator: ia memanggil child module, mengoper nilai ke dalamnya, dan mengambil output darinya. Child module adalah unit kerja yang bisa dipanggil berkali-kali dari tempat berbeda. Memahami pembagian tanggung jawab ini — mana yang seharusnya ada di root, mana yang seharusnya ada di child — adalah kunci arsitektur Terraform yang bersih.

Root Module: Orkestrator #

Root module adalah titik masuk dari setiap terraform apply. Ia yang membaca variable dari tfvars, memanggil child module, dan mengelem semuanya bersama.

# environments/production/main.tf — ini adalah root module

# Root module memanggil child module dan mengoper konfigurasi
module "vpc" {
  source = "../../modules/vpc"

  cidr_block  = var.vpc_cidr      # Nilai dari variable root module
  environment = var.environment
}

module "eks" {
  source = "../../modules/eks"

  cluster_name       = "${var.environment}-eks"
  vpc_id             = module.vpc.vpc_id             # Output dari module lain
  private_subnet_ids = module.vpc.private_subnet_ids
}

module "rds" {
  source = "../../modules/rds"

  identifier         = "${var.environment}-db"
  vpc_id             = module.vpc.vpc_id
  private_subnet_ids = module.vpc.private_subnet_ids
}

# Root module juga bisa punya resource langsung (bukan di child module)
# untuk resource yang terlalu spesifik untuk di-abstraksi
resource "aws_route53_record" "app" {
  zone_id = var.hosted_zone_id
  name    = "app.${var.domain}"
  type    = "CNAME"
  ttl     = 300
  records = [module.eks.load_balancer_dns]
}

Child Module: Unit Kerja #

Child module berisi implementasi dari satu “konsep” infrastruktur. Ia tidak tahu di mana dipanggil, dari environment apa, atau bagaimana outputnya akan digunakan — ia hanya melakukan tugasnya berdasarkan input yang diterima.

# modules/rds/main.tf — ini adalah child module

# Child module tidak tahu tentang environment atau project
# Ia hanya menerima variable dan membuat resource

resource "aws_db_subnet_group" "this" {
  name       = "${var.identifier}-subnet-group"
  subnet_ids = var.subnet_ids

  tags = merge(var.tags, {
    Name = "${var.identifier}-subnet-group"
  })
}

resource "aws_security_group" "this" {
  name   = "${var.identifier}-sg"
  vpc_id = var.vpc_id

  ingress {
    from_port   = 5432
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = var.allowed_cidr_blocks
  }
}

resource "aws_db_instance" "this" {
  identifier        = var.identifier
  engine            = var.engine
  instance_class    = var.instance_class
  allocated_storage = var.allocated_storage

  db_subnet_group_name   = aws_db_subnet_group.this.name
  vpc_security_group_ids = [aws_security_group.this.id]

  tags = var.tags
}

Sumber Child Module: Berbagai Jenis Source #

Child module bisa berasal dari berbagai sumber. Pilihan source mempengaruhi cara versioning dan update module.

# SOURCE 1: Path lokal — untuk module di monorepo yang sama
module "vpc" {
  source = "../../modules/vpc"
  # Path relatif dari direktori root module saat ini
}

# SOURCE 2: Terraform Registry — module publik dari registry.terraform.io
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"
  # Format: <namespace>/<module>/<provider>
}

# SOURCE 3: Git repository — module di repo terpisah
module "vpc" {
  source = "git::https://github.com/org/terraform-module-vpc.git?ref=v2.1.0"
  # ?ref= bisa berupa tag, branch, atau commit hash
}

# SOURCE 4: Git dengan SSH
module "vpc" {
  source = "git::ssh://[email protected]/org/terraform-module-vpc.git?ref=v2.1.0"
}

# SOURCE 5: Subdirektori dalam Git repository
module "vpc" {
  source = "git::https://github.com/org/terraform-modules.git//modules/vpc?ref=v1.0.0"
  # // (double slash) memisahkan repo URL dari path dalam repo
}

Bagaimana State Mengelola Resource dari Child Module #

Resource yang dibuat oleh child module masuk ke state root module — bukan state terpisah. Address-nya mencerminkan hierarki module.

terraform state list

# Output — resource dari child module diberi prefix module.<nama>:
# module.vpc.aws_vpc.this
# module.vpc.aws_subnet.public[0]
# module.vpc.aws_subnet.public[1]
# module.vpc.aws_subnet.private[0]
# module.vpc.aws_internet_gateway.this
# module.eks.aws_eks_cluster.this
# module.eks.aws_eks_node_group.workers
# module.rds.aws_db_instance.this
# module.rds.aws_security_group.this
# aws_route53_record.app    ← Resource langsung di root module (tanpa prefix module.)
# Operasi state pada resource di dalam module
terraform state show module.vpc.aws_vpc.this
terraform state rm module.vpc.aws_subnet.public[0]

# Target plan/apply untuk module tertentu
terraform plan -target=module.vpc
terraform apply -target=module.rds

Komposisi Module: Module Memanggil Module #

Child module bisa memanggil child module lain — tapi ini perlu dilakukan dengan hati-hati agar tidak menciptakan hierarki yang terlalu dalam.

# modules/eks/main.tf — child module yang memanggil child module lain

# EKS module memanggil IAM module untuk membuat role yang dibutuhkan
module "cluster_iam" {
  source = "../iam-role"

  name               = "${var.cluster_name}-cluster-role"
  assume_role_service = "eks.amazonaws.com"
  policy_arns = [
    "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
  ]
}

resource "aws_eks_cluster" "this" {
  name     = var.cluster_name
  role_arn = module.cluster_iam.role_arn  # Output dari sub-module

  vpc_config {
    subnet_ids = var.subnet_ids
  }
}
REKOMENDASI KEDALAMAN HIERARKI:

Root Module
  └── Child Module (level 1) ← IDEAL, mudah dipahami
      └── Child Module (level 2) ← MASIH OK untuk abstraksi yang jelas
          └── Child Module (level 3) ← MULAI KOMPLEKS, pertimbangkan ulang
              └── ... ← HINDARI — sulit di-debug, sulit di-test

Perbedaan Tanggung Jawab #

ROOT MODULE BERTANGGUNG JAWAB UNTUK:
  ✓ Komposisi — memanggil module yang tepat
  ✓ Konfigurasi environment — nilai yang berbeda per environment
  ✓ "Glue" antar module — mengoper output satu module ke input module lain
  ✓ Resource yang sangat spesifik untuk deployment tertentu
  ✓ Backend configuration dan provider configuration
  ✓ Output yang mengekspos nilai ke sistem eksternal

CHILD MODULE BERTANGGUNG JAWAB UNTUK:
  ✓ Implementasi satu "konsep" infrastruktur
  ✓ Validasi input yang diterima
  ✓ Mengekspos output yang berguna untuk pemanggil
  ✓ Defaults yang masuk akal untuk konfigurasi yang tidak wajib
  ✗ JANGAN: hardcode nilai yang seharusnya bisa dikonfigurasi
  ✗ JANGAN: asumsikan context deployment (environment, region)

Ringkasan #

  • Root module adalah orkestrator — memanggil child module, mengoper nilai, dan menyambungkan output satu module ke input module lain.
  • Child module adalah unit kerja yang tidak tahu di mana ia dipanggil — hanya menerima input dan menghasilkan output.
  • Resource dari child module masuk ke state root module dengan address module.<nama>.<type>.<name> — bukan state terpisah.
  • Empat jenis source: path lokal (monorepo), Terraform Registry, Git repository, dan subdirektori Git.
  • Pin versi module eksternal dengan version atau ?ref= — jangan biarkan module selalu mengambil versi terbaru tanpa kontrol.
  • Batasi kedalaman hierarki — module yang memanggil module yang memanggil module membuat debugging jauh lebih sulit.

← Sebelumnya: Struktur   Berikutnya: Versioning →

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