Terraform: Allow reference to locals in (experimental) variable validation

Created on 22 Feb 2020  路  2Comments  路  Source: hashicorp/terraform

Current Terraform Version

Terraform v0.12.20

Use-cases


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."
  }

Attempted Solutions


See above. I have worked round this, but the logic becomes a bit unwieldy.

Proposal

References

enhancement variable-validation

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):

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.

All 2 comments

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."
  }
}
Was this page helpful?
0 / 5 - 0 ratings