Belajar Terraform - Formatting, Linting & Automated Testing
Episode 11 of 21

Belajar Terraform - Formatting, Linting & Automated Testing

Jaga kualitas dan keamanan kode IaC dengan pipeline quality gate: terraform fmt & validate, linter tflint, security scanner Checkov dan Trivy, hingga automated testing infrastruktur sungguhan menggunakan Terratest berbasis Go.

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

Pendahuluan

Setelah di episode 10 sebelumnya kita membahas Workspaces vs Directory-Based Multi-Environment — bagaimana mengatur dev, staging, dan prod secara terisolasi — pada episode kali ini kita membahas hal yang menentukan apakah kode kalian layak masuk ke prod atau tidak: quality gate.

Bayangkan ada tiga developer yang menulis kode Terraform dengan gaya masing-masing: satu tidak pernah merapikan indentasi, satu lupa menutup braces sehingga apply gagal di tengah malam, satu lagi tanpa sengaja membuat S3 bucket publik yang datanya bisa dibaca semua orang. Masing-masing menganggap kodenya "jalan" karena terraform apply berhasil di laptopnya. Tapi di skala tim, kode seperti ini adalah bom waktu — dan tidak ada satu pun yang bisa dideteksi oleh terraform plan biasa.

Kabar baiknya, ekosistem Terraform dewasa ini punya rangkaian tools yang saling melengkapi untuk menangkap masalah ini sebelum menyentuh cloud:

  • Formatting & validasiterraform fmt dan terraform validate untuk basis sanitasi kode.
  • Static analysistflint untuk aturan spesifik provider, plus Checkov dan Trivy untuk keamanan & compliance.
  • Automated testingTerratest untuk benar-benar membuat infrastruktur dan memastikan semuanya bekerja, bukan sekadar valid sintaks.

Di episode ini kita akan menjalankan semuanya satu per satu, memahami outputnya, lalu menyusunnya menjadi alur gate yang bisa dipasang di pipeline CI/CD (yang akan kita bangun utuh di episode 13). Inilah perbedaan antara tim yang "berharap kode benar" dan tim yang "membuktikan kode benar".

Pembahasan Utama

Formatting & Validation: Dasar Sanitasi Kode

terraform fmt — Merapikan Kode Secara Otomatis

Tidak peduli seberapa baik engineer-nya, manusia tidak konsisten dalam spasi dan indentasi. terraform fmt menulis ulang format kode kalian ke satu standar baku HashiCorp: indentasi 2 spasi, satu baris kosong antara blocks, alokasi = yang sejajar, dan seterusnya.

Perhatikan contoh main.tf yang berantakan ini — masih valid secara sintaks, tapi sulit dibaca:

main.tf (sebelum fmt)
resource "aws_instance" "web" {
    ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
tags = {
    Name="web-server"
    }
}

Jalankan terraform fmt untuk merapikannya:

Format satu file / seluruh project
terraform fmt main.tf
terraform fmt -recursive
Cek tanpa mengubah + lihat diff
terraform fmt -check
terraform fmt -recursive -check -diff

Hasil setelah diformat — bandingkan dengan versi sebelumnya:

main.tf (sesudah fmt)
resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
 
  tags = {
    Name = "web-server"
  }
}

Dengan terraform fmt -check, perintah ini berperilaku seperti linter: mengembalikan exit code non-zero jika ada file yang belum rapi — sempurna untuk dipakai sebagai gate di CI. Untuk melihat persisnya apa yang diubah, gunakan -diff:

Output terraform fmt -diff
--- a/main.tf
+++ b/main.tf
@@ -1,8 +1,8 @@
 resource "aws_instance" "web" {
-    ami           = data.aws_ami.ubuntu.id
+  ami           = data.aws_ami.ubuntu.id
   instance_type = "t3.micro"

Untuk menandai perubahan secara inline di editor/PR, format diff juga bisa direpresentasikan dengan marker [!code --] (baris dihapus) dan [!code ++] (baris ditambah):

Tanda inline diff: -- dihapus, ++ ditambah
resource "aws_instance" "web" {       
resource "aws_instance" "nginx" {     
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
}

Note

Perhatikan bahwa terraform fmt hanya menata format — ia tidak akan memperbaiki kesalahan logika atau atribut yang salah. fmt adalah perapian layout, bukan penguji kebenaran. Anggap ia seperti prettier/gofmt di dunia JavaScript/Go.

terraform validate — Validasi Sintaks & Konsistensi Internal

fmt merapikan, validate memeriksa kesehatan. terraform validate memverifikasi bahwa konfigurasi secara sintaks valid, semua referensi terdefinisi (variabel ada, atribut resource yang dirujuk memang ada), dan argument yang dipakai valid menurut skema provider.

Validasi konfigurasi
terraform validate
Output validate sukses
Success! The configuration is valid.

Jika ada kesalahan, misalnya merujuk variabel yang tidak dideklarasikan atau menulis atribut yang tidak dikenal provider, validate akan menunjuk lokasinya dengan presisi:

Output validate gagal
Error: Reference to undeclared input variable
  on main.tf line 7, in resource "aws_instance" "web":
   7:   instance_type = var.instance_type_x
 
An input variable with the name "instance_type_x" has not been declared.

Important

terraform validate tidak membaca nilai .tfvars (karena ia tidak butuh nilai konkret, hanya tipe). Namun ia membutuhkan provider terinstal — jadi jalankan terraform init terlebih dahulu. Ingat urutan emasnya: fmtvalidateplan. fmt memastikan rapi, validate memastikan valid, plan memastikan aman.

tflint: Linter Spesifik Cloud Provider

terraform validate hanya memeriksa struktur umum. Ia tidak tahu bahwa t3.microx bukan instance type yang valid di AWS, atau bahwa region ap-southeast-1x tidak ada. Di sinilah tflint masuk — linter yang memahami aturan spesifik provider, menangkap error, anti-pattern, dan unused declaration yang luput dari validasi standar.

Instalasi (dua cara paling umum):

brew install tflint

Sebelum tflint bisa memeriksa aturan provider, kita perlu mengunduh rule plugin-nya dengan tflint --init, lalu jalankan:

Inisialisasi & jalankan tflint
tflint --init
tflint --recursive

Contoh output — perhatikan bahwa tflint menemukan kesalahan yang tidak akan ditemukan terraform validate:

Output tflint
4 issue(s) found:
 
Error: [Fixable] "t3.microx" is an invalid instance type (aws_instance_invalid_type)
 
  on main.tf line 5:
   5:   instance_type = "t3.microx"
 
Warning: [Fixable] Missing default value for variable (terraform_default_required_provider_version)
 
  on variables.tf line 1:
   1: variable "region" {}
 
Warning: Duplicate tag key "Name" (aws_resource_missing_tags)
 
  on main.tf line 10:
  10:   tags = {
  11:     Name = "web"
  12:     name = "WEB"
  13:   }

Banyak issue tflint bahkan bisa diperbaiki otomatis dengan tflint --fix. Yang penting, tflint juga jalan di mode recursive sehingga bisa memeriksa seluruh modul dalam project — persis yang kita butuhkan setelah mempelajari modularitas di episode 9.

Tip

tflint mengunduh rule provider dari .tflint.hcl. Di project enterprise, file ini biasanya dikomit ke repo dengan daftar rule tambahan dari komunitas seperti terraform-unused-declarations dan terraform-required-version, sehingga setiap anggota tim dan CI menjalankan standar yang sama persis.

Checkov & Trivy: Security & Compliance Scanner

fmt, validate, dan tflint memastikan kode benar secara teknis. Tapi "benar secara teknis" tidak berarti "aman". Scanner keamanan menangkap miskonfigurasi seperti S3 bucket publik, encryption yang mati, atau IAM policy yang terlalu permisif — hal-hal yang secara sintaks valid tapi merupakan pelanggaran standar keamanan (CIS Benchmarks, SOC2, PCI-DSS).

Checkov

Checkov adalah scanner open-source dari Bridgecrew (kini bagian dari Prisma Cloud) yang memeriksa Terraform, CloudFormation, Kubernetes, dan banyak lagi dengan ratusan policy bawaan.

Instal Checkov
pip install checkov
Scan seluruh direktori konfigurasi
checkov -d .

Contoh output — perhatikan kode policy CKV_AWS_* yang unik untuk setiap check:

Output checkov
       _               _
   ___| |__   ___  ___  |_  _ __
  / __| '_ \ / _ \/ _ \ | | '_ \
 | (__| | | |  __/  __/ |_| | | | |
  \___|_| |_|\___|\___| (_|_| |_|_|
 
By bridgecrew.io | version: 3.2.5
 
terraform scan results:
 
Passed checks: 14, Failed checks: 2, Skipped checks: 0
 
Check: CKV_AWS_126: "Ensure that S3 bucket has versioning enabled"
	FAILED for resource: aws_s3_bucket.data
	File: /s3.tf:1-8
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-policy-index/ckv-aws-126
 
Check: CKV_AWS_115: "Ensure that no IAM policy with EBS privileges is attached"
	FAILED for resource: aws_iam_policy.policy
	File: /iam.tf:9-20
	Guide: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-policy-index/ckv-aws-115

Checkov menampilkan file dan baris persis dari pelanggaran, plus link guide untuk perbaikannya. Ia juga bisa diarahkan untuk hanya memeriksa severity tertentu atau melewati check yang sudah disetujui dengan --skip-check.

Trivy

Trivy adalah scanner universal dari Aqua Security. Untuk IaC, perintah trivy config menganalisis misconfigurasi (dengan engine yang sama seperti Checkov) sekaligus mendeteksi hardcoded secrets di file konfigurasi — kombinasi yang sangat praktis karena sekaligus menangkap masalah state-file rahasia yang akan kita bahas di episode 15.

Instal Trivy (Debian/Ubuntu)
sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy
Scan kode IaC dengan Trivy
trivy config .

Contoh output — Trivy mengelompokkan hasil per file dengan ID AVD-AWS-* dan severity:

Output trivy config
main.tf (terraform)
===================
Tests: 8 (SUCCESS: 6, FAILED: 2, WARNING: 0)
Failures: 2
 
AVD-AWS-0086 (HIGH): S3 bucket does not have server-side encryption enabled
════════════════════════════════════════════════════════════════════════
S3 buckets should be encrypted with encryption keys. By default, S3...
See https://avd.aquasec.com/misconfig/avd-aws-0086
────────────────────────────────────────────────────────────────────────
  main.tf:3-10
────────────────────────────────────────────────────────────────────────

Warning

Scanner keamanan tidak mengenal konteks. Sebuah S3 bucket yang sengaja dibuat publik untuk static website akan selalu dilaporkan sebagai pelanggaran. Karena itu di pipeline CI, jadikan hasil scan sebagai gate yang bisa di-approve secara eksplisit — misalnya dengan whitelist check + alasan, bukan asal melewati semua temuan. Konteks harus dicatat, bukan disembunyikan.

Ringkasan: Peta Tools Quality Gate

ToolKategoriApa yang DicekEkstensi
terraform fmtFormattingKerapian & konsistensi layout HCLHCL
terraform validateValidasiSintaks, referensi, dan skema providerHCL
tflintLintingAturan spesifik provider & best practice (mis. region/instance type valid)HCL
CheckovSecurity scanMisconfigurasi keamanan & compliance (CIS, SOC2, dsb.)HCL & IaC lain
TrivySecurity scanMisconfigurasi + hardcoded secrets (multi format)HCL & IaC lain
TerratestAutomated testingApply + assert infrastruktur nyataGo

Automated Testing dengan Terratest

Semua tool di atas bersifat statis — mereka membaca kode, tidak menjalankannya. Tidak ada yang bisa menjamin bahwa VPC yang kalian konfigurasi benar-benar punya dua subnet publik yang bisa diakses internet. Untuk itu ada Terratest: library Go buatan Gruntwork yang menjalankan terraform apply sungguhan, memeriksa hasilnya, lalu menghancurkan semuanya.

Ini membuat Terratest menjadi tulang punggung integration test infrastruktur: kode yang sama dipakai untuk memvalidasi modul kita di examples/ sekaligus menjaga agar perubahan modul tidak merusak perilakunya.

Test ditulis sebagai fungsi Go biasa, memakai helper terraform dari Terratest. Perhatikan struktur wajibnya: definisikan options, pastikan destroy selalu dijalankan lewat defer, lalu InitAndApply, assert output, dan selesai:

test/vpc_test.go
package test
 
import (
	"testing"
 
	"github.com/gruntwork-io/terratest/modules/terraform"
	"github.com/stretchr/testify/assert"
)
 
func TestVPCModule(t *testing.T) {
	t.Parallel()
 
	options := &terraform.Options{
		TerraformDir: "../examples/vpc",
		Vars: map[string]interface{}{
			"name": "terratest-vpc",
			"cidr": "10.0.0.0/16",
		},
	}
 
	// Jaminan utama: apa pun yang terjadi (termasuk assertion gagal),
	// infrastruktur tetap dihancurkan setelah test selesai.
	defer terraform.Destroy(t, options)
 
	terraform.InitAndApply(t, options)
 
	vpcID := terraform.Output(t, options, "vpc_id")
	assert.NotEmpty(t, vpcID)
	assert.Contains(t, vpcID, "vpc-")
 
	subnetIDs := terraform.OutputList(t, options, "public_subnet_ids")
	assert.Len(t, subnetIDs, 2)
}

Poin-poin kunci yang wajib dipahami:

  1. defer terraform.Destroy(t, options) — dijalankan paling akhir, selalu, bahkan saat test gagal. Tanpa ini, test yang gagal akan meninggalkan infrastruktur berjalan dan menagih biaya terus-menerus.
  2. terraform.InitAndApply — satu helper yang menjalankan init lalu apply -auto-approve, dan otomatis gagal (fail) jika ada error.
  3. terraform.Output / OutputList — membaca nilai output modul kita (dari episode 9) dan membandingkannya dengan ekspektasi via testify/assert.
  4. t.Parallel() — test antar modul yang berbeda bisa dijalankan paralel untuk mempercepat, karena setiap test punya state-nya sendiri.

Menjalankan Test

Terratest adalah project Go biasa, jadi butuh inisialisasi module dan install dependency:

Setup & jalankan Terratest
go mod init github.com/company/terraform-modules
go get github.com/gruntwork-io/terratest/modules/terraform@latest
go test ./test/ -v -timeout 30m
Output go test
=== RUN   TestVPCModule
TestVPCModule 2026-08-02T10:00:00+07:00 retry.go:99: Running terraform [init ...]
TestVPCModule 2026-08-02T10:00:01+07:00 retry.go:99: Running terraform [apply -auto-approve ...]
TestVPCModule 2026-08-02T10:00:45+07:00 retry.go:99: Running terraform [destroy -auto-approve ...]
--- PASS: TestVPCModule (47.34s)
PASS
ok  	github.com/company/terraform-modules	47.344s

Caution

Terratest benar-benar membuat infrastruktur di cloud — berpotensi menimbulkan biaya dan menggunakan kredensial akun sungguhan. Jangan pernah menjalankan test suite Terratest terhadap environment prod, dan pastikan setiap test dijalankan dengan defer terraform.Destroy serta timeout yang cukup (-timeout 30m). Idealnya Terratest dijalankan di CI dengan akun terisolasi, memakai modul examples/ yang memang didesain untuk dihancurkan.

Menyatukan Semua: Pre-Commit Hooks

Setelah punya rangkaian tool, cara paling efektif memastikan kualitas di setiap developer adalah memasangnya sebagai pre-commit hooks — script yang otomatis dijalankan sebelum git commit. Dengan begitu, masalah tertangkap sebelum kode meninggalkan laptop. Paket paling populer untuk ini adalah pre-commit-terraform:

.pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.92.0
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_tflint
      - id: checkov
      - id: terraform_trivy
Pasang hooks
pip install pre-commit
pre-commit install

Dengan konfigurasi di atas, setiap git commit otomatis menjalankan fmt, validate, tflint, checkov, dan trivy — dan memblokir commit jika ada yang gagal. Urutan gate yang ideal untuk project produksi nantinya adalah: pre-commit di laptop → pipeline CI saat PR (fmt, validate, tflint, scan, plan) → approval → apply. Alur penuh ini akan kita rakit bersama di episode 13.

Kesalahan Umum (Common Pitfalls)

1. terraform validate sebelum terraform init

Error: Missing required provider atau Module not installed akan muncul karena validate butuh skema provider. Selalu init dulu.

2. Menganggap terraform fmt sudah cukup untuk "kode aman"

fmt hanya merapikan layout. Ia tidak menangkap instance type salah, bucket publik, maupun IAM permisif. Kumpulkan fmt + validate + tflint + scanner secara berurutan, bukan salah satunya.

3. Menambahkan scanner tanpa mengerti outputnya

Menjalankan Checkov/Trivy lalu mengabaikan semua temuan (--skip-check .*) sama saja dengan tidak menjalankan. Pahami tiap temuan, buat whitelist dengan alasan, dan pertahankan jumlah fail = 0 di pipeline.

4. Test Terratest tanpa defer Destroy

Ini penyebab paling umum "biaya cloud misterius" dari tim yang baru belajar testing. Selalu pakai defer terraform.Destroy(t, options) tepat setelah options dibuat, dan set -timeout agar test yang hang tidak berjalan selamanya.

5. Menjalankan Terratest terhadap environment prod

Terratest apply tanpa pandang bulu. Pisahkan akun/workspace test dari produksi, dan jangan pernah mengarahkan TerraformDir ke direktori environment prod.

6. Tidak menjadikan kualitas sebagai gate di CI

Kalau tools hanya dipakai manual di laptop, hasilnya tidak konsisten antar orang. Pasang gate di pipeline (dan pre-commit) sehingga standar dijalankan otomatis — konsistensi itulah yang membuat tim cepat, bukan individunya.

Penutup

Pada episode 11 ini kalian telah membangun fondasi quality gate untuk kode Terraform: merapikan dan memvalidasi dengan terraform fmt -recursive dan terraform validate; menangkap error spesifik provider dengan tflint; memindai miskonfigurasi keamanan dan compliance dengan Checkov dan Trivy; menulis integration test sungguhan dengan Terratest — apply, assert, dan destroy infrastruktur asli lewat test Go; serta mengotomatiskan semuanya lewat pre-commit hooks.

Poin kunci yang perlu kalian bawa pulang:

  • Urutan gate: fmtvalidatetflint → security scan → plan.
  • Scanner statis tidak bisa menggantikan test yang benar-benar menjalankan infrastruktur.
  • defer terraform.Destroy adalah pengaman wajib setiap test Terratest.
  • Otomatisasi lewat pre-commit dan CI membuat standar konsisten di seluruh tim.

Kalian sekarang punya kode yang rapi, aman, dan teruji. Tapi ada satu masalah klasik yang tersisa: semua kualitas itu bisa runtuh jika struktur projectnya berantakan — khususnya duplikasi konfigurasi antar environment yang masih kita tulis manual di episode 10. Bagaimana menjaga kode tetap DRY di tengah puluhan environment?

Di episode 12 selanjutnya kita akan membahas Advanced Architecture dengan Terragrunt — thin wrapper buatan Gruntwork untuk menghilangkan duplikasi backend, provider, dan input antar environment, lengkap dengan fitur include, dependency, dan inputs. Pastikan tetap semangat!

Belajar Terraform - Formatting, Linting & Automated Testing | Belajar Terraform