State Sharing #
Ketika infrastruktur tumbuh dan dipecah menjadi beberapa konfigurasi Terraform yang terpisah, muncul kebutuhan yang tidak terhindarkan: konfigurasi A perlu tahu output dari konfigurasi B. Tim networking membuat VPC, tim compute perlu VPC ID itu untuk membuat EC2. Tim platform membuat EKS, tim aplikasi perlu endpoint cluster untuk deploy workload. Bagaimana informasi ini berpindah antar konfigurasi adalah keputusan arsitektur yang punya implikasi jangka panjang pada coupling, fleksibilitas, dan kemudahan troubleshooting.
Dua Pendekatan State Sharing #
Ada dua cara utama untuk berbagi informasi antar konfigurasi Terraform yang terpisah, dengan trade-off yang sangat berbeda.
PENDEKATAN 1: terraform_remote_state
Cara : Konfigurasi B membaca state konfigurasi A secara langsung
Coupling: Ketat — B harus tahu detail backend A
Fleksibel: Rendah — perubahan output A bisa breaking B
Debugging: Sulit — dependency tersembunyi di dalam konfigurasi
PENDEKATAN 2: Variable Input
Cara : Nilai dioper secara eksplisit saat terraform apply
Coupling: Longgar — B hanya menerima nilai, tidak tahu asalnya
Fleksibel: Tinggi — sumber nilai bisa berubah tanpa modifikasi B
Debugging: Mudah — dependency eksplisit dan terlihat jelas
terraform_remote_state: Ketika Coupling Bisa Diterima #
terraform_remote_state paling masuk akal ketika hubungan antar konfigurasi sangat stabil dan dikelola oleh tim yang sama.
# Konfigurasi "networking" menghasilkan output:
# outputs.tf di workspace networking
output "vpc_id" {
value = aws_vpc.main.id
description = "VPC ID — dikonsumsi oleh workspace compute dan database"
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
}
output "database_subnet_ids" {
value = aws_subnet.database[*].id
}
# Konfigurasi "compute" membaca state networking
data "terraform_remote_state" "networking" {
backend = "s3"
config = {
bucket = "my-terraform-state"
key = "production/networking/terraform.tfstate"
region = "ap-southeast-1"
}
}
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
subnet_id = data.terraform_remote_state.networking.outputs.private_subnet_ids[0]
}
KAPAN terraform_remote_state MASUK AKAL:
✓ Konfigurasi dikelola oleh tim yang sama
✓ Output yang dibaca stabil (tidak sering berubah nama/tipe)
✓ Dependency antar konfigurasi memang by design (bukan kebetulan)
✓ Konfigurasi sumber lebih "foundational" dari konfigurasi konsumen
(networking → compute, bukan sebaliknya)
KAPAN HINDARI terraform_remote_state:
✗ Konfigurasi dikelola tim yang berbeda tanpa koordinasi
✗ Output di konfigurasi sumber sering berubah
✗ Ada circular dependency (A membaca state B, B membaca state A)
✗ Konfigurasi konsumen butuh berjalan secara independen
(misalnya: testing, development isolated)
Variable Input: Decoupling yang Lebih Sehat #
Alih-alih membaca state secara langsung, konfigurasi menerima nilai yang dibutuhkan sebagai variable input. Sumber nilai tersebut bisa dari output konfigurasi lain, dari CI/CD pipeline, atau dari file tfvars.
# Konfigurasi "compute" menerima nilai sebagai variable — tidak tahu asalnya
variable "vpc_id" {
description = "ID VPC tempat resource akan ditempatkan"
type = string
}
variable "private_subnet_ids" {
description = "List ID private subnet"
type = list(string)
}
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
subnet_id = var.private_subnet_ids[0]
}
# Cara 1: Ambil output dari konfigurasi lain secara manual
cd environments/production/networking
NETWORKING_OUTPUTS=$(terraform output -json)
VPC_ID=$(echo $NETWORKING_OUTPUTS | jq -r '.vpc_id.value')
SUBNET_IDS=$(echo $NETWORKING_OUTPUTS | jq -r '.private_subnet_ids.value | @json')
# Lalu oper ke konfigurasi compute
cd ../compute
terraform apply \
-var="vpc_id=$VPC_ID" \
-var="private_subnet_ids=$SUBNET_IDS"
# Cara 2: CI/CD pipeline yang mengoper nilai antar konfigurasi
# .github/workflows/deploy.yml
jobs:
deploy-networking:
outputs:
vpc_id: ${{ steps.outputs.outputs.vpc_id }}
subnet_ids: ${{ steps.outputs.outputs.subnet_ids }}
steps:
- name: Apply networking
run: terraform apply -auto-approve
working-directory: environments/production/networking
- name: Capture outputs
id: outputs
run: |
echo "vpc_id=$(terraform output -raw vpc_id)" >> $GITHUB_OUTPUT
echo "subnet_ids=$(terraform output -json private_subnet_ids)" >> $GITHUB_OUTPUT
working-directory: environments/production/networking
deploy-compute:
needs: deploy-networking
steps:
- name: Apply compute
run: |
terraform apply -auto-approve \
-var="vpc_id=${{ needs.deploy-networking.outputs.vpc_id }}" \
-var='private_subnet_ids=${{ needs.deploy-networking.outputs.subnet_ids }}'
working-directory: environments/production/compute
Hybrid: Outputs File sebagai Interface Eksplisit #
Pola ini membuat dependency lebih eksplisit dibanding terraform_remote_state tapi lebih otomatis dari oper manual via CLI.
# Setelah networking apply, generate file outputs.auto.tfvars
# yang akan otomatis dibaca oleh konfigurasi compute
# Script atau CI step setelah networking apply:
# terraform output -json | jq '{
# vpc_id: .vpc_id.value,
# private_subnet_ids: .private_subnet_ids.value
# }' > ../compute/networking-outputs.auto.tfvars.json
# environments/compute akan membaca file ini otomatis
# (*.auto.tfvars.json dibaca tanpa perlu -var-file)
Anti-Pattern: Circular Dependency #
# ANTI-PATTERN: A membaca state B, B membaca state A
# Circular dependency yang tidak bisa di-apply tanpa memecah siklus
# networking/main.tf:
data "terraform_remote_state" "compute" {
# Networking baca output dari compute
config = { key = "production/compute/terraform.tfstate" ... }
}
# compute/main.tf:
data "terraform_remote_state" "networking" {
# Compute baca output dari networking
config = { key = "production/networking/terraform.tfstate" ... }
}
# Masalah:
# - networking tidak bisa di-apply karena butuh state compute
# - compute tidak bisa di-apply karena butuh state networking
# - Tidak ada yang bisa jalan pertama kali (chicken-and-egg)
# SOLUSI: Identifikasi resource yang menjadi "foundational"
# dan pisahkan ke konfigurasi yang tidak bergantung pada konfigurasi lain
Menentukan Granularitas Pemisahan #
TERLALU MONOLITIK:
Satu konfigurasi untuk seluruh infrastruktur
→ Plan lambat, blast radius besar, bottleneck kolaborasi
TERLALU TERFRAGMENTASI:
50 konfigurasi kecil yang saling bergantung
→ Orkestrasi rumit, banyak state sharing, sulit dipahami
GRANULARITAS YANG TEPAT — pisahkan berdasarkan:
LIFECYCLE: Resource yang berubah dengan frekuensi berbeda
Networking (jarang berubah) → konfigurasi tersendiri
Compute (sering berubah) → konfigurasi tersendiri
Aplikasi (sering deploy) → konfigurasi tersendiri
OWNERSHIP: Resource yang dimiliki tim berbeda
Tim networking → konfigurasi networking
Tim platform → konfigurasi platform (EKS, RDS)
Tim aplikasi → konfigurasi aplikasi
BLAST RADIUS: Resource yang tidak boleh saling mempengaruhi
Production database → konfigurasi tersendiri dengan akses sangat terbatas
Networking production → konfigurasi tersendiri
Ringkasan #
- Dua pendekatan:
terraform_remote_state(coupling ketat, mudah setup) vs variable input (decoupled, lebih fleksibel). Pilih berdasarkan seberapa stabil hubungan antar konfigurasi.terraform_remote_stateuntuk konfigurasi yang sangat terkait dan dikelola tim yang sama — networking → compute adalah contoh klasik yang tepat.- Variable input untuk decoupling antar tim — konfigurasi tidak perlu tahu siapa yang menyediakan nilainya, hanya perlu tahu nilainya sendiri.
- CI/CD pipeline bisa menjadi orkestrator yang mengambil output dari satu konfigurasi dan mengopernya sebagai variable ke konfigurasi berikutnya.
- Hindari circular dependency — jika A butuh B dan B butuh A, identifikasi resource foundational dan pisahkan ke lapisan yang tidak bergantung pada siapapun.
- Granularitas pemisahan berdasarkan lifecycle (seberapa sering berubah), ownership (siapa yang mengelola), dan blast radius (apa yang tidak boleh saling mempengaruhi).
← Sebelumnya: Cross Provider Reference Berikutnya: CI Pipeline →