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


Referensi Silang antara AWS dan Provider Lain #

Terraform bisa menggabungkan resource dari berbagai provider dalam satu konfigurasi.

# Contoh: AWS EC2 + Cloudflare DNS
resource "aws_instance" "web" {
  ami           = "ami-12345"
  instance_type = "t3.micro"
}

resource "cloudflare_record" "web" {
  zone_id = var.cloudflare_zone_id
  name    = "app"
  value   = aws_instance.web.public_ip  # Referensi ke AWS
  type    = "A"
  ttl     = 300
}

# Contoh: AWS + Datadog monitoring
resource "datadog_monitor" "cpu" {
  name    = "High CPU - ${aws_instance.web.id}"
  type    = "metric alert"
  query   = "avg(last_5m):avg:system.cpu.user{host:${aws_instance.web.id}} > 80"
  message = "CPU tinggi di ${aws_instance.web.public_ip}"
}
flowchart LR
    A["AWS EC2
instance"] -->|"public_ip"| B["Cloudflare
DNS record"]
    A -->|"instance_id"| C["Datadog
monitor"]
    A -->|"public_ip"| D["PagerDuty
service"]

    style A fill:#fff3e0,stroke:#e65100
    style B fill:#f3e5f5,stroke:#6a1b9a
    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#fce4ec,stroke:#c62828

Terraform Provider Dependency Graph #

Ketika menggunakan multiple provider, Terraform harus mengunduh dan menginisialisasi semuanya.

# Inisialisasi semua provider
terraform init
# Output:
# - Installing hashicorp/aws v5.30.0...
# - Installing cloudflare/cloudflare v4.20.0...
# - Installing datadog/datadog-aws v3.33.0...
# Terraform has been successfully initialized!

# Cek provider yang dibutuhkan
terraform providers
# ├── provider[registry.terraform.io/hashicorp/aws]
# ├── provider[registry.terraform.io/cloudflare/cloudflare]
# └── provider[registry.terraform.io/datadog/datadog-aws]

# Lock provider versions
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64
# Generate .terraform.lock.hcl untuk konsistensi cross-platform
flowchart TD
    A["terraform init"] --> B["Download AWS
provider"]
    A --> C["Download Cloudflare
provider"]
    A --> D["Download Datadog
provider"]
    B --> E["Initialize
provider"]
    C --> E
    D --> E
    E --> F["Ready to
plan/apply"]

    style A fill:#e3f2fd,stroke:#1565c0
    style F fill:#e8f5e9,stroke:#2e7d32

Provider Configuration Best Practices #

# BEST PRACTICE: Konfigurasi provider di satu tempat
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
  }
  required_version = ">= 1.6.0"
}

# Provider default
provider "aws" {
  region = var.aws_region
  
  default_tags {
    tags = {
      Project     = var.project_name
      Environment = var.environment
      ManagedBy   = "terraform"
    }
  }
}

# Provider alias
provider "aws" {
  alias  = "us_east"
  region = "us-east-1"
}

# Provider non-AWS
provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

Provider Version Compatibility #

# Saat menggunakan multi-provider, pastikan version compatible

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
    datadog = {
      source  = "datadog/datadog"
      version = "~> 3.0"
    }
  }
  required_version = ">= 1.6.0"
}
# Check compatibility issues
terraform init 2>&1 | grep "incompatible"

# Lock provider versions untuk semua platforms
terraform providers lock   -platform=linux_amd64   -platform=darwin_arm64   -platform=windows_amd64

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