Drift Detection Automation #

Infrastruktur yang dikelola Terraform bisa berubah tanpa melalui Terraform — seseorang mungkin memodifikasi security group langsung di console untuk debug, atau cloud provider otomatis mengubah beberapa atribut resource. Drift adalah kondisi ketika realita infrastruktur tidak lagi sesuai dengan state Terraform. Jika dibiarkan, drift menumpuk dan lama-lama membuat Terraform menjadi tidak bisa dipercaya sebagai sumber kebenaran. Deteksi drift yang dijalankan secara terjadwal memberikan visibilitas atas kondisi ini sebelum menjadi masalah besar.

Apa itu Drift dan Mengapa Berbahaya #

CONTOH DRIFT NYATA:

  Terraform state says:
    aws_security_group.web:
      ingress: [port 443, 0.0.0.0/0]

  Realita di AWS:
    aws_security_group.web:
      ingress: [port 443, 0.0.0.0/0]
              [port 22, 0.0.0.0/0]   ← Ditambahkan manual untuk debug
              [port 8080, 10.0.0.0/8] ← Ditambahkan untuk testing

  Akibat:
  - Security hole yang tidak disadari (port 22 terbuka ke publik)
  - Terraform plan yang dijalankan TANPA drift check akan menghapus rule ini
    → Kejutan saat apply karena "perubahan yang tidak terduga"
  - Atau sebaliknya: apply tidak dilakukan karena plan terlihat bersih
    tapi realita infrastruktur berbeda

JENIS DRIFT:
  1. Konfigurasi berubah di luar Terraform (manual di console)
  2. Cloud provider mengubah atribut (maintenance, auto-upgrade)
  3. Integrasi pihak ketiga yang memodifikasi resource
  4. Tag yang diubah oleh billing tool atau security scanner

Scheduled Plan untuk Deteksi Drift #

Cara paling straightforward untuk mendeteksi drift adalah menjalankan terraform plan secara terjadwal. Jika plan menunjukkan ada perubahan padahal tidak ada yang mengubah konfigurasi, itu adalah drift.

# .github/workflows/drift-detection.yml

name: Drift Detection

on:
  schedule:
    - cron: '0 8 * * 1-5'  # Setiap hari kerja jam 8 pagi
  workflow_dispatch:          # Bisa dipicu manual jika perlu

jobs:
  detect-drift:
    name: Detect Drift — ${{ matrix.environment }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [production, staging]
      fail-fast: false  # Lanjutkan meski satu environment gagal

    steps:
      - uses: actions/checkout@v4

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.7.0"

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/TerraformReadRole
          aws-region: ap-southeast-1

      - name: Terraform Init
        run: terraform init
        working-directory: infrastructure/environments/${{ matrix.environment }}

      - name: Terraform Plan (Drift Check)
        id: plan
        run: |
          # -detailed-exitcode: exit 0 = tidak ada perubahan, exit 2 = ada perubahan
          terraform plan \
            -detailed-exitcode \
            -no-color \
            -refresh=true \
            2>&1 | tee plan_output.txt
          echo "exit_code=$?" >> $GITHUB_OUTPUT          
        working-directory: infrastructure/environments/${{ matrix.environment }}
        continue-on-error: true

      - name: Analyze Drift
        id: drift
        run: |
          EXIT_CODE="${{ steps.plan.outputs.exit_code }}"
          case $EXIT_CODE in
            0) echo "status=no_drift"   >> $GITHUB_OUTPUT
               echo "✅ No drift detected in ${{ matrix.environment }}" ;;
            1) echo "status=plan_error" >> $GITHUB_OUTPUT
               echo "❌ Terraform plan failed — check logs" ;;
            2) echo "status=drift_detected" >> $GITHUB_OUTPUT
               echo "⚠️ Drift detected in ${{ matrix.environment }}!" ;;
          esac          

      - name: Extract Drift Summary
        if: steps.drift.outputs.status == 'drift_detected'
        run: |
          # Ambil hanya bagian yang relevan dari plan output
          grep -E "^  [+~-]|will be|must be replaced|Plan:" plan_output.txt \
            | head -50 > drift_summary.txt
          cat drift_summary.txt          
        working-directory: infrastructure/environments/${{ matrix.environment }}

      - name: Notify Slack — Drift Detected
        if: steps.drift.outputs.status == 'drift_detected'
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "⚠️ Infrastructure drift detected",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*⚠️ Drift Detected: `${{ matrix.environment }}`*\n\nInfrastruktur tidak sesuai dengan state Terraform.\nLihat detail: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
                  }
                }
              ]
            }            
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_INFRA_WEBHOOK }}

Terraform Refresh: Memperbarui State #

Saat drift terdeteksi, ada dua respons yang mungkin: kembalikan infrastruktur ke konfigurasi Terraform (apply), atau perbarui state agar mencerminkan kondisi aktual (refresh).

# OPSI 1: Kembalikan infrastruktur ke konfigurasi Terraform
# Ini adalah respons default dan yang paling direkomendasikan
terraform apply

# OPSI 2: Perbarui state agar mencerminkan kondisi aktual
# Gunakan ini jika perubahan manual memang disengaja dan ingin dipertahankan
# PERHATIAN: ini tidak mengubah konfigurasi .tf — hanya state
terraform apply -refresh-only

# terraform refresh (deprecated — gunakan apply -refresh-only)
# terraform refresh

# Setelah refresh-only apply, kamu perlu update konfigurasi .tf
# agar sesuai dengan kondisi aktual, lalu commit ke repository
# Contoh: Setelah drift ditemukan pada security group
# Kondisi aktual: ada rule tambahan yang memang diinginkan

# Pilihan A: Hapus rule manual (kembalikan ke Terraform)
# → terraform apply (tanpa perubahan konfigurasi .tf)

# Pilihan B: Tambahkan rule ke konfigurasi (terima perubahan manual)
resource "aws_security_group_rule" "debug_access" {
  type              = "ingress"
  from_port         = 8080
  to_port           = 8080
  protocol          = "tcp"
  cidr_blocks       = ["10.0.0.0/8"]
  security_group_id = aws_security_group.web.id
  description       = "Internal debug access — added after drift review 2024-01-15"
}
# Commit ini ke repository → drift resolved

Mengelola Noise: Drift yang Bisa Diabaikan #

Tidak semua drift adalah masalah. Beberapa atribut memang berubah secara normal dan tidak perlu dikhawatirkan.

# Tambahkan ignore_changes untuk atribut yang memang berubah di luar Terraform
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type

  lifecycle {
    ignore_changes = [
      # AMI berubah saat ada patch otomatis
      ami,
      # Tags yang ditambahkan oleh billing tool atau security scanner
      tags["LastScannedBy"],
      tags["CostCenter"],
      # User data mungkin diubah oleh automation eksternal
      user_data_base64,
    ]
  }
}
# Filter drift report — hanya tampilkan yang bukan dari ignore_changes
# (Terraform sudah otomatis tidak melaporkan atribut dalam ignore_changes)

# Untuk custom filtering: parse JSON plan output
terraform show -json tfplan | jq '
  .resource_changes[]
  | select(.change.actions != ["no-op"])
  | {
      resource: .address,
      actions: .change.actions,
      changes: (.change.before // {} | keys)
    }
'

Ringkasan #

  • Drift terjadi ketika infrastruktur diubah di luar Terraform — console, CLI ad-hoc, atau automation eksternal. Jika tidak dideteksi, drift menumpuk dan membuat Terraform tidak bisa dipercaya.
  • Scheduled plan (-detailed-exitcode) adalah cara paling straightforward untuk deteksi drift — jalankan setiap hari, alert jika ada perubahan.
  • Exit code 2 dari terraform plan -detailed-exitcode berarti ada perubahan (drift), exit code 0 berarti bersih.
  • Dua respons saat drift ditemukan: terraform apply (kembalikan ke konfigurasi) atau terraform apply -refresh-only lalu update konfigurasi (terima perubahan manual).
  • ignore_changes di lifecycle block mencegah drift palsu — gunakan untuk atribut yang memang berubah di luar Terraform (tag dari billing tool, AMI patch otomatis).
  • Alert ke Slack atau channel komunikasi tim saat drift ditemukan — tim perlu tahu dan merespons sebelum drift menjadi masalah yang lebih besar.

← Sebelumnya: Policy as Code   Berikutnya: Terraform Cloud →

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