I would like to know if it is possible to merge two map of maps without replacing the main map object.
My map object is defined as follows:
variable "apps" {
type = map(object({
is_enabled = bool
cost_center = string
}))
default = {}
}
locals {
default_apps = {
"api-1" = {
is_enabled = false
cost_center = "1234"
},
"api-2" = {
is_enabled = false
cost_center = "1235"
},
}
apps = merge(
local.default_apps,
var.apps
)
}
I would like to change the value of api-1[‘s_enabled’] to true.
If define my tfars as follows:
apps = {
"api-1" = {
is_enabled = true
}
}
I get the following error:
Error: Invalid value for input variable
The environment variable TF_VAR_apps does not contain a valid value for
variable "apps": element "api-1": attribute "cost_center" is required.
It works if I define my tfvars like so:
apps = {
"api-1" = {
is_enabled = true
cost_center = "1234"
}
}
My goal is to override a single value of one of the pre defined local variables under default_apps
(e.x is_enabled) in tfvars.
Hi @varig203,
It seems like your intent here is to merge each of the elements of var.apps
and of local.default_apps
separately, so that var.apps
can optionally override parts of the objects in local.default_apps
. It may work out simpler to instead define a variable apps_enabled
which is a map(bool)
that is merged specifically into the is_enabled
attribute, but I’ll show an example that I think will support the generality you were hoping for here, at the expense of some increased complexity:
locals {
apps = tomap({
for k in setunion(keys(local.default_apps), keys(var.apps)) : k => {
is_enabled = tobool(coalesce(
try(var.apps[k].is_enabled, null),
try(local.default_apps[k].is_enabled, null),
))
cost_center = tostring(coalesce(
try(var.apps[k].cost_center, null),
try(local.default_apps[k].cost_center, null),
))
}
})
}
This is using several different Terraform language features together:
-
for
expressions to create a new mapping with an element for each element of some other collection.
-
keys
to get all of the keys defined in a map.
-
setunion
to merge the keys from both of your maps together into a single set of keys.
-
coalesce
to select the first non-null value from a series of values.
-
try
to concisely handle the situation where a particular key isn’t defined in var.apps
or local.default_apps
, because it’s allowed to have elements defined in one map that are not also present in the other map.
-
tomap
, tobool
, and tostring
to ensure that the result always has the expected type and to generate an error if a future maintainer populates local.default_apps
incorrectly.
Note that in order to comply with the type constraint you’ll still need to write out in the variable definition all of the expected attributes, but you can set the ones you don’t intend to override to null
so that the coalesce
call will ignore them:
apps = {
"api-1" = {
is_enabled = true
cost_center = null
}
}
Thank you @apparentlymart this works for me.