Terraform require only specific values in object

Consider the following Example:

variable "myvar" {
  type = object({
    key1 = string
    key2 = string
    key3 = string
  })
}

Is it possible to define an object which requires the structure defined above, but additionally allows to specify arbitrary keys for input? I don’t want to use optional() for the values here, since the additional keys are not known. All specified keys should then be applied using merge(var.myvar) on a resource.

Hi @Rai,

What you want to do here is not possible in the Terraform language, because it mixes ideas from two different kinds of type:

  • Object types have a fixed set of attributes decided by the module author. Each attribute may have a different type.
  • Map types represent any number of values of the same type that are each identified by arbitrary keys. The keys are therefore decided by the caller of your module, rather than by you.

If you just want an arbitrary set of key/value pairs then map(string) would be the most appropriate type constraint. You could optionally combine that with a custom validation rule which requires particular keys to be present for the argument to be valid:

variable "myvar" {
  type = map(string)

  validation {
    condition = contains(keys(var.myvar), "key1")
    error_message = "The key \"key1\" is required."
  }
  validation {
    condition = contains(keys(var.myvar), "key2")
    error_message = "The key \"key2\" is required."
  }
  validation {
    condition = contains(keys(var.myvar), "key3")
    error_message = "The key \"key3\" is required."
  }
}

The above will allow arbitrary map keys during type conversion but will then check afterwards for the three required keys and raise an error if they aren’t set.

Amazing solution @apparentlymart
Appreciate your help!

Having run into this, I have a use-case which seems not well supported by Terraform.

I need to have an input which can take both lists and strings, however I’m unable to define it as an object because some of the keys have dots in the name.

Here’s a simplified example. In Azure, we have a postgres server which needs to have some postgres settings changed via the azurerm_postgresql_flexible_server_configuration resource. Some of the settings we need to adjust are defined by Azure with dots in what ends up being the key’s name in the input variable. Because it has dots in the key name, I can not specify the key I need to use, as a key in the object constructor, thus I HAVE to use a map(any) type.

When the input has just a single key, or all keys of the same type, setting the variable type to map(any) works fine for us, but as soon as we add a key with a different type, Terraform throws an error that all keys must have the same type.

Is there no way to handle this?

For what it’s worth, the settings in question are all strings in Azure’s API, we convert the list inputs to strings via a local before sending it off to Azure.

This is done to allow us to sanitize the input and enforce certain values get appended to the lists no matter what; so that security settings can be enforced. We take the inputs as lists and then convert the lists to comma separated strings as required by Azure, so everything works but Terraform has 2 issues described above with keys of differing types and objects not accepting keys with dots in the key name.

@tspearconquest,

map(any) is a type constraint, and does not mean a map which can contain any type, rather it means a map which contains a single type which will be determined later. You generally don’t want to use map(any) since it’s usually possible to know the type of a simple map, so if the input type is not known you would need the more general any type constraint.

While Terraform does not allow specifying a type constraint with invalid attribute names, the underlying type system can accommodate that case more dynamically (which is often used when dealing with unstructured data, such as json). If you accept any type as the parameter, the keys can be any valid string, and can then be accessed via index expressions.

1 Like