Output Contract #

Ketika modul Terraform dipakai oleh satu tim, mengubah output adalah pekerjaan kecil. Tapi ketika modul dipakai oleh sepuluh tim sekaligus, menghapus atau mengganti nama satu output bisa memecah semua konfigurasi yang bergantung padanya. Output bukan hanya mekanisme teknis — ia adalah kontrak antara penulis modul dan pemanggilnya. Memahami cara merancang kontrak ini dengan baik adalah keterampilan yang membedakan modul yang cepat dipakai lagi dari modul yang cepat ditinggalkan.

Output sebagai Public API #

Analogi terbaik untuk output modul adalah public API dari sebuah library. Begitu API tersebut dipakai, mengubahnya butuh pertimbangan tentang backward compatibility.

KONTRAK YANG BAIK:

  Module "vpc" menjanjikan:
  - output vpc_id         → selalu ada, tipe string
  - output public_subnet_ids  → selalu ada, tipe list(string)
  - output private_subnet_ids → selalu ada, tipe list(string)

  Pemangil bisa bergantung pada kontrak ini:
  module.vpc.vpc_id             → AMAN digunakan
  module.vpc.public_subnet_ids  → AMAN digunakan

BREAKING CHANGE YANG HARUS DIHINDARI:
  - Menghapus output yang sudah ada
  - Mengubah tipe output (string → list)
  - Mengubah nama output
  - Mengubah struktur output object secara tidak backward-compatible

Prinsip Desain Output yang Baik #

# PRINSIP 1: Nama output yang deskriptif dan konsisten

# ANTI-PATTERN: Nama yang ambigu
output "id" {
  value = aws_vpc.main.id  # ID apa? VPC? Subnet? Instance?
}

output "ip" {
  value = aws_instance.web.public_ip  # IP publik atau privat?
}

# BENAR: Nama yang eksplisit tentang resource dan atribut
output "vpc_id" {
  value = aws_vpc.main.id
}

output "web_instance_public_ip" {
  value = aws_instance.web.public_ip
}
# PRINSIP 2: description yang informatif — menjelaskan UNTUK APA output ini dipakai

# ANTI-PATTERN: Description yang mengulang nama
output "vpc_id" {
  description = "The VPC ID"  # ✗ Tidak menambah informasi
  value       = aws_vpc.main.id
}

# BENAR: Description yang menjelaskan konteks penggunaan
output "vpc_id" {
  description = "ID VPC utama — digunakan oleh modul compute dan database untuk menempatkan resource di network yang benar."
  value       = aws_vpc.main.id
}
# PRINSIP 3: Konsistensi tipe — jangan campur tipe untuk output yang serupa

# ANTI-PATTERN: Tipe output yang tidak konsisten
output "public_subnet_id" {
  value = aws_subnet.public[0].id  # String — hanya satu subnet
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id  # List — bisa banyak subnet
}
# Pemangil harus tahu kapan pakai string dan kapan pakai list

# BENAR: Konsisten menggunakan list meskipun hanya satu element
output "public_subnet_ids" {
  value = aws_subnet.public[*].id  # Selalu list
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id  # Selalu list
}

Mengelompokkan Output Terkait #

Daripada banyak output terpisah untuk satu resource, output yang terstruktur lebih mudah digunakan dan lebih tahan terhadap breaking change.

# ANTI-PATTERN: Output yang terlalu granular
output "lb_dns_name" {
  value = aws_lb.main.dns_name
}
output "lb_arn" {
  value = aws_lb.main.arn
}
output "lb_zone_id" {
  value = aws_lb.main.zone_id
}
output "lb_security_group_id" {
  value = aws_security_group.lb.id
}
# 4 output terpisah untuk satu resource — rentan breaking change jika ada yang ditambah/hapus

# BENAR: Output terstruktur dalam satu object
output "load_balancer" {
  description = "Informasi load balancer — DNS, ARN, zone ID, dan security group"
  value = {
    dns_name         = aws_lb.main.dns_name
    arn              = aws_lb.main.arn
    zone_id          = aws_lb.main.zone_id
    security_group_id = aws_security_group.lb.id
  }
}
# Lebih mudah diperluas — tambah field baru tanpa breaking existing usage
# module.app.load_balancer.dns_name → tetap valid

Menambah Output Tanpa Breaking Change #

Menambah output baru selalu aman. Yang berbahaya adalah mengubah atau menghapus output yang sudah ada.

# AMAN: Menambah output baru
# Pemangil lama tidak terpengaruh, pemangil baru bisa memanfaatkan output baru

# Versi 1.0 module:
output "vpc_id" { value = aws_vpc.main.id }
output "private_subnet_ids" { value = aws_subnet.private[*].id }

# Versi 1.1 module (backward compatible — hanya tambah):
output "vpc_id" { value = aws_vpc.main.id }              # Tetap ada
output "private_subnet_ids" { value = aws_subnet.private[*].id }  # Tetap ada
output "public_subnet_ids" { value = aws_subnet.public[*].id }    # BARU — aman
output "nat_gateway_ips" { value = aws_eip.nat[*].public_ip }     # BARU — aman
# BERBAHAYA: Mengubah nama atau tipe output yang sudah ada
# Semua pemangil yang menggunakan output ini akan error

# JANGAN lakukan ini tanpa major version bump:
# Sebelum: output "subnet_ids" { value = ... }
# Sesudah: output "private_subnet_ids" { value = ... }  ← rename = breaking change

# Jika harus rename, beri masa transisi:
output "subnet_ids" {
  value       = aws_subnet.private[*].id
  description = "DEPRECATED: Gunakan private_subnet_ids. Akan dihapus di v3.0."
}

output "private_subnet_ids" {
  value       = aws_subnet.private[*].id
  description = "List ID private subnet"
}

Output untuk Integrasi dengan Sistem Eksternal #

Output tidak hanya untuk sesama konfigurasi Terraform — sering kali nilainya perlu dikonsumsi oleh sistem lain.

# Output yang dirancang untuk dikonsumsi oleh sistem eksternal
# sebaiknya dalam format yang mudah diparse

output "connection_strings" {
  description = "Connection string untuk berbagai komponen — digunakan oleh deployment pipeline"
  sensitive   = true  # Mengandung kredensial
  value = {
    database = "postgresql://${aws_db_instance.main.username}@${aws_db_instance.main.endpoint}/${aws_db_instance.main.db_name}"
    redis    = "redis://${aws_elasticache_cluster.main.cache_nodes[0].address}:${aws_elasticache_cluster.main.cache_nodes[0].port}"
  }
}

output "kubernetes_config" {
  description = "Konfigurasi untuk setup kubectl — digunakan oleh CI/CD pipeline"
  value = {
    cluster_name     = aws_eks_cluster.main.name
    cluster_endpoint = aws_eks_cluster.main.endpoint
    cluster_region   = var.aws_region
  }
}
# Konsumsi output dari CI/CD pipeline
DB_ENDPOINT=$(terraform output -raw database_endpoint)
CLUSTER_NAME=$(terraform output -json kubernetes_config | jq -r '.cluster_name')

aws eks update-kubeconfig \
  --name "$CLUSTER_NAME" \
  --region "$(terraform output -json kubernetes_config | jq -r '.cluster_region')"

Ringkasan #

  • Output adalah kontrak publik — begitu dipakai orang lain, perubahan butuh pertimbangan backward compatibility.
  • Nama output yang eksplisitvpc_id, web_instance_public_ip lebih baik dari id atau ip yang ambigu.
  • Description yang menjelaskan penggunaan, bukan mengulang nama — “digunakan oleh modul compute untuk…” jauh lebih berguna.
  • Output terstruktur sebagai object lebih mudah diperluas tanpa breaking change dibanding banyak output terpisah per atribut.
  • Menambah output selalu aman, menghapus atau rename selalu breaking — jika harus, beri masa transisi dengan deprecation notice.
  • Tandai sensitive = true untuk output yang mengandung credential atau data sensitif lain.

← Sebelumnya: Apa itu Output?   Berikutnya: Sensitive Output →

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