Skip to main content

Validation Rules

เพิ่ม validation block ใน variable เพื่อ enforce rule กับค่า input — fail fast แทนรอ apply ผิดพลาด

ทำไมต้องใช้ Validation?

# ❌ ไม่มี validation
variable "environment" {
type = string
}

# user ใส่ "Prod" หรือ "PRODUCTION" หรือ "prdo" ก็ผ่าน type check
# → apply แล้วเจอ bug ตอน runtime
# ✅ มี validation
variable "environment" {
type = string

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
}

→ Terraform reject ตั้งแต่ตอน plan

Validation Block Syntax

variable "<NAME>" {
type = <TYPE>

validation {
condition = <BOOL_EXPRESSION>
error_message = "..."
}

validation {
# มีหลาย validation block ได้
condition = ...
error_message = "..."
}
}

ตัวอย่าง Validations ที่ใช้บ่อย

1. Enum (whitelist values)

variable "environment" {
type = string

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
}

2. Range (number)

variable "instance_count" {
type = number

validation {
condition = var.instance_count >= 1 && var.instance_count <= 10
error_message = "Instance count must be between 1 and 10."
}
}

3. Regex (string format)

variable "vpc_cidr" {
type = string

validation {
condition = can(regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$", var.vpc_cidr))
error_message = "VPC CIDR must be a valid CIDR notation (e.g., 10.0.0.0/16)."
}
}

4. Length

variable "name" {
type = string

validation {
condition = length(var.name) >= 3 && length(var.name) <= 32
error_message = "Name must be between 3 and 32 characters."
}
}

5. Multiple Conditions

variable "instance_type" {
type = string

validation {
condition = can(regex("^[a-z0-9]+\\.[a-z0-9]+$", var.instance_type))
error_message = "Instance type must follow format like 't3.micro'."
}

validation {
condition = !startswith(var.instance_type, "t1.")
error_message = "t1 instance types are deprecated."
}
}

Function can() — สำคัญมาก!

can() ห่อ expression — return true ถ้าไม่ throw error, false ถ้า throw

# ❌ ผิด — regex throw error ถ้า input ไม่ใช่ string
condition = regex("^[0-9]+$", var.x) != ""

# ✅ ถูก — can() catch error
condition = can(regex("^[0-9]+$", var.x))

ใช้บ่อยมากกับ regex, type conversion, list access

Cross-Variable Validation (Terraform 1.9+)

ก่อนหน้า 1.9 — validation อ้างได้แค่ var.<self> ตั้งแต่ 1.9 — อ้าง variable อื่นได้แล้ว

variable "min_size" { type = number }
variable "max_size" { type = number }

variable "max_size" {
type = number

validation {
condition = var.max_size >= var.min_size
error_message = "max_size must be >= min_size."
}
}

ตัวอย่าง: Production Validation Set

variables.tf
variable "environment" {
type = string
description = "Environment name"

validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be: dev, staging, or prod."
}
}

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

validation {
condition = can(cidrsubnet(var.vpc_cidr, 0, 0))
error_message = "Must be a valid CIDR (e.g., 10.0.0.0/16)."
}

validation {
condition = tonumber(split("/", var.vpc_cidr)[1]) <= 24
error_message = "VPC CIDR mask must be /24 or larger (e.g., /16, /20)."
}
}

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

validation {
condition = can(regex("^t[23]\\.|^m5\\.|^c5\\.", var.instance_type))
error_message = "Only t2/t3/m5/c5 family instances allowed."
}
}

variable "tags" {
type = map(string)
description = "Tags to apply to all resources"

validation {
condition = contains(keys(var.tags), "Environment")
error_message = "tags must include 'Environment' key."
}

validation {
condition = contains(keys(var.tags), "Owner")
error_message = "tags must include 'Owner' key."
}
}

variable "subnet_count" {
type = number
description = "Number of subnets"
default = 2

validation {
condition = var.subnet_count >= 2 && var.subnet_count <= 6
error_message = "subnet_count must be 2-6 (matching AZ count)."
}
}

Custom Conditions ใน Output (Terraform 1.2+)

นอกจาก variable — output block ก็มี precondition กับ postcondition

output "instance_id" {
value = aws_instance.web.id

precondition {
condition = aws_instance.web.instance_type != "t1.micro"
error_message = "t1.micro is deprecated."
}
}

ดูใน Section 6: Outputs / Preconditions

Validation Errors ตอน Plan

$ terraform plan -var="environment=Prod"


│ Error: Invalid value for variable

│ on variables.tf line 1:
│ 1: variable "environment" {

│ Environment must be: dev, staging, or prod.

│ This was checked by the validation rule at variables.tf:5,3-13.

Anti-Patterns

# ❌ ไม่ validate รูปแบบที่ provider จะ validate ให้อยู่แล้ว
variable "instance_type" {
validation {
condition = startswith(var.instance_type, "t")
error_message = "Must start with t."
}
}
# AWS provider จะ error เองถ้า instance type ผิด

# ✅ Validate business rule ที่ provider ไม่รู้
variable "instance_type" {
validation {
condition = !contains(["t1.micro", "t2.nano"], var.instance_type)
error_message = "Use at least t3.small for our workloads."
}
}

สรุป

  • validation block ใน variable — fail fast ตั้งแต่ plan
  • condition = boolean expression
  • error_message = ข้อความที่ user เห็น
  • ใช้ can() ห่อ regex / type conversion เพื่อกัน error
  • Terraform 1.9+ — validate ข้าม variable ได้
  • Validate business rule ไม่ใช่ provider rule (ปล่อยให้ provider เช็คเอง)

ต่อไป → Local Values