Why Every Terraform Module Needs Proper Validation
Source: Dev.to
If you’ve ever deployed a Terraform module only to discover that someone passed a private subnet ID where a public one was expected, you know the pain. The deployment succeeds, but nothing works. You spend minutes debugging, only to realize the input was wrong from the start.
Terraform provides built‑in validation features, but most people don’t use them.
Problem Statement
A naïve NAT Gateway module might look like this:
variable "subnet_id" {
description = "Subnet to place the NAT Gateway in"
type = string
}
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet_id = var.subnet_id
}
- This accepts any subnet ID—public, private, or even a typo.
- Terraform and AWS won’t complain immediately.
- The result: private subnets without internet access and a lot of wasted time.
Adding Variable Validations
Since Terraform 1.0 you can add validation blocks to variables:
variable "public_subnet_ids" {
description = "Public subnet IDs for NAT Gateway placement"
type = list(string)
validation {
condition = length(var.public_subnet_ids) > 0
error_message = "At least one public subnet ID is required."
}
validation {
condition = alltrue([for id in var.public_subnet_ids : startswith(id, "subnet-")])
error_message = "All values must be valid subnet IDs (starting with 'subnet-')."
}
}
Now terraform plan fails immediately with a clear message if someone passes an empty list or malformed IDs.
Using Preconditions in Resources
When you need to validate relationships between variables, use a precondition block inside a resource’s lifecycle:
resource "aws_nat_gateway" "this" {
count = var.single_nat_gateway ? 1 : length(var.public_subnet_ids)
allocation_id = aws_eip.this[count.index].id
subnet_id = var.public_subnet_ids[count.index]
lifecycle {
precondition {
condition = var.single_nat_gateway || length(var.public_subnet_ids) >= length(var.private_route_table_ids)
error_message = "When using multi‑AZ NAT, you need at least as many public subnets as private route tables."
}
}
}
This catches architectural mistakes at plan time, not after a lengthy apply.
Validation Checklist
| What | Why |
|---|---|
| Non‑empty required lists | Prevents silent no‑ops |
ID format (subnet-, vpc-, sg-) | Catches copy‑paste errors |
| CIDR block format | Regex validation on network inputs |
| Mutually exclusive flags | e.g., single_nat_gateway vs per‑AZ mode |
| Cross‑variable consistency | Preconditions on resource blocks |
Every validation you add reduces support tickets, Slack “why isn’t this working?” messages, and hours lost to debugging obvious misconfigurations.
Conclusion
These validations run during terraform plan—zero cost, zero risk, and faster feedback. If you’re building Terraform modules for AWS, consider checking out the HAIT module collection on the Terraform Registry.