HLT
HLT

Reputation: 607

The given value is not suitable for child module

I am trying to set default values on my map(objects) variable in terraform but I am getting the following error when using my module:

The given value is not suitable for child module variable "virtual_servers"
│ defined at .terraform/modules/{module_name}/variables.tf:122,1-27: element
│ "{element_name}": attributes "client_profiles", "irules", "profiles",
│ "rev_zone", "server_profiles", and "source_address_translation" are
│ required.

My understanding is that I should be able to provide sensible defaults in my module and then override those defaults when calling the module.

Here is the code:

Module

variables.tf:

variable "virtual_servers" {
  type = map(object({
    port                       = number
    fwd_zone                   = string
    rev_zone                   = string
    source_address_translation = string
    irules                     = list(string)
    profiles                   = list(string)
    client_profiles            = list(string)
    server_profiles            = list(string)
  }))
  default = {
    default = {
      port                       = 1
      fwd_zone                   = "test"
      rev_zone                   = "test"
      source_address_translation = "test"
      irules                     = null
      profiles                   = null
      client_profiles            = null
      server_profiles            = null
    }
  }
}

main.tf

resource "virtual_server" "this" {
  for_each                   = var.virtual_servers
  port                       = each.value.port
  source_address_translation = each.value.source_address_translation
  irules                     = each.value.irules

  profiles        = each.value.profiles
  client_profiles = each.value.client_profiles
  server_profiles = each.value.server_profiles
}

Making use of the module

main.tf

module "module_name" {
source = "module_source"

  virtual_servers_1 = {
    "name" = {
      port                       = 80
      fwd_zone                   = "override"
    }
    "virtual_servers_2" = {
      port                       = 80
      fwd_zone                   = "override"
    }
  }
}

Can anyone point me in the direction of where I am going wrong?

EDIT: If I pass in all the values required to virtual_servers_1 and virtual_servers_2 it works. However I want to be able to supply default values.

Upvotes: 3

Views: 14623

Answers (1)

Martin Atkins
Martin Atkins

Reputation: 74694

The default argument inside a variable block is only for situations where the caller doesn't set the variable at all. Setting a default tells Terraform that the overall argument should be optional and to use the given value instead if it isn't set.

There is no corresponding feature for individual attributes. If you define an attribute as part of an object type then it must always be present in the argument in order for the value to match the type, but the caller can set the attribute to null explicitly in order to include it in the type without providing a value for it:

module "module_name" {
  source = "module_source"

  virtual_servers_1 = {
    "name" = {
      port                       = 80
      fwd_zone                   = "override"
      source_address_translation = null
      irules                     = null
      profiles                   = null
      client_profiles            = null
      server_profiles            = null
    }
    "virtual_servers_2" = {
      port                       = 80
      fwd_zone                   = "override"
      source_address_translation = null
      irules                     = null
      profiles                   = null
      client_profiles            = null
      server_profiles            = null
    }
  }
}

Note that, because as noted above default is only for when the variable isn't set at all, those null values will appear directly in the var.virtual_servers value. If you want to replace them with actual default values for use elsewhere in your module then you can derive a new value where those values are replaced by defaults, using the coalesce function in a local value:

locals {
  virtual_servers_1 = tomap({
    for k, s in var.virtual_servers_1 : k => {
      port                       = coalesce(s.port, 1)
      fwd_zone                   = coalesce(s.fwd_zone, "test")
      rev_zone                   = coalesce(s.rev_zone, "test")
      source_address_translation = coalesce(s.source_address_translation, "test")
      irules                     = coalesce(s.irules, tolist([]))
      profiles                   = coalesce(s.profiles, tolist([]))
      client_profiles            = coalesce(s.client_profiles, tolist([]))
      server_profiles            = coalesce(s.server_profiles, tolist([]))
    }
  })
}

You can then use local.virtual_servers_1 instead of var.virtual_servers_1 elsewhere in the module to guarantee that you'll never encounter a null, and will instead see the default value if the input was null. (I set the list ones to default to an empty list because that's typically easier to work with elsewhere than a null list.)

Upvotes: 2

Related Questions