Type & Validation #

Variable tanpa type constraint dan validation hanyalah placeholder — siapapun bisa memasukkan nilai apapun, dan error baru muncul ketika apply sudah dijalankan dan provider mencoba membuat resource dengan nilai yang salah. Type constraint dan validation rules memindahkan deteksi error lebih awal: ke saat terraform plan, bahkan sebelum satu pun API call dilakukan. Ini adalah perbedaan antara gagal cepat di lokal dan gagal lambat di production.

Type Constraints Secara Detail #

Type constraint memastikan variable menerima tipe data yang sesuai. Terraform akan error saat plan jika nilai yang diberikan tidak cocok dengan tipe yang dideklarasikan.

# TIPE PRIMITIF dengan constraint ketat

variable "environment" {
  type = string
  # Menerima: "dev", "production", "staging-01"
  # Menolak: 42, true, ["dev"]
}

variable "replica_count" {
  type = number
  # Menerima: 3, 1.5, 0
  # Menolak: "three", true
}

variable "enable_deletion_protection" {
  type = bool
  # Menerima: true, false
  # Juga menerima: "true", "false", "1", "0" (Terraform auto-convert)
}
# TIPE KOLEKSI dengan constraint elemen

variable "allowed_cidrs" {
  type        = list(string)
  description = "CIDR blocks yang diizinkan akses"
  # Menerima: ["10.0.0.0/8", "192.168.0.0/16"]
  # Menolak: ["10.0.0.0/8", 42]  ← elemen bukan string
}

variable "instance_ports" {
  type        = map(number)
  description = "Pemetaan nama layanan ke nomor port"
  # Menerima: { http = 80, https = 443, grpc = 9090 }
  # Menolak: { http = "eighty" }  ← nilai bukan number
}

variable "unique_regions" {
  type        = set(string)
  description = "Set region unik (duplikat otomatis dihapus)"
  # Menerima: ["ap-southeast-1", "us-east-1"]
  # Duplikat dalam set otomatis di-deduplicate oleh Terraform
}
# OBJECT — type constraint paling detail
variable "rds_config" {
  type = object({
    engine            = string
    engine_version    = string
    instance_class    = string
    allocated_storage = number
    multi_az          = bool
    backup_retention  = optional(number, 7)  # Field optional dengan default
  })

  default = {
    engine            = "postgres"
    engine_version    = "15.3"
    instance_class    = "db.t3.medium"
    allocated_storage = 100
    multi_az          = false
    # backup_retention tidak perlu diisi — ada default 7
  }
}

Validation Rules #

Validation rules memungkinkan kamu mendefinisikan constraint yang lebih spesifik dari sekadar tipe data — misalnya “string ini harus salah satu dari nilai yang diizinkan” atau “number ini harus lebih besar dari 0”.

variable "environment" {
  type        = string
  description = "Nama environment deployment"

  validation {
    condition     = contains(["dev", "staging", "production"], var.environment)
    error_message = "Environment harus salah satu dari: dev, staging, production."
  }
}

# Coba set environment = "prod" → error saat plan:
# ╷
# │ Error: Invalid value for variable
# │
# │   on variables.tf line 1:
# │    1: variable "environment" {
# │
# │ Environment harus salah satu dari: dev, staging, production.
# │
# │ This was checked by the validation rule at variables.tf:6,3-13.
# ╵
variable "replica_count" {
  type        = number
  description = "Jumlah replica untuk deployment"

  validation {
    condition     = var.replica_count >= 1 && var.replica_count <= 20
    error_message = "Jumlah replica harus antara 1 dan 20."
  }
}

variable "vpc_cidr" {
  type        = string
  description = "CIDR block untuk VPC utama"

  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "VPC CIDR harus berupa CIDR block yang valid, contoh: 10.0.0.0/16."
  }
}

Multiple Validation Blocks #

Satu variable bisa memiliki lebih dari satu blok validation — masing-masing mengecek kondisi yang berbeda.

variable "instance_type" {
  type        = string
  description = "EC2 instance type"

  # Validasi 1: Harus dimulai dengan prefix yang valid
  validation {
    condition     = can(regex("^(t3|t4g|m5|m6i|c5|c6i|r5|r6i)\\.", var.instance_type))
    error_message = "Instance type harus menggunakan keluarga yang disetujui (t3, t4g, m5, m6i, c5, c6i, r5, r6i)."
  }

  # Validasi 2: Tidak boleh menggunakan ukuran nano atau micro di production
  validation {
    condition     = !can(regex("\\.(nano|micro)$", var.instance_type))
    error_message = "Instance size nano dan micro tidak diizinkan — gunakan minimal small."
  }
}

Validasi dengan Referensi ke Variable Lain #

Validation block bisa mereferensikan variable itu sendiri, tapi tidak bisa mereferensikan variable lain. Untuk cross-variable validation, gunakan locals.

# ANTI-PATTERN: Mencoba referensi variable lain di validation
variable "max_size" {
  type = number

  validation {
    # ✗ Tidak bisa — var.min_size tidak bisa diakses di sini
    condition     = var.max_size >= var.min_size
    error_message = "max_size harus >= min_size."
  }
}

# BENAR: Gunakan locals untuk cross-variable validation
variable "min_size" {
  type = number
}

variable "max_size" {
  type = number
}

locals {
  # Validasi cross-variable menggunakan local value dengan kondisi
  validate_size_range = (
    var.max_size >= var.min_size
    ? null
    : tobool("max_size (${var.max_size}) harus lebih besar atau sama dengan min_size (${var.min_size})")
  )
}

Pola Validasi yang Umum di Production #

# Validasi format AWS resource ID
variable "vpc_id" {
  type = string

  validation {
    condition     = can(regex("^vpc-[a-f0-9]{8,17}$", var.vpc_id))
    error_message = "vpc_id harus berformat 'vpc-' diikuti 8-17 karakter hex."
  }
}

# Validasi nama yang aman untuk resource naming
variable "project_name" {
  type = string

  validation {
    condition     = can(regex("^[a-z][a-z0-9-]{2,30}[a-z0-9]$", var.project_name))
    error_message = "project_name harus lowercase, dimulai huruf, hanya huruf/angka/tanda hubung, panjang 4-32 karakter."
  }
}

# Validasi nilai sensitif tidak kosong
variable "db_password" {
  type      = string
  sensitive = true

  validation {
    condition     = length(var.db_password) >= 16
    error_message = "Password database harus minimal 16 karakter."
  }
}

# Validasi CIDR tidak overlap dengan range yang dilarang
variable "app_cidr" {
  type = string

  validation {
    condition     = can(cidrhost(var.app_cidr, 0)) && !startswith(var.app_cidr, "169.254.")
    error_message = "app_cidr harus CIDR valid dan bukan link-local (169.254.x.x)."
  }
}

Ringkasan #

  • Type constraint mendeteksi tipe yang salah saat plan — jauh lebih baik dari error saat apply ketika resource sudah sebagian terbuat.
  • object() dengan optional() untuk variable yang mengandung banyak sub-field dengan beberapa field yang bisa dikosongkan.
  • Validation block untuk constraint semantik — nilai yang secara tipe benar tapi tidak valid secara bisnis: environment hanya boleh “dev/staging/production”, CIDR harus valid, panjang minimal, dll.
  • Multiple validation blocks untuk memisahkan kondisi yang berbeda — pesan error menjadi lebih spesifik dan membantu.
  • Cross-variable validation menggunakan locals — validation block tidak bisa mengakses variable lain, tapi tobool() di locals bisa digunakan untuk membuat error yang informatif.
  • Investasi di validation rules di awal menghemat waktu debugging di kemudian hari — error yang ditemukan di plan jauh lebih murah dari error yang ditemukan di production.

← Sebelumnya: Apa itu Variable?   Berikutnya: File & Environment →

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