Belajar Terraform - Advanced Architecture dengan Terragrunt
Episode 12 of 21

Belajar Terraform - Advanced Architecture dengan Terragrunt

Mengenal Terragrunt, thin wrapper buatan Gruntwork yang menghapus duplikasi backend dan provider di arsitektur multi-environment, serta fitur include, dependency, inputs, dan run-all untuk menjaga kode Terraform tetap DRY.

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

Pendahuluan

Setelah di episode 11 sebelumnya kita membahas bagaimana menjaga kualitas kode melalui terraform fmt, tflint, Checkov, hingga automated testing dengan Terratest — pada episode kali ini kita akan membahas satu lapisan arsitektur yang membuat semua praktik itu terasa lebih ringan: Terragrunt.

Di episode 10, kalian sudah diperkenalkan pada pola directory-based untuk memisahkan environment (dev/, staging/, prod/). Pola itu memang benar secara arsitektur, tapi ada satu masalah yang menyertainya: duplikasi. Setiap folder environment, bahkan setiap komponen di dalamnya, harus mengulang blok backend "s3" { ... }, blok provider "aws" { ... }, dan konfigurasi yang sama berulang-ulang. Bayangkan tim kalian memiliki 5 environment dan 6 komponen infrastruktur di masing-masing environment — itu 30 salinan konfigurasi backend yang identik. Ketika ada perubahan sekecil satu region, kalian harus mengubah 30 file. Di sinilah Terragrunt hadir sebagai jawaban.

Mengapa topik ini penting di dunia kerja nyata? Karena begitu tim tumbuh melewati satu environment, masalah terbesar bukan lagi menulis kode Terraform, melainkan mengelola pengulangan tanpa kehilangan fleksibilitas. Terragrunt adalah tool yang dipakai ribuan perusahaan — termasuk banyak unicorn startup — untuk membuat infrastruktur scale secara konsisten. Kalian akan sangat sering menjumpainya di pekerjaan DevOps/SRE, jadi memahami arsitekturnya sekarang akan membayar mahal di kemudian hari.

Pembahasan Utama

Masalah DRY pada Arsitektur Multi-Environment

Sebelum masuk ke Terragrunt, mari kita perjelas dulu masalah yang ingin dipecahkan. Ingat konfigurasi backend di episode 5:

dev/vpc/backend.tf
terraform {
  backend "s3" {
    bucket         = "myapp-tfstate-bucket"
    key            = "dev/vpc/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "myapp-tfstate-lock"
  }
}

Sekarang salin blok yang sama ke dev/eks/backend.tf, dev/rds/backend.tf, prod/vpc/backend.tf, dan seterusnya. Belum lagi blok provider yang biasanya ditulis seragam di semua folder:

dev/vpc/provider.tf
provider "aws" {
  region = "ap-southeast-1"
  default_tags {
    tags = {
      Environment = "dev"
      ManagedBy   = "terraform"
    }
  }
}

Masalahnya bukan sekadar "kode tidak cantik". Ini masalah maintenance: setiap perubahan konfigurasi backend atau provider harus disebar manual ke seluruh folder, dan sangat mudah ada satu folder yang terlewat. Folder yang terlewat tersebut kemudian memakai konfigurasi lama yang mungkin sudah salah atau tidak aman. Inilah yang disebut masalah DRY violation — kita mengulang hal yang sama di banyak tempat, padahal aturan emas engineering adalah Don't Repeat Yourself.

Note

Modul (episode 9) sudah menyelesaikan masalah duplikasi resource, tapi tidak menyelesaikan masalah duplikasi konfigurasi di sekitar resource — yaitu backend, provider, dan variabel yang sama diulang di setiap folder environment. Terragrunt mengisi celah ini, bukan menggantikan modul.

Mengenal Terragrunt

Terragrunt adalah thin wrapper — lapisan tipis di atas Terraform CLI — yang dibuat oleh Gruntwork (perusahaan yang didirikan Yevgeniy Brikman, penulis buku Terraform: Up & Running). Terragrunt tidak menulis ulang Terraform; ia membaca file konfigurasi sendiri bernama terragrunt.hcl, lalu memanggil terraform di balik layar sesuai instruksi.

Bayangkan Terragrunt seperti manajer proyek yang mengatur para pekerja. Setiap pekerja (Terraform) sangat pintar membangun satu bangunan (satu komponen infrastruktur), tapi mereka butuh koordinasi: blueprint dari mana, material yang sama di mana, urutan kerja antar gedung bagaimana. Terragrunt yang mengkoordinasikan itu semua, sehingga para pekerja tidak perlu dijelaskan ulang hal yang sama setiap kali.

Filosofi Terragrunt dirangkum dalam tiga pilar:

  1. DRY — satu konfigurasi backend/provider ditulis sekali, diwariskan ke semua environment.
  2. Dependency graph — menjalankan modul yang saling bergantung dalam urutan yang benar secara otomatis.
  3. Multi-environment orchestration — mengeksekusi plan/apply untuk banyak folder sekaligus dengan satu perintah run-all.

Fitur Utama Terragrunt: Keyword Kunci

Untuk memahami Terragrunt, kalian harus menguasai lima keyword berikut. Ini adalah bahasa sehari-hari para pengguna Terragrunt:

KeywordFungsiContoh Penggunaan
terraform.sourceLokasi module Terraform yang akan dieksekusisource = "../modules//vpc"
includeMewarisi konfigurasi dari file induk (root terragrunt.hcl)include "root" { path = find_in_parent_folders() }
remote_stateKonfigurasi backend state secara terpusatremote_state { backend = "s3" ... }
generateMembuat file .tf otomatis (misal provider.tf)generate "provider" { path = "provider.tf" ... }
dependencyMembaca output dari module lain + membuat dependency graphdependency.vpc.outputs.vpc_id
inputsMeneruskan variabel ke module Terraforminputs = { environment = "prod" }
run-allMenjalankan command ke seluruh unit dalam graphterragrunt run-all plan

Important

Perhatikan dua gaya penulisan dependency: dependency (blok) digunakan pada unit-level untuk membaca output modul lain, sedangkan terraform_remote_state (data source) adalah cara lama dari Terraform itu sendiri. Terragrunt memilih dependency karena lebih eksplisit, ikut membangun graph, dan memiliki mock_outputs agar plan tetap berjalan meskipun modul dependensinya belum pernah di-apply.

Struktur Direktori dengan Terragrunt

Pola umum repositori Terragrunt dipecah menjadi dua repositori terpisah: modules (kode modul yang bisa di-reuse) dan live (konfigurasi per environment yang memanggil modul). Berikut contoh struktur yang direkomendasikan:

Struktur direktori repositori infrastructure-live
infrastructure-live/
├── terragrunt.hcl                      # Root: backend + provider + variabel global
├── modules/                            # (biasanya repo terpisah: infra-modules)
   ├── vpc/
   ├── eks/
   └── rds/
├── dev/
   ├── vpc/terragrunt.hcl
   ├── eks/terragrunt.hcl
   └── rds/terragrunt.hcl
└── prod/
    ├── vpc/terragrunt.hcl
    ├── eks/terragrunt.hcl
    └── rds/terragrunt.hcl

Perhatikan satu hal: tidak ada lagi file .tf di folder environment. Setiap folder hanya berisi satu file terragrunt.hcl. File backend.tf dan provider.tf akan dibangkitkan otomatis oleh Terragrunt saat eksekusi. Inilah kunci DRY: kalian tidak menulisnya manual, Terragrunt yang menuliskannya untuk kalian.

Root terragrunt.hcl: Backend & Provider Terpusat

Inti dari seluruh arsitektur ini ada di file root. File ini akan diwarisi (via include) oleh semua folder di bawahnya. Mari kita bedah isinya:

terragrunt.hcl (root)
locals {
  environment = basename(get_terragrunt_dir())
}
 
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<-EOF
    provider "aws" {
      region = "${local.region}"
      default_tags {
        tags = {
          Environment = "${local.environment}"
          ManagedBy   = "terragrunt"
        }
      }
    }
  EOF
}
 
remote_state {
  backend = "s3"
  config = {
    bucket         = "myapp-tfstate-bucket"
    key            = "${local.environment}/${path_relative_to_include()}/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "myapp-tfstate-lock"
  }
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
}
 
inputs = {
  aws_region = "ap-southeast-1"
  environment = local.environment
}

Penjelasan bagian-bagian pentingnya:

  • locals.environment — dihitung otomatis dari nama folder (dev, prod). Ketika file ini diwariskan ke prod/vpc/, nilainya menjadi prod; di dev/eks/ menjadi dev. Satu sumber nilai, banyak pemakaian.
  • generate "provider" — menyuruh Terragrunt membuat file provider.tf di folder unit setiap kali dijalankan. if_exists = "overwrite_terragrunt" memastikan file buatan Terragrunt ditimpa tanpa bertanya. Perhatikan kita tidak perlu menulis file provider.tf manual di folder mana pun.
  • remote_state — satu-satunya tempat definisi backend. Kunci state (key) disusun otomatis dari environment + path relatif, sehingga setiap folder unit punya state masing-masing yang terisolasi, persis filosofi directory-based di episode 10.
  • inputs — nilai yang diteruskan sebagai variabel ke module Terraform, menggantikan file terraform.tfvars yang selama ini diduplikasi.

Tip

path_relative_to_include() adalah fungsi Terragrunt yang menghitung path relatif folder unit terhadap folder root. Kombinasi basename(get_terragrunt_dir()) dan path_relative_to_include() ini adalah dua fungsi yang paling sering dipakai untuk membangun key state yang konsisten dan unik.

Child terragrunt.hcl: include + inputs

Setiap folder unit di bawah dev/ atau prod/ hanya perlu tiga hal: warisi root, tunjuk module, dan kirim variabel. Contoh untuk VPC di environment prod:

prod/vpc/terragrunt.hcl
include "root" {
  path = find_in_parent_folders()
}
 
terraform {
  source = "${get_parent_terragrunt_dir()}//modules/vpc"
}
 
inputs = {
  environment = "prod"
  vpc_name    = "vpc-main"
  cidr_block  = "10.0.0.0/16"
  enable_nat  = true
}
  • include "root"find_in_parent_folders() mencari file terragrunt.hcl terdekat ke atas dan mewarisi seluruh konfigurasinya (provider, backend, inputs). Semua duplikasi dihapus dalam satu langkah.
  • terraform.source — menunjuk lokasi module. Simbol // di ...//modules/vpc menunjukkan bahwa modules/ adalah root module directory dan vpc adalah submodulnya — sintaks standar Terraform registry yang tetap valid di Terragrunt.
  • inputs — nilai spesifik untuk environment ini. Kalian bebas menambah nilai yang sama atau meng-merge dengan nilai global dari root. Inilah fleksibilitas yang selama ini dicari: global di root, spesifik di unit.

Note

Ketika sebuah unit mendefinisikan inputs yang sama dengan root, nilai di unit menang (bersifat overriding). Jika ingin mengubah strategi merge agar root selalu menang, kalian bisa mengatur merge_strategy = "deep" atau "no_merge" pada blok include.

Menghubungkan Modul dengan dependency

Infrastruktur jarang berdiri sendiri: cluster EKS butuh VPC, database RDS butuh berada di subnet yang dibuat VPC. Terragrunt menghubungkan modul-modul ini lewat blok dependency. Contoh prod/eks/terragrunt.hcl yang memakan output dari prod/vpc:

prod/eks/terragrunt.hcl
include "root" {
  path = find_in_parent_folders()
}
 
terraform {
  source = "${get_parent_terragrunt_dir()}//modules/eks"
}
 
dependency "vpc" {
  config_path = "../vpc"
 
  mock_outputs = {
    vpc_id              = "vpc-00000000"
    private_subnet_ids  = ["subnet-00000000"]
    public_subnet_ids   = ["subnet-11111111"]
  }
}
 
inputs = {
  vpc_id             = dependency.vpc.outputs.vpc_id
  private_subnet_ids = dependency.vpc.outputs.private_subnet_ids
  public_subnet_ids  = dependency.vpc.outputs.public_subnet_ids
}

Yang terjadi di balik layar:

  1. config_path membuat Terragrunt tahu bahwa eks bergantung pada vpc.
  2. Saat terragrunt plan dijalankan pada eks, Terragrunt otomatis membaca state vpc (melakukan inisialisasi cepat di folder tersebut) lalu memuat nilai outputs-nya.
  3. Nilai itu diteruskan sebagai inputs ke module EKS — persis seperti melempar data antar komponen tanpa hardcode.

mock_outputs adalah jaring pengaman: nilainya dipakai sementara ketika state vpc belum ada (misal pada run pertama di repo baru), sehingga plan tidak error hanya karena urutan. Di production, nilai asli dari state yang menggantikannya.

Warning

dependency bersifat read-only: Terragrunt membaca output modul lain, tapi tidak pernah otomatis meng-apply modul itu lebih dulu. Jika vpc belum di-apply, output yang dibaca hanya mock_outputs. Biasakan menjalankan terragrunt run-all apply dari root agar seluruh graph di-apply dalam urutan dependensi yang benar, bukan terragrunt apply per folder secara acak.

Menjalankan Terragrunt: Perintah Sehari-hari

Terragrunt bisa dijalankan per unit maupun untuk seluruh graph sekaligus. Berikut perintah yang paling sering dipakai:

terragrunt plan

run-all adalah salah satu fitur paling berharga. Ia membaca seluruh graph dependensi dari dependency blocks, lalu mengeksekusi unit-unit tersebut secara paralel — namun memastikan unit yang jadi dependensi selesai duluan. Bayangkan dengan 18 unit infrastruktur: satu perintah run-all plan cukup untuk melihat rencana perubahan di seluruh environment, tanpa login ke setiap folder satu per satu.

Contoh output terragrunt run-all apply (terpotong)
$ terragrunt run-all apply --terragrunt-non-interactive
 
Initializing the backend...
 
Successfully configured the backend "s3"!
Initializing provider plugins...
- Finding latest version of hashicorp/aws...
 
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
 
Outputs:
 
vpc_id = "vpc-0a1b2c3d4e5f6a7b8"
 
GROUP 5
- /infrastructure-live/prod/vpc
- /infrastructure-live/prod/eks
- /infrastructure-live/prod/rds
 
GROUP 4
...

Perhatikan output mengelompokkan unit berdasarkan urutan eksekusi graph (GROUP). Unit yang tidak saling bergantung berjalan paralel di group yang sama, dan group yang lebih tinggi menunggu group di bawahnya selesai.

Terragrunt vs Pola Terraform Murni

Berikut perbandingan singkat untuk memperjelas posisi Terragrunt dalam arsitektur kalian:

AspekTerraform Murni (Directory-based)Terragrunt
Konfigurasi backendDiduplikasi di tiap folderDitulis sekali di root, diwariskan
Konfigurasi providerDiduplikasi di tiap folderDibangkitkan via generate
Membaca output modul lainterraform_remote_state manualdependency + mock_outputs
Eksekusi multi-folderScript/shell manualrun-all otomatis searah graph
Nilai variabel per envFile .tfvars terpisahinputs terpusat + override
Kompleksitas awalRendahSedang (perlu memahami keyword)
Cocok untukTim kecil, 1–2 environmentTim besar, banyak env & komponen

Kesalahan Umum (Common Pitfalls) Terragrunt

Berikut jebakan yang paling sering ditemui di lapangan:

KesalahanGejalaSolusi
Melupakan if_exists pada generateTerragrunt berhenti & meminta konfirmasi di CIGunakan overwrite_terragrunt
Folder tanpa includeBackend/provider tidak terbentuk → init errorPastikan setiap unit mewarisi root
key state tidak unik antar unitDua unit berbagi state yang sama → saling timpaKombinasikan basename(get_terragrunt_dir()) + path_relative_to_include()
Meng-apply per folder secara acakUrutan salah → EKS gagal karena VPC belum adaGunakan run-all apply
Menghapus mock_outputsPlan error di repo baru/CI clean stateBiarkan mock_outputs selalu ada
Commit file hasil generate ke GitKonflik dan kebingungan overwriteTambahkan pola provider.tf/backend.tf ke .gitignore
Menaruh Terragrunt dan modul dalam satu repoUkuran repo membengkak, coupling eratPisahkan repo infra-live dan infra-modules

Caution

Jangan pernah meng-commit file hasil generate (misal provider.tf, backend.tf yang dibuat Terragrunt di folder unit) ke Git. File tersebut adalah artefak yang akan ditimpa Terragrunt; meng-commitnya hanya membuat repo kotor dan konflik saat CI berjalan. Tambahkan ke .gitignore dan biarkan Terragrunt membuatnya saat runtime.

Penutup

Pada episode 12 ini kita telah membahas bagaimana Terragrunt menyelesaikan masalah duplikasi konfigurasi yang tidak bisa diselesaikan modul biasa. Kita belajar bahwa masalah utama arsitektur multi-environment bukan menulis kode, melainkan mengelola pengulangan backend dan provider. Dengan include, satu file root menjadi sumber kebenaran; dengan generate, file .tf disusun otomatis; dengan dependency, modul saling bertukar output; dan dengan run-all, seluruh environment dieksekusi searah dependency graph hanya dengan satu perintah.

Poin kunci yang perlu kalian bawa pulang:

  • Terragrunt adalah thin wrapper, bukan pengganti Terraform — ia hanya menghapus pengulangan di sekitar kode.
  • Tiga keyword wajib: include (warisan), inputs (variabel), dan dependency (hubungan antar modul).
  • Backend dan provider cukup ditulis sekali di root, lalu dibangkitkan otomatis per unit.
  • run-all mengubah operasi manual 18 folder menjadi satu perintah.
  • Hasil generate adalah artefak runtime — jangan pernah di-commit.

Di episode 13 selanjutnya, kita akan membawa seluruh arsitektur ini ke tingkat berikutnya dengan topik CI/CD Pipeline Automation (GitHub Actions / GitLab CI) — membangun pipeline yang otomatis menjalankan terraform fmt, tflint, security scan, dan plan, dengan approval gate sebelum apply, plus login cloud tanpa password lewat OIDC. Pastikan tetap semangat!