Notes on Terraform

How to use flatten to iterate into a nested list

To simplify the configuration you could want a nested who is a map of list. This can be useful to associate, for example, a CNAME to multiple domains:

variable "cnames" {
    description = "The CNAMEs associated with the domain"
    type = map(list(string))
    default = {
        "toto" = [
            "*",
            "dolibarr",
            "grafana",
            "influxdb",
            "iot",

        ],
        "domotic" = [
            "bitwarden",
            "emby",
        ]
    }
  
}

Using this structure, we can create associations like:

  • toto -> dolibarr
  • toto -> grafana
  • etc…

To do this, we need a local variable, which can be declared like this:

# Flatten the cname to allow processing in a for each
locals {
  cnames = flatten([
    for target, cname_targets in var.cnames : [
      for orign in cname_targets : {
        origin = orign
        target = target
      }
    ]
    ]

  )
}

This can be translated into python as:

cnames = []
for target, cname_targets in var.cnames:
    for origin in cname_targets:
        cnames.append({
            "origin": origin,
            "target": target,
        })

So we transformed this:

{
    "toto" = [
        "*",
        "dolibarr",
        "grafana",
        "influxdb",
        "iot",

    ],
    "domotic" = [
        "bitwarden",
        "emby",
    ]
}

to this:

[
    {"origin": "toto", "target": "*"},
    {"origin": "toto", "target": "dolibarr"},
    {"origin": "toto", "target": "grafana"},
    {"origin": "toto", "target": "influxdb"},
    {"origin": "toto", "target": "iot"},
    {"origin": "domotic", "target": "bitwarden"},
    {"origin": "domotic", "target": "emby"},
]

This will allow us to use a for each in the resource:

resource "ovh_domain_zone_record" "cnames" {
  for_each  = { for cname_assoc in local.cnames : "${cname_assoc.origin}-${cname_assoc.target}" => cname_assoc }
  zone      = "moospirit.org"
  subdomain = each.value.origin
  fieldtype = "CNAME"
  ttl       = var.dns-ttl
  target    = each.value.target
}

The magic from this snippet:

{ for cname_assoc in local.cnames : "${cname_assoc.origin}-${cname_assoc.target}" => cname_assoc }

This instruction is to iterate in the local.cnames list we have created earlier, and create a key-value pair in the for, with this form:

{"toto-dolibarr": {"origin": "toto", "target": "dolibarr"} }

We can create the key from the content of the map that has been flattened. Which in turn allow us to reference variable from the map with:

each.value.orign

To import, we need to use the bracket definition, ex:

terraform import ovh_domain_zone_record.cnames["toto-dolibar"] 126864.ovh.com