Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

Terraform Variable Validation

Variable validation lets you define rules that input values must satisfy. When a rule fails, Terraform stops before planning with a clear error message — catching configuration mistakes before any API calls are made.


Basic Validation Syntax

variable "variable_name" {
type = string
validation {
condition = <boolean expression using var.variable_name>
error_message = "Human-readable error message."
}
}

A variable can have multiple validation blocks — all must pass.


Common Validation Patterns

Allowed Values (Enum)

variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "environment must be one of: dev, staging, production."
}
}
variable "log_level" {
type = string
validation {
condition = contains(["DEBUG", "INFO", "WARN", "ERROR"], var.log_level)
error_message = "log_level must be DEBUG, INFO, WARN, or ERROR."
}
}

Numeric Range

variable "replica_count" {
type = number
validation {
condition = var.replica_count >= 1 && var.replica_count <= 50
error_message = "replica_count must be between 1 and 50."
}
}
variable "port" {
type = number
validation {
condition = var.port >= 1 && var.port <= 65535
error_message = "Port number must be between 1 and 65535."
}
validation {
condition = var.port >= 1024 || contains([80, 443], var.port)
error_message = "Non-standard ports must be >= 1024. Only 80 and 443 are allowed below 1024."
}
}

String Pattern with Regex

variable "cluster_name" {
type = string
validation {
condition = can(regex("^[a-z][a-z0-9-]{2,62}[a-z0-9]$", var.cluster_name))
error_message = "cluster_name must be 4-64 chars, start with a letter, and contain only lowercase letters, numbers, and hyphens."
}
}
variable "s3_bucket_name" {
type = string
validation {
condition = can(regex("^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$", var.s3_bucket_name))
error_message = "S3 bucket names must be 3-63 chars, start and end with a letter or number, and contain only lowercase letters, numbers, hyphens, and dots."
}
}
variable "ecr_image_tag" {
type = string
validation {
condition = can(regex("^(latest|v[0-9]+\\.[0-9]+\\.[0-9]+)$", var.ecr_image_tag))
error_message = "Image tag must be 'latest' or semantic version like 'v1.2.3'."
}
}

CIDR Block Validation

variable "vpc_cidr" {
type = string
validation {
condition = can(cidrnetmask(var.vpc_cidr))
error_message = "vpc_cidr must be a valid IPv4 CIDR block (e.g., '10.0.0.0/16')."
}
validation {
condition = (
can(regex("^10\\.", var.vpc_cidr)) ||
can(regex("^172\\.(1[6-9]|2[0-9]|3[0-1])\\.", var.vpc_cidr)) ||
can(regex("^192\\.168\\.", var.vpc_cidr))
)
error_message = "vpc_cidr must be a private IP range (10.x.x.x, 172.16-31.x.x, or 192.168.x.x)."
}
}

List Length and Content

variable "availability_zones" {
type = list(string)
validation {
condition = length(var.availability_zones) >= 2
error_message = "At least 2 availability zones are required for high availability."
}
validation {
condition = length(var.availability_zones) <= 3
error_message = "No more than 3 availability zones are supported."
}
validation {
condition = alltrue([
for az in var.availability_zones : can(regex("^[a-z]{2}-[a-z]+-[1-9][a-z]$", az))
])
error_message = "Each availability zone must be a valid AWS AZ name (e.g., 'us-east-1a')."
}
}

Validating Object Variables

variable "database_config" {
type = object({
instance_class = string
allocated_storage = number
backup_days = number
})
validation {
condition = contains([
"db.t3.micro", "db.t3.small", "db.t3.medium",
"db.r6g.large", "db.r6g.xlarge", "db.r6g.2xlarge"
], var.database_config.instance_class)
error_message = "database_config.instance_class must be an approved RDS instance class."
}
validation {
condition = var.database_config.allocated_storage >= 20 && var.database_config.allocated_storage <= 65536
error_message = "allocated_storage must be between 20 GB and 65,536 GB."
}
validation {
condition = var.database_config.backup_days >= 1 && var.database_config.backup_days <= 35
error_message = "backup_days must be between 1 and 35."
}
}

Cross-Variable Validation with Locals

Terraform validation blocks can only reference the variable being validated (var.variable_name). For cross-variable validation, use locals with preconditions:

# Lifecycle preconditions (Terraform 1.2+) — can reference any value
resource "aws_db_instance" "main" {
instance_class = var.database_config.instance_class
allocated_storage = var.database_config.allocated_storage
multi_az = var.enable_multi_az
lifecycle {
precondition {
condition = !(var.environment == "production" && !var.enable_multi_az)
error_message = "Production databases must have multi_az enabled."
}
precondition {
condition = !(var.environment == "production" && var.database_config.backup_days < 7)
error_message = "Production databases must have at least 7 days of backup retention."
}
}
}

can() Function for Safe Validation

can() returns true if an expression succeeds without an error, false if it errors. Essential for validating format-dependent functions:

# Without can() — if regex doesn't match, the condition itself errors (not returns false)
# condition = regex("^[a-z]", var.name) != null # BAD — errors on non-match
# With can() — gracefully handles cases where the expression errors
validation {
condition = can(regex("^[a-z]", var.name))
error_message = "Name must start with a lowercase letter."
}
# Validate URL format
variable "webhook_url" {
type = string
validation {
condition = can(regex("^https://", var.webhook_url))
error_message = "Webhook URL must start with https://."
}
}

Best Practices for Validation Messages

# Bad error message — tells user what failed, not what to do
error_message = "Invalid value."
# Good error message — specific, actionable, includes valid options
error_message = "environment must be 'dev', 'staging', or 'production'. Received: ${var.environment}"
# Also good — reference the docs or constraints clearly
error_message = "instance_type must use an approved class. Approved types: t3.micro, t3.small, t3.medium, t3.large."