Belajar Terraform - Infrastructure Drift Detection & Refactoring
Episode 18 of 21

Belajar Terraform - Infrastructure Drift Detection & Refactoring

Setelah infrastruktur berdiri, ia bisa menyimpang dari kode karena perubahan manual di console cloud. Di episode ini kita mendeteksi drift dengan terraform plan -detailed-exitcode, mengotomasinya lewat scheduled pipeline, dan merestrukturisasi kode tanpa downtime.

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

Pendahuluan

Setelah di episode 17 sebelumnya kita membangun stack infrastruktur multi-tier lengkap — VPC, EKS, RDS, dan S3 yang terenkripsi — pada episode kali ini kita akan menghadapi salah satu musuh terbesar dari semua kode IaC yang sudah kalian tulis: infrastructure drift.

Ini cerita yang sangat umum di dunia nyata. Suatu Senin pagi, tim monitoring melaporkan biaya cloud naik secara misterius. Setelah diselidiki, ternyata ada seorang engineer — dengan niat baik — membuka AWS Console, masuk ke halaman EC2, lalu resize instance produksi dari t3.micro ke t3.medium karena "sepertinya aplikasinya agak lambat". Ia tidak sempat mengubah kode Terraform, tidak membuat Pull Request, dan tidak memberi tahu siapa pun. Esoknya, ketika tim lain menjalankan terraform plan, Terraform menampilkan perubahan yang tidak pernah mereka rencanakan — dan sekarang konfigurasi (kode), state, dan kondisi cloud nyata sudah saling bertentangan.

Fenomena inilah yang disebut infrastructure drift: jarak antara apa yang dideklarasikan di kode, apa yang tercatat di state, dan apa yang benar-benar ada di cloud. Ia terjadi bukan karena kode kalian salah, melainkan karena dunia nyata terus berubah — dan manusia, dengan segala ClickOps-nya, adalah sumber perubahan yang paling tidak terduga.

Mengapa ini penting? Karena inti janji IaC adalah reproducibility: infrastruktur harus bisa dibangun ulang persis dari kode. Selama ada drift, janji itu hancur — dan terraform apply yang tadinya aman bisa tiba-tiba menghapus perubahan yang disengaja, atau malah membiarkan ketidakkonsistenan menumpuk sampai tidak ada yang tahu state sebenarnya. Di episode ini kalian akan belajar mendeteksi drift secara otomatis, memutuskan kapan harus merekonsiliasi atau mengadopsi perubahan, serta merestrukturisasi kode tanpa downtime. Ini adalah keahlian yang membedakan pengguna Terraform dari engineer yang mengoperasikan Terraform.

Pembahasan Utama

Memahami Infrastructure Drift

Dalam sistem Terraform yang sehat, ada tiga sumber kebenaran yang harus selaras:

Sumber KebenaranIsiDimana
Konfigurasi (kode)State yang diinginkan — deklarasi HCLRepositori Git
State fileCatatan Terraform tentang apa yang ia kelolaBackend S3/GCS/Azure Blob
Kondisi nyataObjek yang sebenarnya hidup di cloudAPI Cloud Provider

Drift terjadi ketika kondisi nyata menyimpang dari apa yang tercatat di state (dan akhirnya dari kode). Sebelum plan atau apply, Terraform selalu melakukan refresh — membaca ulang kondisi objek dari API cloud dan membandingkannya dengan state. Jika ada perbedaan, itu adalah drift. Jika ada perbedaan antara state dan kode, itu adalah perubahan yang direncanakan.

Bayangkan tiga orang membaca satu resep masakan. Kode adalah resep tertulisnya, state adalah catatan si juru masak tentang apa yang sudah ia masak, dan cloud adalah masakan yang benar-benar disajikan. Jika ada tamu yang diam-diam menambahkan garam ke dalam panci (ClickOps), si juru masak tidak akan tahu sampai ia mencicipi lagi — dan begitulah, terraform plan adalah "cicip" itu.

Ada beberapa sumber drift yang umum:

  • ClickOps: perubahan manual di console cloud — resize instance, mengubah security group, menambah tag.
  • Autoscaling / cloud-native resizing: resource diubah oleh layanan lain (misal ASG mengganti instance, RDS scaling otomatis).
  • Orang lain (tim lain): perubahan lewat tooling di luar Terraform — CloudFormation, CLI langsung, atau terraform dari direktori berbeda.
  • Provider drift: di beberapa cloud, provider menormalkan nilai (misal mengubah casing tag), sehingga state terus "tidak cocok" walaupun tidak ada yang berubah.

Note

Penting dibedakan: drift (kondisi cloud ≠ state) berbeda dengan perubahan yang direncanakan (state ≠ kode). Yang pertama menandakan infrastruktur "di luar kendali" dan perlu keputusan; yang kedua adalah alur kerja normal yang memang akan diterapkan oleh apply.

Skenario Drift: Seseorang Mengubah Resource di Console

Mari kita lihat drift dalam bentuk paling nyata. Ambil contoh sebuah EC2 instance yang dideklarasikan di kode:

ec2.tf
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
 
  tags = {
    Name = "web-server"
  }
}

Suatu hari seorang engineer masuk ke AWS Console, membuka instance ini, lalu mengganti instance type-nya menjadi t3.medium — katakanlah untuk menguji apakah aplikasi menjadi lebih cepat. Ia tidak mengubah kode apa pun. Sekarang terjadi drift: kode berkata t3.micro, cloud berkata t3.medium.

Ketika kalian menjalankan terraform plan, Terraform me-refresh state dari cloud lalu membandingkannya dengan konfigurasi. Hasilnya:

Output terraform plan — drift terdeteksi
aws_instance.web: Refreshing state... [id=i-0abcd1234efgh5678]
 
Terraform will perform the following actions:
 
  # aws_instance.web will be updated in-place
  ~ resource "aws_instance" "web" {
      ~ instance_type      = "t3.medium" -> "t3.micro" # forces replacement
        tags               = {
            "Name" = "web-server"
        }
      ~ vpc_security_group_ids = [
          + "sg-0abc12345def67890",
        ]
    }
 
Plan: 0 to add, 1 to change, 0 to destroy.

Perhatikan tanda ~ di depan instance_type: itu berarti Terraform menemukan perbedaan antara kondisi cloud nyata (t3.medium) dan keinginan kode (t3.micro). Tanda + pada security group berarti ada security group yang ditambahkan manual di console yang tidak ada di kode. Ini adalah drift — dan plan berhasil "menangkap basah" mereka berdua.

Important

Perhatikan juga komentar # forces replacement pada perubahan instance_type untuk sebagian jenis resource. Artinya: jika kalian menjalankan apply untuk merekonsiliasi perubahan kecil di console, Terraform justru bisa menghancurkan dan membuat ulang resource tersebut — yang di EC2 berarti instance baru dengan IP baru dan data ephemeral hilang. Inilah mengapa keputusan "reconcile vs adopt" tidak boleh diambil dengan tergesa-gesa (detail lebih lanjut di bagian remediasi).

Mendeteksi Drift Secara Manual: plan, refresh, dan plan -refresh-only

Cara paling dasar mendeteksi drift adalah menjalankan terraform plan secara berkala. Tapi ada nuansa penting: dalam alur normal, plan tidak hanya mendeteksi — ia juga menghitung apa yang harus diubah untuk menutup drift. Ada tiga varian yang perlu kalian kuasai:

PerintahFungsiCatatan
terraform planDeteksi drift + susun rencana perubahanStandar, menampilkan aksi yang akan dilakukan
terraform refreshPerbarui state sesuai kondisi nyata di cloudLegacy — tidak menampilkan rencana, berisiko menutupi drift
terraform plan -refresh-onlyTampilkan perubahan state saja, tanpa perubahan resourceAman untuk inspeksi; tidak mengubah resource di cloud

terraform refresh adalah warisan Terraform lama yang kini tidak direkomendasikan untuk penggunaan rutin. Masalahnya: refresh menulis ulang state agar cocok dengan cloud — artinya ia justru menyembunyikan drift dengan menganggap kondisi cloud sebagai kebenaran. Itu berguna dalam kasus tertentu (misal mengadopsi perubahan yang disengaja), tapi kalau dijalankan asal-asalan, kalian kehilangan kesempatan melihat perbedaan. Gantinya, gunakan terraform plan -refresh-only yang menunjukkan perubahan state secara eksplisit dan aman untuk ditinjau:

Deteksi drift: refresh-only
terraform plan -refresh-only

Outputnya akan menampilkan perubahan yang terjadi pada state (misal instance_type berubah di state), tanpa ada niat mengubah resource di cloud.

terraform plan -detailed-exitcode: Bahasa Mesin untuk Drift

Masalah dari terraform plan biasa: ia mengembalikan exit code 0 ketika sukses — baik ketika ada perubahan maupun tidak. Manusia bisa membaca output, tapi mesin (pipeline CI/CD, scheduler) tidak bisa membedakannya. Di situlah -detailed-exitcode hadir.

Plan dengan detailed-exitcode
terraform plan -detailed-exitcode
echo "Exit code: $?"

Dengan flag ini, Terraform mengembalikan salah satu dari tiga exit code:

Exit CodeArtiMakna Operasional
0Tidak ada perubahan — infrastruktur selaras dengan kodeBersih: tidak ada drift
1Terjadi error (syntax, kredensial, backend, dll.)Gagal: investigasi diperlukan
2Ada perubahan yang perlu diterapkanDrift (atau perubahan yang direncanakan): perlu keputusan

Tip

Exit code 2 tidak selalu berarti drift berbahaya — ia juga muncul ketika kalian memang sedang merencanakan perubahan kode yang baru. Kuncinya ada pada konteks: di Pull Request, exit code 2 adalah hal yang wajar; di job terjadwal yang seharusnya "tidak ada yang berubah", exit code 2 adalah alarm drift. Gunakan -detailed-exitcode bersamaan dengan -out=tfplan agar rencana bisa disimpan dan direview: terraform plan -detailed-exitcode -out=tfplan.

Automasi Drift Detection dengan Scheduled Pipeline

Deteksi drift secara manual berarti mengandalkan disiplin seseorang untuk ingat menjalankan terraform plan setiap hari — dan disiplin manusia adalah pengaman yang paling mudah bocor. Solusi yang benar adalah scheduled pipeline: sebuah job di CI/CD yang berjalan otomatis setiap malam, menjalankan terraform plan -detailed-exitcode, dan melaporkan status drift.

Berikut contoh GitHub Actions yang menjalankan deteksi drift setiap hari pukul 02.00 pagi untuk environment production:

.github/workflows/drift-detection.yml
name: Drift Detection
 
on:
  schedule:
    - cron: '0 2 * * *'          # Setiap hari 02.00 UTC
  workflow_dispatch:              # Bisa dipicu manual
 
permissions:
  id-token: write
  contents: read
  issues: write
 
jobs:
  detect-drift:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infra/environments/production
    steps:
      - uses: actions/checkout@v4
 
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.9.0"
 
      - 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
 
      - name: Terraform Plan (deteksi drift)
        id: plan
        run: |
          set +e
          terraform plan -detailed-exitcode -out=tfplan
          code=$?
          set -e
          echo "terraform_exit_code=$code" >> "$GITHUB_OUTPUT"
          if [ "$code" -eq 2 ]; then
            echo "drift_found=true" >> "$GITHUB_OUTPUT"
          else
            echo "drift_found=false" >> "$GITHUB_OUTPUT"
          fi
 
      - name: Laporkan drift ke issue
        if: steps.plan.outputs.drift_found == 'true'
        uses: actions/github-script@v7
        with:
          script: |
            const body = `Drift terdeteksi di \`production\` pada \`${new Date().toISOString()}\`.
 
            Jalankan \`terraform plan\` di direktori \`infra/environments/production\`
            untuk melihat detail perubahan, lalu putuskan: **reconcile** (apply)
            atau **adopt** (ubah kode). Jangan biarkan drift menumpuk!`;
            github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: "🚨 Drift terdeteksi: production",
              body: body
            })

Beberapa detail yang perlu kalian pahami:

  • cron: '0 2 * * *' — menjadwalkan job di dini hari, saat lalu lintas perubahan tim rendah sehingga notifikasi drift tidak tenggelam.
  • set +e / set -e — menonaktifkan exit-on-error sementara agar kita bisa menangkap exit code 2 tanpa membuat job langsung gagal. Tanpa ini, terraform plan yang mengembalikan 2 akan menghentikan workflow seketika.
  • drift_found sebagai output — status drift diekspos sebagai variabel agar step berikutnya bisa bereaksi: membuat issue, mengirim notifikasi Slack, atau menandai PR.
  • OIDC (role-to-assume) — job berjalan passwordless dengan kredensial ephemeral (episode 13), jadi tidak ada Access Key yang tersimpan di secret workflow.

Warning

Pada job drift detection, jangan pernah menjalankan terraform apply otomatis. Apply otomatis untuk menutup drift bisa menghancurkan perubahan yang disengaja — termasuk perubahan yang sengaja dibuat di console karena alasan emergency. Deteksi otomatis harus selalu berakhir dengan laporan ke manusia, dan remediasi tetap melalui alur review yang normal.

Remediasi Drift: Rekonsiliasi atau Adopsi

Saat drift terdeteksi, kalian menghadapi dua pilihan yang masing-masing punya arah berlawanan:

PilihanArah PerubahanKapan Digunakan
Rekonsiliasi (Reconcile)Ubah cloud agar sesuai kode → terraform applyPerubahan di console tidak disengaja / melanggar standar
Adopsi (Adopt)Ubah kode agar sesuai cloud → edit HCL → applyPerubahan di console memang disengaja dan layak dipertahankan

Rekonsiliasi dilakukan ketika perubahan di console adalah kesalahan atau tidak diinginkan — misalnya resize instance yang ternyata membuat biaya membengkak. Cukup jalankan terraform apply, dan Terraform mengembalikan resource ke bentuk yang dideklarasikan di kode:

Rekonsiliasi drift
terraform plan -detailed-exitcode -out=tfplan   # pastikan drift yang terdeteksi memang yang diinginkan
terraform apply tfplan

Adopsi dilakukan ketika perubahan di console ternyata keputusan yang tepat (misal security group baru yang memang dibutuhkan, atau instance type baru yang memang lebih cocok). Maka kalian menulis perubahan itu kembali ke dalam kode — sehingga kode kembali menjadi satu-satunya sumber kebenaran, dan apply tidak lagi mencoba menutup drift:

ec2.tf — adopsi perubahan ke dalam kode
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.medium"

Adopsi ini harus lewat alur normal: ubah kode → buka Pull Request → direview → di-merge → pipeline apply (episode 13). Dengan begitu, perubahan tetap tercatat di Git dan bisa diaudit — bukan menjadi rahasia yang hilang di dalam console.

Important

Aturan emas remediasi drift: jangan pernah menutup drift dengan terraform refresh atau state rm hanya agar "plan-nya bersih". Itu menipu sistem — drift tidak hilang, ia hanya dipindahkan ke dalam state yang kemudian akan menimpa kode kalian berikutnya. Pilihan yang jujur hanya dua: ubah cloud-nya (reconcile) atau ubah kodenya (adopt).

Refactoring Kode Tanpa Downtime

Selain drift dari luar, ada jenis "perubahan" lain yang sama pentingnya: refactoring kode — merapikan struktur, mengganti nama, atau memindahkan resource ke dalam modul. Naifnya, jika kita hanya mengubah nama resource di kode, Terraform akan menganggap resource lama hilang dan membuat yang baru — destroy dan recreate, sesuatu yang memicu downtime dan kehilangan data.

Untungnya, semua mekanisme yang kalian butuhkan sudah kalian pelajari di episode 7 dan 8. Mari kita rangkum tiga senjata utama refactoring tanpa downtime:

1. moved Block: Refactoring yang Tertulis di Kode

Ketika kalian mengganti nama resource atau memindahkannya ke dalam modul, blok moved memberi tahu Terraform: "objek di address lama sekarang hidup di address baru — perlakukan keduanya sebagai hal yang sama."

moved.tf
moved {
  from = aws_instance.web
  to   = aws_instance.nginx
}

Saat terraform plan, output akan menampilkan Moved: aws_instance.web → aws_instance.nginx dengan Plan: 0 to add, 0 to change, 0 to destroy — tidak ada penghancuran, hanya relokasi di dalam state. Setelah apply sukses, blok moved boleh dihapus dari kode.

2. terraform state mv: Refactoring Langsung di Terminal

Untuk restrukturisasi besar — misalnya memindahkan puluhan resource ke dalam modul sekaligus — terraform state mv bekerja langsung di terminal tanpa perlu mengubah kode dulu:

state mv ke dalam modul
terraform state mv 'module.web.aws_instance.app' 'aws_instance.legacy'

Ingat: state mv hanya memindahkan di dalam state — kode konfigurasi juga harus diperbarui ke address baru setelahnya, jika tidak plan berikutnya akan mengira resource lama hilang.

3. lifecycle.create_before_destroy: Ganti Resource Tanpa Jeda Layanan

Ada kalanya refactoring mengharuskan penggantian resource (misal mengubah AMI yang tidak kompatibel, atau mengubah atribut yang forces replacement). Untuk memastikan tidak ada jeda layanan, atur lifecycle agar resource baru dibuat sebelum yang lama dihancurkan:

Rekreasi aman
resource "aws_instance" "web" {
  ami           = "ami-0updated123456789"
  instance_type = "t3.medium"
 
  lifecycle {
    create_before_destroy = true
  }
}

Dengan create_before_destroy = true, Terraform membuat instance baru terlebih dahulu, menunggu hingga siap, baru menghancurkan yang lama — mengikuti urutan yang dijamin dependency graph. Ini adalah mekanisme inti di balik strategi blue-green dan rolling replacement di level resource.

moved {
  from = aws_instance.web
  to   = aws_instance.nginx
}

Tip

Pilihannya sederhana: moved block untuk refactoring yang merupakan bagian dari perubahan kode yang akan di-review (default project modern), terraform state mv untuk aksi state yang bersifat sekali jalan atau melibatkan pemindahan antar state file. Keduanya mencegah destroy/recreate — dan karenanya mencegah downtime.

Kesalahan Umum dalam Menangani Drift & Refactoring

KesalahanDampakSolusi
Mengabaikan drift "kecil"Perbedaan menumpuk sampai plan raksasa yang sulit direviewDeteksi otomatis harian + remediasi cepat
Menutup drift dengan refresh/state rmDrift disembunyikan, kode tidak sinkron, audit basiReconcile (apply) atau adopt (ubah kode)
Apply otomatis di drift detectionMenghancurkan perubahan manual yang disengajaDeteksi hanya melapor; remediasi via review
Hanya mengandalkan plan manualDrift dibiarkan lama karena manusia lupaScheduled pipeline + notifikasi otomatis
Mengubah nama resource tanpa movedDestroy + recreate = downtimeGunakan moved/state mv (episode 8)
Lupa memperbarui kode setelah state mvPlan menampilkan to destroy untuk address lamaPasangkan setiap state mv dengan edit kode
Recreate resource kritis tanpa create_before_destroyJeda layanan saat penggantian resourceAtur lifecycle create_before_destroy

Penutup

Pada episode 18 ini kalian telah memahami infrastructure drift — jarak antara kode, state, dan kondisi cloud nyata akibat perubahan manual di console — dan tiga cara mengelolanya: mendeteksi dengan terraform plan (dan plan -refresh-only), mengotomatiskan deteksi dengan terraform plan -detailed-exitcode di scheduled pipeline, serta memutuskan remediasi antara reconcile dan adopt. Kalian juga mengulang dan menguatkan teknik refactoring tanpa downtime menggunakan moved block, terraform state mv, dan lifecycle.create_before_destroy.

Poin kunci yang perlu kalian bawa pulang:

  • Drift adalah keniscayaan — yang membedakan tim profesional adalah deteksi yang otomatis, bukan ketiadaan drift.
  • Exit code 0/1/2 dari plan -detailed-exitcode adalah bahasa mesin yang bisa dipakai scheduler untuk memberi alarm.
  • Drift detection harus berhenti di laporan ke manusia; remediasi tetap lewat alur review.
  • Reconcile atau adopt? Ubah cloud-nya, atau ubah kodenya — jangan pernah mengaburkan keduanya.

Drift adalah pertanda bahwa "kehidupan berjalan" di infrastruktur kalian. Tapi ada satu ancaman yang jauh lebih menakutkan daripada drift: state file yang corrupt atau hilang total. Ketika itu terjadi, Terraform kehilangan ingatannya — dan seisi infrastruktur berubah menjadi pertanyaan besar: "apakah resource ini masih ada?"

Di episode 19 selanjutnya kita akan membahas Disaster Recovery (DR) & State Recovery — prosedur pemulihan saat state corrupt atau hilang, strategi backup & versioning di remote storage, serta operasi terraform state pull dan terraform state push untuk pemulihan manual yang aman. Pastikan tetap semangat!