AndyHunt.me

CV and personal site of Andy Hunt

You can fully validate a complex input object in Terraform

Posted: Thursday 04 March 2021 @ 12:12:55

Terraform allows you to define your inputs as arbitrarily complex objects and lists of objects. For example, consider the following:

variable "instances" {
    type = list(object({
        name = string
        count = number
        tags = map
    }))
}

It describes an input which is a list of objects, which have name, count, and tags fields. You probably have some validation you'd like to apply to each of those objects, for example:

  • name must not be empty
  • count must be a multiple of 3
  • tags must contain an business_unit tag

Terraform allows you to perform those validations using a validation block in the variable's definition.

The Terraform documentation shows a reasonably trivial example of validating a string, but it's also possible to validate lists, and lists of objects. Enter the alltrue function.

The alltrue function takes an array, and asserts that every entry is the value true. If you combine this with a Terraform for expression, you can produce an input to alltrue which is the result of applying a validation function to each element in the list.

You can start by writing a basic validation routine (for the above rules) for a single object. Assuming it's called instance, it might look like:

( length(instance.name) > 0
&& (instance.count % 3) == 0
&& lookup(instance.tags, "business_unit", null) != null
)

You can then combine this with a for expression to apply it to each object in a list, so that you're left with a list of boolean values:

[for instance in var.instances : (
    length(instance.name) > 0
    && (instance.count % 3) == 0
    && lookup(instance.tags, "business_unit", null) != null
)]

Finally, you can wrap this in the alltrue function to assert that every validation passed:

variable "instances" {
    type = list(object({
        name = string
        count = number
        tags = map
    }))

    validation {
        condition = (
            alltrue([for instance in var.instances : (
                length(instance.name) > 0
                && (instance.count % 3) == 0
                && lookup(instance.tags, "business_unit", null) != null
            )])
        )
        error_message = "Validation of an object failed"
    }
}

Unfortunately, Terraform doesn't support multiple validation blocks for a single variable, so it isn't possible to provide a more useful error message.