Cross Provider Reference #

Ketika kamu mengelola infrastruktur dengan beberapa provider sekaligus, resource dari provider yang berbeda hampir pasti perlu saling mengetahui satu sama lain. Load balancer AWS perlu tahu domain Cloudflare mana yang akan diarahkan ke sana. Kubernetes deployment perlu tahu endpoint database yang baru saja dibuat di RDS. Secret di Vault perlu diisi dengan password yang digenerate oleh AWS. Cross provider reference adalah cara Terraform mengekspresikan ketergantungan ini, dan memahaminya membantumu membangun konfigurasi multi-provider yang kohesif.

Cara Cross Provider Reference Bekerja #

Referensi lintas provider bekerja persis sama dengan referensi biasa — kamu cukup menggunakan address resource dari provider lain. Terraform secara otomatis membangun dependency graph yang melintasi batas provider.

provider "aws" {
  region = "ap-southeast-1"
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

# Resource dari provider AWS
resource "aws_lb" "main" {
  name               = "production-lb"
  internal           = false
  load_balancer_type = "application"
  subnets            = module.vpc.public_subnet_ids
}

# Resource dari provider Cloudflare yang mereferensikan resource AWS
resource "cloudflare_record" "app" {
  zone_id = var.cloudflare_zone_id
  name    = "app"
  value   = aws_lb.main.dns_name  # ← Cross-provider reference
  type    = "CNAME"
  proxied = true
}

# Dependency graph yang terbentuk:
# aws_lb.main → cloudflare_record.app
# (cloudflare_record.app tidak bisa dibuat sebelum aws_lb.main selesai)

Pola Umum Cross Provider Reference #

AWS + Cloudflare: DNS Otomatis #

# Sertifikat SSL di AWS ACM
resource "aws_acm_certificate" "main" {
  domain_name       = "myapp.com"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

# Record DNS untuk validasi sertifikat — di Cloudflare
resource "cloudflare_record" "acm_validation" {
  for_each = {
    for dvo in aws_acm_certificate.main.domain_validation_options :
    dvo.domain_name => {
      name  = dvo.resource_record_name
      value = dvo.resource_record_value
      type  = dvo.resource_record_type
    }
  }

  zone_id = var.cloudflare_zone_id
  name    = each.value.name
  value   = each.value.value
  type    = each.value.type
  proxied = false  # Tidak di-proxy untuk validasi
  ttl     = 60
}

# Tunggu sampai sertifikat tervalidasi
resource "aws_acm_certificate_validation" "main" {
  certificate_arn         = aws_acm_certificate.main.arn
  validation_record_fqdns = [for record in cloudflare_record.acm_validation : record.hostname]
}

AWS + Kubernetes: Deploy Aplikasi ke EKS #

provider "aws" {
  region = "ap-southeast-1"
}

provider "kubernetes" {
  host                   = aws_eks_cluster.main.endpoint
  cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)

  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    command     = "aws"
    args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.main.name]
  }
}

# EKS cluster dari AWS provider
resource "aws_eks_cluster" "main" {
  name     = "production-eks"
  role_arn = aws_iam_role.eks_cluster.arn

  vpc_config {
    subnet_ids = module.vpc.private_subnet_ids
  }
}

# Kubernetes namespace dari Kubernetes provider
# (bergantung pada EKS cluster yang baru dibuat)
resource "kubernetes_namespace" "app" {
  metadata {
    name = "production"
  }

  # depends_on eksplisit karena provider kubernetes bergantung
  # pada EKS cluster yang baru dibuat
  depends_on = [aws_eks_cluster.main]
}

# ConfigMap dengan endpoint database dari AWS RDS
resource "kubernetes_config_map" "app_config" {
  metadata {
    name      = "app-config"
    namespace = kubernetes_namespace.app.metadata[0].name
  }

  data = {
    DB_HOST = aws_db_instance.main.address  # Cross-provider reference
    DB_PORT = aws_db_instance.main.port
    DB_NAME = aws_db_instance.main.db_name
  }
}

AWS + HashiCorp Vault: Secret Management #

provider "aws" {
  region = "ap-southeast-1"
}

provider "vault" {
  address = "https://vault.mycompany.com"
}

# Password database digenerate Vault
resource "vault_database_secret_backend_role" "app" {
  name    = "app-role"
  backend = vault_database_secret_backend.main.path
  db_name = vault_database_secret_backend_connection.rds.name
}

# RDS menggunakan endpoint yang akan dikonfigurasikan ke Vault
resource "aws_db_instance" "main" {
  identifier     = "production-db"
  engine         = "postgres"
  instance_class = "db.t3.medium"
}

# Konfigurasi Vault untuk database connection
resource "vault_database_secret_backend_connection" "rds" {
  backend = vault_database_secret_backend.main.path
  name    = "rds-postgres"

  postgresql {
    connection_url = "postgresql://{{username}}:{{password}}@${aws_db_instance.main.endpoint}/${aws_db_instance.main.db_name}"
    # Cross-provider reference: gunakan endpoint RDS dari AWS
  }
}

Masalah Umum dan Solusinya #

# MASALAH 1: Provider Kubernetes dikonfigurasi sebelum cluster ada
# Error: "no such host" atau connection refused

# ANTI-PATTERN: Provider kubernetes dikonfigurasi di terraform block
# tanpa depends_on — terraform mencoba koneksi saat init
provider "kubernetes" {
  host = aws_eks_cluster.main.endpoint  # ✗ Belum tentu ada saat provider diinisialisasi
}

# BENAR: Gunakan exec-based authentication yang lazy (koneksi hanya saat dibutuhkan)
provider "kubernetes" {
  host                   = aws_eks_cluster.main.endpoint
  cluster_ca_certificate = base64decode(aws_eks_cluster.main.certificate_authority[0].data)

  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    command     = "aws"
    args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.main.name]
  }
  # exec-based auth hanya berjalan saat resource kubernetes benar-benar dibuat
}
# MASALAH 2: Race condition antar provider
# Resource B dari provider B dibuat sebelum resource A dari provider A selesai
# meskipun ada referensi

# BENAR: Pastikan referensi sudah cukup, atau tambahkan depends_on
resource "cloudflare_record" "app" {
  value = aws_lb.main.dns_name  # Referensi sudah cukup untuk create ordering

  # Untuk kasus yang lebih kompleks (side effect yang tidak terlihat):
  depends_on = [aws_lb_listener.https]
}
# MASALAH 3: Nilai "known after apply" dari satu provider
# dibutuhkan untuk mengkonfigurasi provider lain

# Ini adalah limitasi yang perlu workaround
# (biasanya dipecah menjadi dua terraform apply terpisah,
# atau menggunakan data source untuk membaca setelah resource dibuat)

Dependency Graph Lintas Provider #

# Terraform membangun dependency graph yang memperhitungkan
# semua referensi lintas provider

terraform graph | dot -Tsvg > graph.svg

# Graph akan menunjukkan:
# [aws provider] aws_lb.main
#      │
#      └──────────────────────────────────┐
#                                         ▼
#                              [cloudflare provider] cloudflare_record.app
#
# Terraform memahami bahwa cloudflare_record.app harus dibuat
# SETELAH aws_lb.main — meskipun keduanya dikelola provider yang berbeda

Ringkasan #

  • Cross-provider reference bekerja sama seperti referensi biasa — cukup gunakan address resource dari provider lain. Terraform otomatis membangun dependency lintas provider.
  • Tiga pola umum: AWS + Cloudflare (DNS otomatis), AWS + Kubernetes (deploy ke EKS), AWS + Vault (secret management).
  • Provider Kubernetes perlu konfigurasi khusus — gunakan exec-based auth agar koneksi bersifat lazy dan tidak gagal saat cluster belum ada.
  • depends_on untuk side-effect dependency yang tidak terlihat dari referensi langsung — misalnya Cloudflare record bergantung pada listener yang sudah selesai, bukan hanya load balancer.
  • Nilai “known after apply” dari provider A tidak bisa langsung digunakan untuk mengkonfigurasi provider B — ini limitasi yang kadang memerlukan dua apply terpisah.
  • Terraform graph tetap kohesif meskipun resource dari berbagai provider — urutan eksekusi dihitung dari seluruh dependency, tidak per-provider.

← Sebelumnya: Apa itu Multi Provider?   Berikutnya: State Sharing →

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