Validate list(object) variables

Hi i am trying to add validations for list of an object type:

variable "rules" {
  type = list(object({
    name = string
    access = string
  }))

  validation {
    condition = contains(["Allow", "Deny"], var.rules.access)
    error_message = "Invalid access, can be either Allow or Deny."
  }
}

I understand this won’t work, 'am trying to see if there is any way I can validate the object.

Thank you !!

Hi @ramsateesh,

I assume what you want to achieve here is to apply that condition to each of the objects in the list. In which case, the main building block of the answer is to use for expressions to evaluate the condition once for each element of the list.

In Terraform v0.13 you can write this as a for expression with an if clause where you test the length of the result to see if the condition was valid:

  validation {
    condition = length([
      for o in var.rules : true
      if contains(["Allow", "Deny"], o.access)
    ]) == length(var.rules)
    error_message = "All rules must have access of either Allow or Deny."
  }

The above works by filtering the input to only include the items that are valid and then checking whether the resulting list still has the same amount of elements. It would have fewer elements if any of the items were invalid.

The forthcoming Terraform 0.14.0 (expected in the next week or so) will include a new function alltrue which aims to simplify the above pattern by allowing you to rewrite it as a for expression whose result is a list of boolean values that must all be true for the condition to hold:

  validation {
    condition = alltrue([
      for o in var.rules : contains(["Allow", "Deny"], o.access)
    ])
    error_message = "All rules must have access of either Allow or Deny."
  }

Once you’re able to use Terraform v0.14 I would recommend adopting this second pattern because I think (subjectively) it’d be easier for a future maintainer to read and understand what it means and how it works.

12 Likes

Perfect, thanks a ton for the examples :+1:

1 Like

This worked perfectly for me in a different context. Thanks!

Now that 1.0 is out, it would be great to add this to the documentation along with other validation info.

Thanks a lot @apparentlymart
This helped me save a lot of time in figuring out how to use such validations. I believe it should be added to the official documentation.

Hi everyone,

I’m trying to set up a validation based on your suggestion and it doesn’t seem to work for list(string) vars. I’m getting the below validation error. It works perfectly fine when I comment the validation paragraph out.

variable “project” {
type = map(object({
mongodb_project_name = string
role_names = optional(list(string))
}))
validation {
condition = alltrue([ for x in var.project : contains([“GROUP_OWNER”], x.role_names)])
error_message = “The role name must be one of the following values: ["GROUP_OWNER", "GROUP_READ_ONLY".”
}

Error: Invalid value for variable

│ on variables.tf line 9:
│ 9: variable “project” {
│ ├────────────────
│ │ var.project is map of object with 1 element

│ The role name must be one of the following values: [“GROUP_OWNER”].

input:

project = {
project1 = {
mongodb_project_name = “mongo-db”
role_names = [ “GROUP_OWNER”]
}}

Hey Hi,

You are on the right track, the only thing that is missing here is that you are passing in a list to the second argument of contains function, this expects a value. You can change your validation function to below and it will work:

variable “project” {
type = map(object({
mongodb_project_name = string
role_names = optional(list(string))
}))
validation {
condition = alltrue( flatten( [ for x in var.project : [for role in x.role_names : contains([“GROUP_OWNER”], role)] ] ) )
error_message = “The role name must be one of the following values: ["GROUP_OWNER", "GROUP_READ_ONLY".”
}
2 Likes

Unfortunately, I’m still getting an error.

│ Error: Unsupported attribute

│ on variables.tf line 20, in variable “projects”:
│ 20: condition = alltrue( flatten( [ for x in var.projects : [for role in x.role_names : contains([ “GROUP_OWNER”, “GROUP_READ_ONLY”, “GROUP_DATA_ACCESS_ADMIN”, “GROUP_DATA_ACCESS_READ_ONLY”, “GROUP_CLUSTER_MANAGER” ], role)] ] ) )

│ This object does not have an attribute named “role_names”.

Here is my variables.tf

variable “projects” {
type = list(object({
mongodb_project_name = string
org_id = string
project_owner_id = optional(string)
teams = list(object({
team_id = optional(string)
role_names = optional(list(string))
}))
}))

so the role_names are an attribute of teams as opposed to projects which is causing an issue.

Hey did you find an answer for this ??

The answer is pretty much in the same lines. You have to iterate teams now and the code should work. All The Best.

Hey,

thanks this helped me with a similar issue.

One question: Why does the first snippet include true if before the contains? Is that a Terraform <0.13 constraint? Doesn’t contains(["Allow", "Deny"], o.access) output true anyway like in example snippet two?

My question is not related to the alltrue or == length handling. It’s more of a both versions work but I don’t understand why true if was used and I think it can be ommited.

BR

1 Like