Terraform #

Dalam era cloud native, komputasi serverless telah meredefinisi cara kita mendeploy dan menjalankan aplikasi. Dengan mengeliminasi kebutuhan untuk merawat server fisik atau mesin virtual, serverless menawarkan kecepatan deployment yang luar biasa dan penskalaan otomatis yang sangat efisien. Namun, ada satu realitas penting yang sering kali diabaikan oleh tim pengembang: meskipun server fisik menghilang dari pandangan kita, infrastruktur itu sendiri tetap ada dan bahkan menjadi lebih terfragmentasi.

Saat kita membangun aplikasi serverless berskala produksi di Google Cloud Platform (GCP), sistem kita tidak hanya terdiri dari satu potongan kode. Di lapangan, arsitektur serverless yang lengkap biasanya mencakup puluhan layanan Cloud Run, beberapa fungsi Cloud Functions, saluran antrean Pub/Sub, scheduler cron, rahasia di Secret Manager, pemicu Eventarc, orkestrator Workflows, serta ratusan kebijakan hak akses Identity and Access Management (IAM). Mengonfigurasi seluruh jaring laba-laba infrastruktur ini secara manual melalui Google Cloud Console (antarmuka grafis/GUI) adalah tindakan berisiko tinggi yang memicu kesalahan manusia (human error), inkonsistensi antar lingkungan kerja (dev, staging, prod), serta ketiadaan jejak audit perubahan (audit trail).

Untuk mengatasi tantangan tersebut, Terraform hadir sebagai alat Infrastructure as Code (IaC) deklaratif terdepan di industri. Dengan menuliskan definisi infrastruktur serverless kita ke dalam file kode deklaratif HashiCorp Configuration Language (HCL), Terraform memungkinkan kita memprovisikan, memperbarui, dan mengelola seluruh ekosistem serverless GCP sebagai single source of truth yang dapat dilacak oleh Git, direview melalui Pull Request, dan dideploy secara otomatis melalui pipeline CI/CD.


Arsitektur Infrastructure as Code untuk Ekosistem Serverless #

Sebelum masuk ke detail penulisan kode HCL, kita perlu memahami konsep arsitektur internal Terraform dan bagaimana ia berinteraksi dengan Google Cloud API secara aman dan andal.

1. Sistem Deklaratif dan Dependency Graph #

Terraform bekerja dengan model Deklaratif. Artinya, kita menuliskan kondisi akhir (desired state) dari infrastruktur yang kita inginkan (misal: "buat layanan Cloud Run bernama backend dengan memori 1GB"), dan Terraform yang akan menganalisis perbedaan antara kondisi nyata di cloud saat ini dengan kondisi yang kita inginkan.

Terraform membangun sebuah Dependency Graph internal yang memetakan dependensi antar resource. Misalnya, jika trigger Eventarc membutuhkan Pub/Sub topic, dan Pub/Sub topic membutuhkan service account, Terraform secara otomatis mengetahui urutan pembuatan resource tersebut tanpa perlu kita instruksikan secara manual.

2. Siklus Hidup Perintah Terraform secara Detail #

Siklus kerja Terraform dikelola melalui empat perintah utama yang berurutan:

  • terraform init: Menginisialisasi direktori kerja. Pada tahap ini, Terraform membaca kode konfigurasi, mengunduh plugin Google Provider dari HashiCorp Registry, serta menyiapkan plugin backend penyimpanan state. File lock ketergantungan .terraform.lock.hcl juga dibuat untuk mencatat checksum versi provider guna menjamin konsistensi build di laptop developer maupun server CI/CD.
  • terraform plan: Melakukan analisis komparatif. Terraform membaca kondisi infra saat ini menggunakan API GCP, membandingkannya dengan state file, lalu menghasilkan daftar rencana tindakan. Kita dapat menggunakan opsi -out=tfplan untuk menyimpan rencana tindakan ini ke dalam file fisik yang terenkripsi, memastikan bahwa hanya rencana yang disetujui tersebut yang akan dieksekusi di langkah berikutnya.
  • terraform apply: Mengeksekusi instruksi plan ke GCP API. Sebelum menerapkan perubahan, Terraform menggunakan fitur locking di backend (misalnya GCS lock) untuk menahan akses write dari user lain. Setelah eksekusi sukses, state file diperbarui dan variabel keluaran (outputs) akan dicetak ke terminal.
  • terraform destroy: Digunakan untuk menghapus seluruh resource yang terdaftar di file state. Perintah ini harus dijalankan dengan tingkat kewaspadaan tertinggi di produksi, namun sangat berguna untuk menghapus environment sandbox sementara untuk menghemat biaya operasional.

3. State File: Sumber Kebenaran Tunggal yang Terisolasi #

Terraform menyimpan peta kondisi infrastruktur nyata ke dalam sebuah file khusus yang disebut State File (terraform.tfstate). State file bertindak sebagai memori internal Terraform untuk mendeteksi jika ada perubahan manual (configuration drift) di konsol GCP.

  • Penyimpanan State di GCS (Remote Backend): Pada lingkungan produksi, menyimpan file state di komputer lokal developer sangat dilarang (strict anti-pattern). Kita harus menyimpan file state secara aman di Google Cloud Storage (GCS) dengan mengaktifkan fitur enkripsi, versioning objek, serta penguncian state (state locking). Fitur penguncian menjamin bahwa jika dua developer (atau pipeline CI/CD) menjalankan proses deployment secara bersamaan, Terraform menggunakan mekanisme metadata lock GCS untuk memblokir eksekusi kedua sampai eksekusi pertama selesai guna mencegah kerusakan file state.

Diagram Aliran Provisioning Terraform (IaC Architecture) #

Berikut adalah visualisasi arsitektur bagaimana kode HCL dievaluasi oleh Terraform CLI, dicocokkan dengan remote state file di GCS bucket, dan dideploy ke API GCP untuk membuat berbagai resource serverless kita secara otomatis.

flowchart TD
    Local["Developer / CI/CD (Terraform CLI)"] -->|"Apply HCL Configuration"| TFEngine["Terraform Engine"]
    TFEngine -->|"Read State File"| GCSState["Google Cloud Storage Bucket (Remote State)"]
    TFEngine -->|"Provision Resources via GCP API"| GCP["Google Cloud Platform (GCP)"]
    
    subgraph GCP Resources
        CRService["google_cloud_run_service"]
        GCFFunction["google_cloudfunctions_function"]
        PSTopic["google_pubsub_topic"]
        ETrigger["google_eventarc_trigger"]
        IAM["google_project_iam_member"]
    end
    
    GCP --> CRService
    GCP --> GCFFunction
    GCP --> PSTopic
    GCP --> ETrigger
    GCP --> IAM

    style TFEngine stroke:#0288d1,stroke-width:2px

Deklarasi Resource Serverless Utama #

Menulis konfigurasi IaC untuk serverless menuntut keakuratan parameter HCL. Berikut adalah rincian fungsionalitas parameter utama yang digunakan:

  • google_cloud_run_service: Digunakan untuk memprovisikan container serverless. Parameter penting di dalamnya mencakup limits (alokasi memori dan CPU), autoscaling (menetapkan minScale untuk mengurangi cold start dan maxScale untuk membatasi biaya), serta container_concurrency untuk menyetel batas konkurensi request per instans.
  • google_pubsub_topic dan google_pubsub_subscription: Digunakan untuk membangun antrean pesan. Konfigurasi ack_deadline_seconds mengatur batas waktu toleransi pemrosesan pesan sebelum dikirim ulang, sementara parameter push_config digunakan jika ingin menyalurkan event langsung ke endpoint HTTP Cloud Run dengan otentikasi OIDC terenkripsi.
  • google_workflows_workflow: Mengompilasi dan mengunggah kode orkestrasi YAML menggunakan argumen source_contents ke dalam engine serverless.
  • google_eventarc_trigger: Mengatur kriteria pencocokan event source (matching_criteria) dan mendefinisikan kontainer target tujuan di dalam parameter destination.

Manajemen Hak Akses (IAM) Berbasis Least Privilege #

Keamanan adalah aspek paling krusial sekaligus paling menantang dalam arsitektur serverless. Karena setiap layanan serverless berjalan secara terisolasi di jaringan awan, mereka membutuhkan identitas khusus untuk dapat mengakses resource lain (misalnya layanan Cloud Run memanggil database Cloud SQL atau membaca file di Cloud Storage).

  • JANGAN menggunakan service account default Google Compute Engine atau App Engine untuk menjalankan layanan serverless Anda di produksi, karena akun tersebut memiliki hak akses sebagai editor project yang terlalu luas.
  • BENAR: Buat Service Account kustom khusus untuk setiap layanan serverless menggunakan Terraform. Berikan hak akses dengan prinsip Least Privilege (hak akses minimal) menggunakan binding IAM yang presisi.

Perbedaan Krusial Binding IAM: Member vs. Binding vs. Policy #

Terraform menawarkan tiga resource untuk mengelola izin IAM:

  1. google_project_iam_policy (Sangat Berbahaya): Menimpa seluruh kebijakan IAM di tingkat project dengan daftar yang didefinisikan. Semua izin lain yang dibuat manual akan langsung dihapus.
  2. google_project_iam_binding (Berbahaya): Mengambil kendali penuh atas satu role spesifik. Jika kita mendefinisikan binding untuk role roles/storage.admin, seluruh user atau service account lain yang memiliki role tersebut namun tidak ditulis di file Terraform akan langsung dicabut aksesnya.
  3. google_project_iam_member (Sangat Direkomendasikan): Menambahkan satu anggota (member) ke satu role secara aditif tanpa mengganggu hak akses anggota lain yang sudah ada. Ini adalah pilihan paling aman untuk menghindari ketidaksengajaan menghapus hak akses tim admin lain.

Struktur Folder Project Terraform Multi-Environment #

Untuk menjaga kebersihan kode dan menghindari risiko ketidaksengajaan menghancurkan infrastruktur produksi saat melakukan eksperimen di lingkungan development, kita harus menyusun direktori Terraform secara modular dan terpisah berdasarkan environment.

Mengapa Memilih Struktur Folder Dibandingkan Terraform Workspaces? #

Terraform menyediakan fitur bawaan bernama Workspaces untuk mengelola multi-environment dalam satu direktori kode. Namun, untuk tingkat produksi enterprise, menggunakan isolasi berbasis Struktur Folder jauh lebih aman karena:

  • Pemisahan Backend State: Setiap folder environment memiliki konfigurasi file backend state yang terisolasi sepenuhnya di bucket GCS yang berbeda. Kerusakan state di environment dev tidak akan pernah merambat ke environment prod.
  • Kredensial Terpisah: Kita dapat membatasi hak akses service account CI/CD agar service account dev tidak memiliki akses tulis sama sekali ke folder prod, menegakkan isolasi keamanan mutlak.
  • Variabel Dinamis yang Lebih Terbaca: Menyimpan nilai variabel spesifik lingkungan di dalam file terraform.tfvars di masing-masing folder jauh lebih mudah diaudit dibanding melacak workspace state yang tersimpan di memori Terraform.

Contoh Implementasi Kode: Konfigurasi HCL Lengkap #

Berikut adalah contoh lengkap kode Terraform (main.tf di tingkat environment) yang mengonfigurasi remote backend di GCS, memprovisikan layanan Cloud Run, membuat Pub/Sub topic, mengonfigurasi pemicu Eventarc, serta menetapkan hak akses IAM secara aman.

# ==========================================================================
# 1. PROVIDER & REMOTE BACKEND CONFIGURATION
# ==========================================================================
terraform {
  required_version = ">= 1.6.0"
  
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.10.0"
    }
  }

  # ✓ BENAR: Menyimpan state file di Google Cloud Storage dengan fitur lock
  backend "gcs" {
    bucket = "my-company-terraform-states"
    prefix = "serverless-app/production"
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

# ==========================================================================
# 2. SERVICE ACCOUNT & IAM PRIVILEGES (Least Privilege)
# ==========================================================================

# Membuat Service Account khusus untuk Cloud Run Backend
resource "google_service_account" "run_backend_sa" {
  account_id   = "cr-backend-production-sa"
  display_name = "Service Account untuk Cloud Run Backend Production"
}

# Memberikan akses agar Cloud Run dapat membaca Secret Manager
resource "google_project_iam_member" "secret_accessor" {
  project = var.project_id
  role    = "roles/secretmanager.secretAccessor"
  member  = "serviceAccount:${google_service_account.run_backend_sa.email}"
}

# ==========================================================================
# 3. PUB/SUB TOPIC & SUBSCRIPTION PROVISIONING
# ==========================================================================
resource "google_pubsub_topic" "order_topic" {
  name = "production-order-events-topic"
  
  labels = {
    environment = "production"
    owner       = "checkout-team"
  }
}

# ==========================================================================
# 4. CLOUD RUN SERVICE CONFIGURATION (Production Ready)
# ==========================================================================
resource "google_cloud_run_service" "backend_app" {
  name     = "backend-api-production"
  location = var.region

  # Mengaktifkan revision splitting secara otomatis ke revision terbaru
  traffic {
    percent         = 100
    latest_revision = true
  }

  template {
    spec {
      service_account_name = google_service_account.run_backend_sa.email
      
      containers {
        image = var.container_image_url
        
        resources {
          limits = {
            cpu    = "2000m" # 2 vCPU
            memory = "2Gi"   # 2 GB RAM
          }
        }

        # Menyuntikkan environment variable
        env {
          name  = "APP_ENV"
          value = "production"
        }

        env {
          name  = "DATABASE_NAME"
          value = "production_db"
        }
      }
    }

    metadata {
      annotations = {
        # ✓ BENAR: Mengonfigurasi batas autoscaling & concurrency secara presisi
        "autoscaling.knative.dev/minScale" = "1" 
        "autoscaling.knative.dev/maxScale" = "30"
        "container.googleapis.com/concurrency" = "80"
      }
    }
  }
}

# ==========================================================================
# 5. EVENTARC TRIGGER CONFIGURATION
# ==========================================================================

# Service Account untuk mengizinkan Eventarc melakukan routing event
resource "google_service_account" "eventarc_trigger_sa" {
  account_id   = "eventarc-order-trigger-sa"
  display_name = "Service Account Eventarc Order Trigger"
}

# Role untuk mengizinkan Service Account Eventarc memanggil API Cloud Run
resource "google_cloud_run_service_iam_member" "eventarc_invoker" {
  service  = google_cloud_run_service.backend_app.name
  location = google_cloud_run_service.backend_app.location
  role     = "roles/run.invoker"
  member   = "serviceAccount:${google_service_account.eventarc_trigger_sa.email}"
}

# Trigger Eventarc untuk mendekatkan pesan dari Pub/Sub order topic
resource "google_eventarc_trigger" "pubsub_trigger" {
  name     = "pubsub-order-event-trigger"
  location = var.region

  destination {
    cloud_run_service {
      service = google_cloud_run_service.backend_app.name
      region  = google_cloud_run_service.backend_app.location
      path    = "/webhooks/order" # Endpoint HTTP target backend
    }
  }

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.pubsub.topic.v1.messagePublished"
  }

  # Filter berdasarkan topic asal
  transport {
    pubsub {
      topic = google_pubsub_topic.order_topic.id
    }
  }

  service_account = google_service_account.eventarc_trigger_sa.email

  depends_on = [
    google_cloud_run_service_iam_member.eventarc_invoker
  ]
}

Integrasi CI/CD Pipeline (Terraform + GitOps) #

Mendeploy infrastruktur secara manual menggunakan terminal dari laptop lokal developer berisiko memicu kerusakan state file akibat inkonsistensi versi Terraform CLI atau kredensial yang tidak aman.

  • BENAR: Integrasikan siklus provisioning Terraform ke dalam pipeline CI/CD (seperti GitHub Actions) berbasis GitOps secara otomatis.

Berikut adalah contoh skrip workflow GitHub Actions (.github/workflows/terraform.yml) untuk memvalidasi dan memprovisikan infrastruktur kita:

name: "Terraform GitOps Pipeline"

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

permissions:
  contents: read
  pull-requests: write

jobs:
  terraform:
    name: "Terraform Job"
    runs-on: "ubuntu-latest"
    
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.6.0"

      # Otentikasi ke Google Cloud menggunakan Workload Identity Federation
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Terraform Format Check
        run: terraform fmt -check -recursive

      - name: Terraform Init
        run: terraform init
        working-directory: ./environments/prod

      - name: Terraform Validate
        run: terraform validate
        working-directory: ./environments/prod

      - name: Terraform Plan
        id: plan
        if: github.event_name == 'pull_request'
        run: terraform plan -no-color
        working-directory: ./environments/prod

      # Menerapkan perubahan hanya ketika Pull Request di-merge ke branch main
      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve
        working-directory: ./environments/prod

Best Practice Mengelola Serverless IaC #

Penerapan prinsip-prinsip arsitektur berikut sangat dianjurkan untuk menjamin kestabilan dan keamanan kode Terraform serverless kita dalam jangka panjang:

1. Ambil Kredensial Sensitif dari Secret Manager secara Dinamis #

Menuliskan password database secara langsung (hardcoded) di file variables.tf atau file .tfvars adalah celah keamanan tingkat tinggi yang sering kali bocor ke repositori Git publik.

  • BENAR: Daftarkan data rahasia tersebut secara manual di konsol Secret Manager GCP. Di Terraform, panggil data rahasia tersebut menggunakan blok data query:
data "google_secret_manager_secret_version" "db_password" {
  secret = "production-database-password"
}

# Gunakan data kueri ini di konfigurasi environment variable Cloud Run
# ${data.google_secret_manager_secret_version.db_password.secret_data}

2. Tangani Versi Image Kontainer dengan Siklus Hidup yang Tepat #

Setiap kali pipeline CI/CD aplikasi kita mem-build container image baru (misal: gcr.io/my-project/api:v1.2.0), mendeploynya menggunakan Terraform dapat menyebabkan file state Terraform tertinggal jika container dideploy langsung oleh pipeline CD terpisah.

  • BENAR: Jika kita mendeploy container image baru secara mandiri menggunakan Cloud Build, konfigurasikan blok lifecycle di resource google_cloud_run_service Terraform untuk mengabaikan perubahan properti image kontainer agar Terraform apply berikutnya tidak mengembalikan versi image ke versi lama secara tidak sengaja:
lifecycle {
  ignore_changes = [
    template[0].spec[0].containers[0].image,
  ]
}

3. Terapkan Labelisasi Resource untuk Manajemen Biaya #

Membengkaknya biaya penggunaan cloud sering kali sulit dilacak asalnya jika kita mendeploy puluhan resource serverless secara acak.

  • BENAR: Selalu tambahkan konfigurasi blok labels (atau metadata.labels di Cloud Run) pada setiap resource yang kita buat di Terraform. Gunakan label standar industri seperti environment = "production", cost-center = "marketing-team", dan project = "checkout-system". Label ini akan secara otomatis terintegrasi ke laporan Billing GCP, memudahkan tim finansial mengaudit biaya penggunaan per tim secara detail.

4. Penanganan Lock State Macet (Force Unlock) #

Terkadang, jika pipeline CI/CD kita mati mendadak di tengah proses terraform apply akibat kegagalan server runner, GCS backend akan mendeteksi state tetap dalam kondisi terkunci (locked), menghalangi proses deployment berikutnya secara permanen.

  • BENAR: Jangan panik dan jangan mencoba menghapus file state. Salin kode Lock ID yang tercetak di terminal error, verifikasi bahwa tidak ada proses deployment lain yang sedang aktif di backend, kemudian jalankan perintah terraform force-unlock <LOCK_ID> di terminal lokal Anda untuk membuka kunci GCS state secara aman.

5. Deteksi Perubahan Manual Secara Berkala (Scheduled Drift Detection) #

Meskipun kita menggunakan GitOps, kadang-kadang anggota tim terpaksa melakukan modifikasi darurat secara manual di konsol Google Cloud saat menangani masalah produksi. Perubahan manual ini (drift) harus segera didefinisikan kembali ke kode agar tidak terhapus di deployment berikutnya.

  • BENAR: Siapkan cron job harian di pipeline GitHub Actions kita yang menjalankan perintah terraform plan -detailed-exitcode pada jam-jam sepi. Opsi -detailed-exitcode akan mengembalikan status code 2 jika dideteksi adanya drift. Pipeline kemudian dapat mengirimkan notifikasi otomatis ke Slack tim developer untuk segera melakukan rekonsiliasi kode.

6. Batasan Versi Kompatibilitas (Version Constraints) #

Upgrade otomatis versi provider Google oleh Terraform init dapat mengenalkan breaking changes tak terduga yang merusak skrip build HCL kita.

  • BENAR: Selalu kunci rentang versi provider Google menggunakan operator ~> di blok required_providers (misal: ~> 5.10.0). Kunci juga versi executable Terraform CLI menggunakan argumen required_version untuk menjamin seluruh tim developer menggunakan engine versi yang sama.

Ringkasan #

  • Terraform adalah tool IaC deklaratif yang bertindak sebagai single source of truth untuk seluruh infrastruktur serverless di GCP.
  • Wajib gunakan remote backend GCS dengan versioning dan object locking aktif guna mengamankan state file dari kerusakan sinkronisasi.
  • Rancang service account kustom khusus untuk setiap layanan serverless guna menegakkan prinsip keamanan least privilege.
  • Pisahkan direktori Terraform menjadi modules (reusable blueprints) dan environments (dev/prod instance) yang terisolasi.
  • Gunakan siklus CI/CD GitOps untuk memvalidasi, merencanakan (plan), dan mengeksekusi (apply) perubahan infrastruktur dari branch utama Git.
  • Terapkan ignore_changes pada image kontainer jika deployment image baru dikelola oleh pipeline CD eksternal di luar Terraform.

← Sebelumnya: Eventarc   Berikutnya: NeonDB →

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