Versioning #
Module tanpa versioning adalah module yang tidak bisa dipercaya untuk production. Bayangkan dua tim menggunakan module VPC yang sama — tim A di-apply hari ini, tim B apply minggu depan. Jika di antara itu ada perubahan di module yang breaking, tim B akan mendapat hasil yang berbeda dari tim A tanpa ada peringatan apapun. Versioning adalah mekanisme yang memastikan setiap pemanggil mendapat versi module yang sama dengan yang mereka pilih, sampai mereka memutuskan untuk upgrade.
flowchart TD
A["Module\nVersioning"] --> B["Semantic\nVersioning"]
A --> C["Version\nConstraints"]
A --> D["Lock\nFile"]
B --> E["MAJOR.MINOR.PATCH\nBreaking.Features.Fixes"]
C --> F["~>, >=, <\nConstraints"]
D --> G[".terraform.lock.hcl\nDependency Lock"]
style A fill:#3b82f6,stroke:#1e40af,color:#fff
style B fill:#10b981,stroke:#059669,color:#fff
style C fill:#f59e0b,stroke:#d97706,color:#fff
style D fill:#8b5cf6,stroke:#6d28d9,color:#fffMengapa Module Perlu Versi #
TANPA VERSIONING:
Team A: terraform apply hari Senin
→ Menggunakan module VPC versi "terbaru"
→ Berhasil, infrastruktur berjalan
Developer module: commit perubahan ke module (Selasa)
→ Mengubah output dari "subnet_id" ke "subnet_ids" (breaking change)
Team B: terraform apply hari Rabu
→ Menggunakan module VPC versi "terbaru" (yang berbeda!)
→ ERROR: "module.vpc.subnet_id" tidak ada
→ Team B bingung — konfigurasi mereka tidak berubah
DENGAN VERSIONING:
module "vpc" {
source = "git::https://github.com/org/tf-module-vpc.git?ref=v1.2.0"
}
Team A dan Team B keduanya menggunakan v1.2.0 → hasil yang sama.
Developer module rilis v2.0.0 dengan breaking change.
Team B tidak akan terdampak sampai mereka memutuskan upgrade ke v2.0.0.
Semantic Versioning untuk Module #
Module Terraform mengikuti konvensi semantic versioning (semver): MAJOR.MINOR.PATCH.
SEMANTIC VERSIONING:
v1.2.3
│ │ │
│ │ └── PATCH: Bug fix, tidak ada perubahan interface
│ │ v1.2.3 → v1.2.4: Fix bug di security group rule
│ │
│ └──── MINOR: Fitur baru, backward compatible
│ v1.2.0 → v1.3.0: Tambah support untuk IPv6 (opsional)
│ Pengguna v1.2.0 tidak terpengaruh
│
└────── MAJOR: Breaking change
v1.x.x → v2.0.0: Rename output "subnet_id" → "subnet_ids"
Semua pengguna v1.x.x harus update konfigurasi sebelum upgrade
CONTOH CHANGELOG YANG BAIK:
v2.0.0 (Breaking Change)
BREAKING: Output "subnet_id" diganti menjadi "subnet_ids" (list)
BREAKING: Variable "az_count" dihapus, gunakan "subnet_count"
NEW: Support untuk NAT Gateway per-AZ
v1.3.0
NEW: Tambah variable "enable_ipv6" (default: false)
NEW: Output "ipv6_cidr_block" ditambahkan
FIX: Security group tidak menghapus rule lama saat update
v1.2.4
FIX: Subnet CIDR kalkulasi error saat VPC CIDR lebih kecil dari /16
Membuat Rilis Module di Git #
Untuk module yang disimpan di Git repository, versi ditentukan oleh Git tag.
# Workflow rilis module:
# 1. Pastikan semua perubahan sudah di-commit dan di-test
git add -A
git commit -m "feat: tambah support NAT Gateway per-AZ"
# 2. Buat tag dengan format v<MAJOR>.<MINOR>.<PATCH>
git tag v1.3.0
# 3. Push tag ke remote
git push origin v1.3.0
# 4. Opsional: buat GitHub Release dengan changelog
# (bisa lewat UI atau GitHub CLI)
gh release create v1.3.0 \
--title "v1.3.0 — NAT Gateway per-AZ" \
--notes "NEW: Support NAT Gateway per-AZ via variable enable_nat_per_az"
# Pemanggil menggunakan tag sebagai referensi versi
module "vpc" {
source = "git::https://github.com/org/terraform-module-vpc.git?ref=v1.3.0"
cidr_block = "10.0.0.0/16"
environment = var.environment
}
Versioning di Terraform Registry #
Untuk module yang dipublikasikan ke Terraform Registry (public atau private), versioning lebih formal dan menggunakan argumen version.
# Terraform Registry — versi dikontrol dengan version constraint
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.1"
# ~> 5.1 = >=5.1.0, <6.0.0
name = "production-vpc"
cidr = "10.0.0.0/16"
}
# Constraint yang lebih ketat untuk production
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "= 20.2.1"
# Pin ke versi exact — tidak ada surprise update
}
# ANTI-PATTERN: Tanpa version constraint di Registry module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
# Tidak ada version — setiap terraform init bisa mendapat versi berbeda!
}
flowchart TD
A["module \"vpc\"\nversion = \"~> 3.0\""] --> B["v3.0.0 ✅"]
A --> C["v3.1.0 ✅"]
A --> D["v3.9.0 ✅"]
A --> E["v4.0.0 ❌\n(Major bump)"]
style A fill:#3b82f6,stroke:#1e40af,color:#fff
style B fill:#10b981,stroke:#059669,color:#fff
style C fill:#10b981,stroke:#059669,color:#fff
style D fill:#10b981,stroke:#059669,color:#fff
style E fill:#ef4444,stroke:#dc2626,color:#fffStrategi Upgrade yang Aman #
Mengupgrade versi module, terutama untuk MAJOR version, perlu dilakukan dengan hati-hati.
# WORKFLOW UPGRADE MODULE:
# 1. Baca CHANGELOG versi baru — cari breaking change
# 2. Test di environment dev/staging dulu
# 3. Jalankan terraform plan setelah update versi
# Perubahan di konfigurasi:
# Dari:
# source = "git::...?ref=v1.2.0"
# Ke:
# source = "git::...?ref=v2.0.0"
# 4. Baca output plan dengan cermat
terraform plan
# Perhatikan resource mana yang akan di-replace
# Breaking change di module sering menyebabkan resource di-replace
# 5. Jika ada replace yang tidak diinginkan, pertimbangkan:
# - Apakah perlu moved block untuk rename resource?
# - Apakah perlu tahap migrasi sebelum upgrade penuh?
# Contoh: module merename resource internal dari v1 ke v2
# Tanpa moved block, Terraform akan destroy + create
# Di konfigurasi root (bukan di module):
moved {
from = module.vpc.aws_subnet.public
to = module.vpc.aws_subnet.public_tier # Nama baru di module v2
}
Mengelola Breaking Change sebagai Penulis Module #
# STRATEGI 1: Deprecation dengan backward compatibility sementara
# Beri masa transisi sebelum benar-benar hapus variable/output lama
variable "subnet_id" {
description = "DEPRECATED: Gunakan subnet_ids. Akan dihapus di v3.0."
type = string
default = null
validation {
condition = var.subnet_id == null
error_message = "subnet_id sudah deprecated, gunakan subnet_ids (list)."
# Atau biarkan berjalan tapi log warning via null_resource
}
}
variable "subnet_ids" {
description = "List ID subnet. Gantikan subnet_id yang deprecated."
type = list(string)
default = []
}
locals {
# Kompatibilitas backward: jika subnet_id diisi, jadikan list
effective_subnet_ids = (
length(var.subnet_ids) > 0
? var.subnet_ids
: var.subnet_id != null ? [var.subnet_id] : []
)
}
Semantic Versioning dalam Praktik #
Semantic versioning (MAJOR.MINOR.PATCH) sangat penting untuk module yang dipublikasikan. Setiap perubahan harus diklasifikasikan dengan benar.
# PATCH (x.y.Z): Perbaikan bug yang tidak mengubah interface
# Contoh: fix typo di description, perbaiki tag yang salah
# Tidak ada breaking change → consumer tidak perlu ubah apapun
git tag v1.2.1
# MINOR (x.Y.z): Fitur baru yang backward-compatible
# Contoh: tambah output baru, tambah optional variable baru
# Consumer existing tidak terpengaruh → bisa upgrade dengan aman
git tag v1.3.0
# MAJOR (X.y.z): Breaking change
# Contoh: hapus variable, ubah tipe output, hapus resource
# Consumer HARUS review dan update → upgrade perlu perencanaan
git tag v2.0.0
flowchart TD
A["Perubahan
di module"] --> B{"Breaking
change?"}
B -->|"Ya"| C["MAJOR
version bump
(X.0.0)"]
B -->|"Tidak"| D{"Fitur
baru?"}
D -->|"Ya"| E["MINOR
version bump
(x.Y.0)"]
D -->|"Tidak"| F{"Bug fix?"}
F -->|"Ya"| G["PATCH
version bump
(x.y.Z)"]
F -->|"Tidak"| H["Tidak perlu
bump"]
style A fill:#e3f2fd,stroke:#1565c0
style C fill:#ffebee,stroke:#c62828
style E fill:#fff3e0,stroke:#e65100
style G fill:#e8f5e9,stroke:#2e7d32Version Constraint Patterns #
# Eksak — hanya versi tertentu (terlalu ketat untuk production)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2" # Tidak akan pernah auto-update
}
# Pessimistic — patch update saja (recommended untuk stability)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.1" # >= 5.1.0, < 5.2.0
}
# Minor update — termasuk minor dan patch
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = ">= 5.1.0, < 6.0.0" # Semua 5.x tapi bukan 6.0
}
Version Constraint Best Practices #
# Untuk module internal (di kontrol kamu sendiri):
module "app" {
source = "./modules/app"
version = "~> 2.1" # Boleh patch update
}
# Untuk module eksternal (tidak kamu kontrol):
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # Lebih ketat — hanya patch
}
# Untuk provider (stabilitas sangat penting):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Pessimistic ~> untuk stability
}
}
}
# Cek versi yang tersedia
terraform providers lock -platform=linux_amd64
# Generate lock file untuk konsistensi cross-platform
# Update provider ke versi terbaru
terraform init -upgrade
# Hati-hati: bisa mengubah behavior
Pinning Provider Versions #
# Di dalam module, JANGAN pin provider version
# Biarkan root module yang menentukan
# Module (modules/networking/main.tf):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
# JANGAN tentukan version di sini
# Biarkan root module menentukan
}
}
}
# Root module (main.tf):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Version ditentukan di root
}
}
}
# .terraform.lock.hcl — commit ke version control
# File ini memastikan semua developer dan CI/CD
# menggunakan versi provider yang sama
# Generate lock file untuk multiple platforms
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 -platform=windows_amd64
# Update provider ke versi terbaru
terraform init -upgrade
# Review perubahan lock file
git diff .terraform.lock.hcl
Version Constraint Operators #
# OPERATORS:
# = (exact): version = "5.0.0"
# != (exclude): version != "5.0.0"
# > (gt): version > "5.0.0"
# >= (gte): version >= "5.0.0"
# < (lt): version < "6.0.0"
# <= (lte): version <= "5.5.0"
# ~> (pessimistic): version ~> "5.0" (allows 5.x but not 6.0)
# CONTOH:
# ~> 5.0 = >= 5.0, < 6.0
# ~> 5.1 = >= 5.1, < 6.0
# >= 5.0, < 6.0 = sama dengan ~> 5.0
# BEST PRACTICE:
# Module di registry: gunakan ~> untuk minor version
# Provider: gunakan ~> untuk major version
# Internal module: pin exact version
Ringkasan #
- Versioning adalah kebutuhan, bukan pilihan — tanpa versi, dua tim yang apply di waktu berbeda bisa mendapat hasil yang berbeda.
- Semantic versioning: PATCH untuk bug fix, MINOR untuk fitur backward-compatible, MAJOR untuk breaking change.
- Git tag sebagai versi untuk module di Git repo —
?ref=v1.2.0di source URL.versionconstraint di Terraform Registry — gunakan~>untuk fleksibilitas patch/minor,=untuk pin ketat di production.- Jangan pernah module tanpa version constraint untuk source dari Registry — setiap
terraform initbisa mendapat versi berbeda.- Upgrade major version secara bertahap — test di dev/staging dulu, baca output plan dengan cermat, gunakan
movedblock jika ada rename resource internal.
← Sebelumnya: Root vs Child Module Berikutnya: Interface Design →