Terraform v0.12.20
Simplification of mildly complex validation.
I have a variable defining subnets to be built.
Here, a subnet's nat_route should only refer to a valid subnet with "public = true" and "nat = true"
In this example subnet02 is invalid since jump02 has no nat gateway set.
variable "subnets" {
type = map(map(string))
default = {
jump01 = {
cidr_block = "10.0.1.0/24"
public = true
nat = true
},
jump02 = {
cidr_block = "10.0.2.0/24"
public = true
nat = false
},
subnet01 = {
cidr_block = "10.0.101.0/24"
nat_route = "jump01"
},
subnet02 = {
cidr_block = "10.0.102.0/24"
nat_route = "jump02"
}
}
validation {
condition = length(
[
for item in compact([
for key, value in var.subnets :
try(value["nat_route"], "")
]) :
item
if ! contains(compact([
for key, value in var.subnets :
try(value["public"] == "true" && value["nat"] == "true" ? key : "", "")
]), item)
]
) == 0
error_message = "A nat route does not refer to a public subnet with a NAT gateway."
}
}
Instead, it would be nice to be able to break down the logic with locals. Even if the locals refer only to the variable being validated (which is the case here)
locals {
nat_route_list = compact([
for key, value in var.subnets :
try(value["nat_route"], "")
])
nat_subnet_list = compact([
for key, value in var.subnets :
try(value["public"] == "true" && value["nat"] == "true" ? key : "", "")
])
invalid_nat_routes = [
for item in local.nat_route_list :
item
if ! contains(local.nat_subnet_list, item)
]
}
Then use :
validation {
condition = length(local.invalid_nat_routes) == 0
error_message = "A nat route does not refer to a public subnet with a NAT gateway."
}
See above. I have worked round this, but the logic becomes a bit unwieldy.
Could we actually expand this to also allow referencing locals within a variable block in other places? For example, I'd like to be able to do the following (in practice with more complex descriptions of options):
locals {
env_options = {
dev = "Development"
stg = "Staging"
prd = "Production"
}
}
variable "env" {
description = "Environment, one of ${local.env_options}"
type = string
validation {
condition = (
length(var.env) == 3 &&
contains(keys(local.env_options), var.env)
)
error_message = "Invalid env value, options are: ${local.env_options}"
}
}
I think that being able to have these descriptions of the options included in the variable description and validation error message like this would be a huge usability improvement and make the validation code a lot DRYer since in order to provide the same functionality under the current system, I would need to create the list of options 3 separate times instead of just once as a local.
I have typically the same case with AZs and no workaround possible :/
variable "azs" {
type = list(string)
validation {
condition = try(index(local.valid_azs, var.azs) >= 0, false)
error_message = "AZs list should be a list of AZ letter (ex: [a,b,c])."
}
}
locals {
valid_azs = ["a","b","c","d","e","f"]
}
To complete this issue, a "cross-variables" interpolation should be great also.
variable "private_subnets" {
type = list(string)
validation {
condition = length(var.private_subnets) <= length(var.azs)
error_message = "Subnets number should not exced azs defined numbers. You can add a duplicated AZ if you need more subnets in AZ."
}
}
Most helpful comment
Could we actually expand this to also allow referencing locals within a variable block in other places? For example, I'd like to be able to do the following (in practice with more complex descriptions of options):
I think that being able to have these descriptions of the options included in the variable description and validation error message like this would be a huge usability improvement and make the validation code a lot DRYer since in order to provide the same functionality under the current system, I would need to create the list of options 3 separate times instead of just once as a local.