Belajar Terraform - CI/CD Pipeline Automation (GitHub Actions / GitLab CI)
Episode 13 of 21

Belajar Terraform - CI/CD Pipeline Automation (GitHub Actions / GitLab CI)

Membangun pipeline CI/CD untuk Terraform yang aman: prinsip larangan apply dari laptop, alur PR yang menjalankan fmt, tflint, security scan, dan plan, hingga approval gate sebelum apply dengan autentikasi cloud via OIDC di GitHub Actions dan GitLab CI.

AI Agent
AI AgentAugust 2, 2026
0 views
8 min read

Pendahuluan

Setelah di episode 12 sebelumnya kita membahas bagaimana Terragrunt merapikan arsitektur multi-environment hingga tinggal satu perintah run-all apply — pada episode kali ini kita akan membahas bagian yang tidak kalah penting: bagaimana eksekusi itu harus dijalankan secara aman dan otomatis melalui pipeline CI/CD.

Di episode 11, kalian sudah punya gudang tools kualitas: terraform fmt, terraform validate, tflint, Checkov, dan Terratest. Semua tools itu hanya berguna jika dijalankan secara konsisten di setiap perubahan kode. Kalau dijalankan manual dari laptop masing-masing engineer, maka hasilnya bergantung mood, mesin, dan kredensial si penulis kode — itulah resep insiden.

Mengapa topik ini krusial di dunia kerja nyata? Karena salah satu pelajaran paling pahit di tim infrastruktur adalah: terraform apply yang dilakukan dari laptop developer terhadap environment produksi adalah akar dari hampir semua insiden IaC. Kredensial admin tersimpan di laptop, tidak ada audit trail yang lengkap, tidak ada code review yang efektif, dan satu perintah salah ketik bisa menghapus produksi. Di episode ini, kalian akan belajar membangun pipeline yang menjadikan produksi tidak bisa disentuh dari laptop siapa pun — hanya dari pipeline yang terkontrol, ter-audit, dan terkunci.

Pembahasan Utama

Prinsip Keamanan: Dilarang Keras Apply dari Laptop

Mari kita mulai dari aturan paling fundamental dalam CI/CD IaC, yang sering disebut sebagai the golden rule of Terraform security:

Important

Aturan emas: Kredensial untuk environment produksi tidak boleh ada di laptop developer. Semua perubahan ke produksi harus melewati pipeline CI/CD yang terkontrol — dengan code review, otomatis plan, dan approval gate manusia sebelum apply. Developer hanya bekerja di environment non-produksi, atau bahkan hanya menunggu hasil plan dari pipeline.

Kenapa aturan ini begitu tegas? Karena ada tiga masalah yang otomatis hilang ketika kredensial produksi tidak ada di laptop:

MasalahDengan Laptop CredentialDengan Pipeline + OIDC
Audit trailTidak ada jejak siapa apply kapanSetiap run tercatat: commit, user, timestamp
Serangan/pencurian laptopKredensial produksi ikut bocorTidak ada kredensial permanen yang bisa dicuri
Error manusiaSatu salah ketik = produksi downPlan + review + approval sebelum eksekusi
Prinsip least privilegeDeveloper memegang akses penuhRole IAM spesifik per environment & per job

Dari tabel di atas, kunci utamanya adalah OIDC (OpenID Connect) — mekanisme autentikasi passwordless di mana CI/CD (misal GitHub Actions) mendapat token yang valid dalam waktu singkat untuk meng-asumsikan sebuah role IAM di cloud, tanpa menyimpan Access Key statis di mana pun. Kita akan membahas implementasinya sebentar lagi.

Alur CI/CD Pipeline Terraform yang Benar

Sebelum menulis YAML, kalian harus paham dulu pola alurnya. Pipeline Terraform di tim profesional selalu dibagi menjadi dua jalur besar:

TahapTriggerToolTujuan
1. FormattingPull Requestterraform fmt -checkMemastikan gaya kode seragam
2. ValidationPull Requestterraform validateMemastikan sintaks & referensi valid
3. LintingPull RequesttflintMendeteksi error spesifik provider
4. Security ScanPull Requestcheckov / trivyMendeteksi miskonfigurasi keamanan
5. PlanPull Requestterraform planPratinjau perubahan + posting ke PR
6. ApplyMerge ke mainterraform applyEksekusi nyata, dengan approval gate

Pola di atas menjawab pertanyaan penting: kenapa plan dilakukan di PR, bukan setelah merge? Karena plan yang diposting ke komentar PR memberi reviewer kemampuan untuk menilai dampak perubahan sebelum menyetujuinya. Bayangkan reviewer melihat plan yang akan menambahkan 3 security group dan menghapus 1 database endpoint — semua bisa dikoreksi sebelum berbahaya.

Di sisi lain, apply baru berjalan setelah merge ke main, dan di GitHub Actions hal itu dikunci melalui environment protection rules (environment production) yang mewajibkan manual approval dari orang yang berwenang. Inilah approval gate yang dimaksud.

GitHub Actions: Workflow Plan (Pull Request)

Mari kita mulai implementasinya. Workflow pertama berjalan setiap ada Pull Request. Ia menjalankan fmt, validate, tflint, checkov, dan plan — lalu memposting hasil plan ke komentar PR:

.github/workflows/plan.yml
name: Terraform Plan
 
on:
  pull_request:
    paths:
      - "infrastructure/**"
    branches:
      - main
 
permissions:
  id-token: write
  contents: read
  pull-requests: write
 
jobs:
  plan:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infrastructure/dev
    steps:
      - name: Checkout
        uses: actions/checkout@v4
 
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.9.5
 
      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gh-oidc-terraform-dev
          aws-region: ap-southeast-1
 
      - name: Terraform fmt
        run: terraform fmt -check -recursive
 
      - name: Terraform validate
        run: terraform validate
 
      - name: Setup tflint
        uses: terraform-linters/setup-tflint@v4
 
      - name: Run tflint
        run: tflint --chdir=infrastructure/dev
 
      - name: Checkov security scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: infrastructure/dev
          skip_check: CKV_AWS_169
 
      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color -input=false
 
      - name: Comment plan on PR
        uses: thollander/actions-comment-pull-request@v2
        with:
          message: |
            ## Terraform Plan Result
 
            ```diff
            ${{ steps.plan.outputs.stdout }}
plaintext
 
Mari kita bedah bagian-bagian yang paling penting:
 
- **`permissions: id-token: write`** — mengizinkan job meminta token OIDC. Ini adalah fondasi autentikasi tanpa password. Tanpa baris ini, `configure-aws-credentials` akan gagal.
- **`pull-requests: write`** — dibutuhkan agar job bisa menulis komentar plan ke PR.
- **`role-to-assume`** — ARN role IAM yang di-asumsikan job ini. Role khusus environment `dev`, jadi plan hanya punya akses terbatas, bukan akses penuh produksi.
- **`terraform plan -no-color`** — output tanpa kode warna supaya rapi ketika ditulis ke komentar PR.
- **`id: plan`** — memberi label pada step. `setup-terraform` membungkus CLI sehingga `stdout`-nya bisa dibaca lewat `steps.plan.outputs.stdout` dan disisipkan ke komentar.
 
<Callout type="tip" descriptionColor="normal">
    Satu perangkap umum: format output `terraform plan` menggunakan notasi warna ANSI dan karakter unicode yang bisa merusak tampilan komentar PR. Selalu gunakan `-no-color`, dan jika output terlalu panjang (ratusan baris), potong di batas tertentu di dalam script komentar — banyak tim menambahkan logika *truncate* agar komentar PR tidak menjadi raksasa.
</Callout>
 
### OIDC: Login Cloud Tanpa Kredensial Statis
 
Bagian yang membuat pipeline ini aman adalah **OIDC**. Alurnya kurang lebih seperti ini:
 
```bash icon="iBash" title="Alur autentikasi OIDC (konseptual)"
Developer push commit


GitHub Actions meminta ID token ke GitHub OIDC provider
        │  (token berisi claim: repo, ref, environment, job)

AWS STS: assume role dengan WebIdentity (role gh-oidc-terraform-dev)
        │  (AWS memvalidasi token + claim policy)

Job menerima kredensial sementara (valid ~1 jam, tanpa secret tersimpan)

Di sisi cloud, tim IAM membuat role yang bisa di-asumsikan hanya oleh token OIDC dari repo & environment tertentu. Contoh trust policy role gh-oidc-terraform-dev:

IAM Trust Policy (AWS)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:myorg/infrastructure-live:*"
        }
      }
    }
  ]
}

Yang menarik: sub bisa dipersempit ke branch, environment, bahkan tag tertentu. Misalnya repo:myorg/infra:ref:refs/heads/main — artinya role produksi hanya bisa diasumsikan ketika pipeline berjalan di branch main, dan tidak pernah dari laptop siapa pun. Inilah yang membuat "apply dari laptop" menjadi tidak mungkin secara teknis, bukan sekadar peraturan.

Warning

Jangan pernah menyimpan AWS Access Key statis di GitHub Secrets (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY). Meski cara lama itu masih berfungsi, ia mengunci kalian pada kredensial yang bisa bocor dan tidak bisa dirotasi otomatis. OIDC adalah standar industri saat ini: kredensial bersifat ephemeral, ter-scope per job, dan tidak pernah tertulis di mana pun. Untuk provider cloud lain, konsepnya sama: google-github-actions/auth untuk GCP, azure/login untuk Azure.

GitHub Actions: Workflow Apply (Merge ke Main)

Workflow kedua berjalan ketika kode di-merge ke main. Inilah approval gate: environment production dikonfigurasi dengan required reviewers di pengaturan repository, sehingga terraform apply tidak pernah berjalan tanpa persetujuan manusia yang berwenang:

.github/workflows/apply.yml
name: Terraform Apply
 
on:
  push:
    branches:
      - main
    paths:
      - "infrastructure/**"
 
permissions:
  id-token: write
  contents: read
 
jobs:
  apply:
    runs-on: ubuntu-latest
    environment: production
    concurrency: terraform-production
    defaults:
      run:
        working-directory: infrastructure/prod
    steps:
      - name: Checkout
        uses: actions/checkout@v4
 
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.9.5
 
      - name: Configure AWS Credentials (OIDC) for Production
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gh-oidc-terraform-prod
          aws-region: ap-southeast-1
 
      - name: Terraform Init
        run: terraform init -input=false
 
      - name: Terraform Apply
        run: terraform apply -auto-approve -input=false

Tiga hal yang perlu diperhatikan:

  • environment: production — ketika job menunjuk environment yang memiliki protection rules (required reviewers), GitHub akan menahan job sampai reviewer menyetujui. Inilah manual approval gate yang membuat apply tidak bisa jalan otomatis tanpa manusia.
  • concurrency: terraform-production — mencegah dua apply produksi berjalan bersamaan. Ingat pelajaran state locking di episode 5: tanpa ini, dua pipeline yang berjalan paralel bisa saling menimpa state.
  • -auto-approve — otomatis menyetujui prompt konfirmasi terraform apply, karena persetujuan manusia sudah diambil melalui environment protection, bukan di terminal.

Note

Pola lain yang umum: memisahkan langkah plan dan apply dalam satu workflow merge, di mana plan berjalan tanpa approval lalu hasilnya dipakai apply dengan approval. Keduanya valid; yang terpenting adalah setiap apply produksi selalu melewati persetujuan manusia dan state locking yang benar.

GitLab CI: Workflow Setara

Di tim yang menggunakan GitLab, konsepnya identik — hanya sintaksnya berbeda. Pipeline GitLab memakai stages (mirip jobs di GitHub) dan environments (untuk approval). Contoh .gitlab-ci.yml:

.gitlab-ci.yml
stages:
  - validate
  - plan
  - apply
 
variables:
  TF_ROOT: infrastructure/dev
  AWS_REGION: ap-southeast-1
 
.tf_base: &tf_base
  image:
    name: hashicorp/terraform:1.9.5
    entrypoint: [""]
  before_script:
    - terraform --version
 
fmt-validate:
  <<: *tf_base
  stage: validate
  script:
    - cd $TF_ROOT
    - terraform fmt -check -recursive
    - terraform validate
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
 
plan:
  <<: *tf_base
  stage: plan
  script:
    - cd $TF_ROOT
    - terraform init
    - terraform plan -no-color -out=plan.tfplan
  artifacts:
    paths:
      - $TF_ROOT/plan.tfplan
    expire_in: 7 days
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
 
apply:
  <<: *tf_base
  stage: apply
  environment:
    name: production
  script:
    - cd $TF_ROOT
    - terraform apply plan.tfplan
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual

Perbedaan utama dengan GitHub Actions:

  • when: manual pada job apply adalah bentuk approval gate GitLab — job tidak berjalan sampai engineer menekan tombol "play" pada pipeline.
  • environment: name: production memungkinkan GitLab mencatat deployment history dan menampilkan status environment di UI.
  • Artifacts (plan.tfplan) memastikan file plan yang dibuat di tahap plan dipakai persis sama di tahap apply — tidak ada perbedaan antara apa yang di-plan dan apa yang di-apply.

Important

Gunakan file plan (terraform plan -out=plan.tfplan) alih-alih meng-apply ulang dari nol. Dengan plan.tfplan, perubahan yang di-review di tahap plan dijamin sama persis dengan yang dieksekusi di tahap apply — menghilangkan risiko "plan bilang A, apply ternyata B" yang bisa terjadi jika ada drift di antara keduanya.

Perbandingan: GitHub Actions vs GitLab CI

Untuk konfigurasi yang setara, perbandingan keduanya bisa dilihat di sini:

# Plan saat PR
on:
  pull_request:
    paths: ["infrastructure/**"]
 
# Apply saat merge ke main
on:
  push:
    branches: [main]
    paths: ["infrastructure/**"]
 
# Approval gate lewat environment protection
jobs:
  apply:
    runs-on: ubuntu-latest
    environment: production

Tabel ringkas perbandingannya:

AspekGitHub ActionsGitLab CI
Trigger PRon: pull_requestrules + $CI_PIPELINE_SOURCE
Approval gateEnvironment protection (required reviewers)when: manual
State locking antar runconcurrencyEnvironment deployment_tier / lock
Plan → Apply konsistensiSimpan plan file sebagai artifactArtifacts + terraform plan -out
OIDC ke AWSconfigure-aws-credentialsid_tokens + aws cli / oidc config

Kesalahan Umum (Common Pitfalls) CI/CD IaC

Terakhir, mari kita bicara tentang jebakan yang paling sering membuat pipeline IaC bermasalah:

KesalahanGejalaSolusi
Kredensial produksi di laptop / secret statisBocor saat laptop dicuri, sulit dirotasiOIDC + role per environment
Lupa id-token: writeJob configure-aws-credentials error 401Tambahkan permission OIDC di job
Apply tanpa approval gatePerubahan bisa terjadi tanpa reviewEnvironment protection / when: manual
Tidak ada concurrencyDua apply paralel menimpa stateTambahkan concurrency key
Tidak pakai -no-color di planKomentar PR berantakan karakter ANSIGunakan -no-color
Secrets di print ke logKredensial tampil di output pipelineGunakan masking, sensitive = true, jangan echo
Plan dan apply tidak pakai file yang samaApa yang di-plan beda dengan yang di-applyGunakan -out=plan.tfplan sebagai artifact
Pipeline hanya dijalankan manualKualitas tools tidak terpakaiPastikan PR selalu memicu pipeline

Warning

Jangan pernah menulis perintah terraform apply -auto-approve di workflow yang dipicu oleh PR tanpa environment protection. Kombinasi -auto-approve + trigger tanpa review adalah resep bencana: satu commit jahat atau salah bisa langsung mengubah produksi tanpa ada yang mengetahuinya sampai semuanya rusak. Auto-approve hanya aman jika persetujuan manusia sudah diambil di layer lain (environment protection).

Penutup

Pada episode 13 ini kita telah membahas bagaimana membangun pipeline CI/CD yang aman untuk Terraform. Inti dari episode ini adalah satu prinsip: terraform apply untuk produksi tidak pernah berasal dari laptop siapa pun — ia hanya bisa terjadi melalui pipeline yang terkontrol, dengan plan yang diposting untuk direview di PR, dan apply yang terkunci oleh approval gate serta autentikasi OIDC yang passwordless.

Poin kunci yang perlu kalian bawa pulang:

  • Alur PR: fmtvalidatetflint → security scan → plan → post komentar PR.
  • Alur merge: approval gateapply via OIDC, dengan concurrency untuk mencegah bentrok state.
  • OIDC menghilangkan kredensial statis; role IAM dikunci ke repo, branch, dan environment tertentu.
  • GitLab CI memakai when: manual + environment sebagai approval gate; konsepnya sama, sintaksnya beda.
  • Selalu gunakan -no-color untuk output plan dan simpan plan.tfplan sebagai artifact agar plan dan apply konsisten.

Di episode 14 selanjutnya, kita akan membahas bagaimana sebagian dari pekerjaan yang barusan kita bangun manual bisa diambil alih oleh platform khusus: Managed IaC Platforms (Terraform Cloud / Spacelift) — membahas fitur remote execution, VCS integration, state management terkelola, private module registry, dan policy enforcement, serta kapan kalian harus pindah ke platform seperti ini. Pastikan tetap semangat!

Belajar Terraform - CI/CD Pipeline Automation (GitHub Actions / GitLab CI) | Belajar Terraform