Extract list of keys k from a map of objects where "map(k).someattrib < 10"

(Answered myself, in the first comment)

I’m learning HCL - I get the general idea of how it works, but I’m struggling with a conditional transform of a map of objects:

What I’d like to do is have the error message in the validation block below display a list of keys of the objects that failed the validation.

I get that what I think I want to be doing is have something that can transform a map of some objects, given a condition on the attributes of that object, and return me a list of keys from the map where that condition is true for each of those keys.

Then I can flatten that to a string for the error message.

OK - this example is not ground breakingly vital, but the aspect of the language is - I can see that understanding map transforms is going to be important - could anyone give me a hint please?

Many thanks for any pointers :smiley:

variable "volumes" {
    type = map(object({
        name        = string
        region      = optional(string, "uk-west")
        size_gb     = number
    }))

    default = {
        test_volume = {
            name    = "test_volume"
            size_gb = 5
        },
    }
    validation {
        condition = alltrue([
            for k, v in var.volumes: 
            v.size_gb >= 10
        ])
        error_message = "volumes.size_gb needs to be 10GB or larger"
    } 
}

d’oh! A short while after reading this, I stumbled across the operation I needed:

error_message = format( "Volumes validation failed, size_gb needs to be 10GB or larger. Failed volumes: [%s]", join ("][", [for k, v in var.volumes : k if v.size_gb < 10 ]))

But I will leave the post up in case there’s a better way…

Maybe you did it this way on purpose, but might also see if a set or list of objects might make more sense than a map of objects in this case. I think in either case (whether you do a map or a set or list), you could theoretically run into the issue of creating a duplicate object.

In any event, making some small tweaks, this seems to also work, though which is better is probably a judgement call:

variable "volumes" {
    type = list(object({
        name        = string
        region      = optional(string, "uk-west")
        size_gb     = number
    }))

    default = [
        {
            name    = "test_volume"
            size_gb = 5
        },
        {
            name    = "test2"
            size_gb = 6
        },
        {
            name    = "test3"
            size_gb = 15
        },
    ]
    validation {
        condition = alltrue([
            for item in var.volumes: 
            item.size_gb >= 10
        ])
        error_message = format("Volumes validation failed, size_gb needs to be 10GB or larger. Failed volumes: [%s]", join (",", [for item in var.volumes : item.name if item.size_gb < 10 ]))
    } 
}

You could also avoid having that 10 GB limit hard-coded in three places by using a local variable or variable for the size limit, and then do something like:

    validation {
        condition = alltrue([
            for item in var.volumes:
            item.size_gb >= local.size_limit
        ])
        error_message = format(
          "Volumes validation failed, size_gb needs to be %dGB or larger. Failed volumes: [%s]",
          local.size_limit,
          join (",", [for item in var.volumes : item.name if item.size_gb < local.size_limit ])
        ) 
    }
1 Like

Hi,

Thank you for your reply. Yes, I was toying with list vs map and initially a list seemed better - But I assumed I would also need the key to be able to reference this resource from another (“attach this volume to that compute instance”). That’s the next bit I am about to try…

I have indeed put the limit into a variable to as you suggested - and today, into a data module so it can live with all the other limits for the same provider. Been learning how modules work so I can keep this neat.

My underlying goal here is to learn TF, when I can afford to break stuff, then take my learning back to work and see if I can refactor a rather larger TF setup I inherited…

I’ll happy admit I don’t get on with declarative languages half as well as procedural ones, so this stuff is always a challenge at first.

Always appreciate the suggestions and ideas :slight_smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.