tl;dr: I want to use the current value of a runtime variable created in the condition
part of a variable validation
, to give more details about the error.
I am looking for a way to improve my error_message
for variable validation in my Terraform project.
Say I have 2 variable:
availabilityzones
gives the list of AZ my module/project can use.workloads
defines some workload to start for this module/project can use.Workloads have an AMI (not relevant here) and are given a number of AZ they can use (span): az_count
.
My workloads
variable has a validation to make sure the number of AZ is consistent with the size of availabilityzones
:
condition = alltrue([
for workload_name, workload in var.workloads :
1 <= workload.az_count && workload.az_count <= length(var.availabilityzones)
])
Validation works and can detect too large az_count
, but I would like to tell the user which workload caused the issue, so I tried to use the workload_name
variable (the one from the condition
) in the error_message
:
error_message = "For a workload, the AZ count must be between `1` and the number of configured AZ (both inclusive). Workload \"${workload_name}\" has: ${tonumber(workload.az_count)}"
But it fails:
│ Error: Invalid reference
│
│ on …/variables.tf line 42, in variable "workloads":
│ 129: error_message = "For a workload, the AZ count must be between `1` and the number of configured AZ (both inclusive). Workload \"${workload_name}\" has: ${tonumber(workload.az_count)}"
│
│ A reference to a resource type must be followed by at least one attribute access, specifying the resource name.
I am guessing the context from condition
is lost once validation has detected an error, and it not passed to error_message
's context?
Is there a workaround?
One I can think of would be repeating the test in error_message
to re-find the invalid value, but this would always find/display the first invalid value, even if multiple workloads are incorrect.
Full code below:
variable "availabilityzones" {
description = "The availability zones (AZs) to use."
default = [
"eu-west-1a",
"eu-west-1b",
"eu-west-1c",
]
type = set(string)
}
variable "workloads" {
default = {
workload_foo = {
az_count = 1
ami = "ami-061ff9349538cd3f1"
}
workload_bar = {
az_count = 2
ami = "ami-0e309138dd3e25b2e"
}
workload_bad = {
az_count = 4 # ⟽ Too many AZ here
ami = "ami-040a86d5d7bf29267"
}
}
type = map(object({
# How many AZ to use?
az_count = number
# Which AMI should the workload use?
ami = string
}))
validation {
condition = alltrue([
for workload_name, workload in var.workloads :
1 <= workload.az_count && workload.az_count <= length(var.availabilityzones)
])
error_message = "For a workload, the AZ count must be between `1` and the number of configured AZ (both inclusive). Workload \"${workload_name}\" has: ${tonumber(workload.az_count)}"
}
}
I don't think you'll be able to use a validation block for this given that within them you can only refer to the variable context.
You can however achieve the desired behavior with a check block:
variable "availabilityzones" {
description = "The availability zones (AZs) to use."
default = [
"eu-west-1a",
"eu-west-1b",
"eu-west-1c",
]
type = set(string)
}
variable "workloads" {
default = {
workload_foo = {
az_count = 1
ami = "ami-061ff9349538cd3f1"
}
workload_bar = {
az_count = 2
ami = "ami-0e309138dd3e25b2e"
}
workload_bad = {
az_count = 4 # ⟽ Too many AZ here
ami = "ami-040a86d5d7bf29267"
}
}
type = map(object({
# How many AZ to use?
az_count = number
# Which AMI should the workload use?
ami = string
}))
}
locals {
workflow_check = {
for workload_name, workload in var.workloads :
workload_name => [1 <= workload.az_count && workload.az_count <= length(var.availabilityzones), workload.az_count]
}
invalid_workflow = {for k,v in local.workflow_check : k => tostring(v[1]) if v[0] == false}
}
check "validate_variables" {
assert {
condition = local.invalid_workflow == {}
error_message = local.invalid_workflow == {} ? "" : "For a workload, the AZ count must be between `1` and the number of configured AZ (both inclusive). Workload \"${keys(local.invalid_workflow)[0]}\" has: ${tonumber(values(local.invalid_workflow)[0])}"
}
}
$ terraform plan
╷
│ Warning: Check block assertion failed
│
│ on main.tf line 49, in check "validate_variables":
│ 49: condition = local.invalid_workflow == {}
│ ├────────────────
│ │ local.invalid_workflow is object with 1 attribute "workload_bad"
│
│ For a workload, the AZ count must be between `1` and the number of configured AZ (both inclusive). Workload "workload_bad" has: 4
╵
you can adapt this so that in the case that there are multiple workloads that fail the check the error message includes them all.