Pattern to handle optional dynamic blocks

Hi @sveniu!

The for_each argument inside a dynamic block is expecting a collection to use as the basis for repetition. In your case it looks like each.value.alias is a single object rather than a list, and so what you need for for_each is either a single-item list when the object is set or a zero-length list when it is null.

Fortunately, that particular situation is a special power of the [*] splat operator when used on something that isn’t a list:

  dynamic "alias" {
    for_each = each.value.alias[*]
    content {
      name                   = each.value.name
      zone_id                = each.value.zone_id
      evaluate_target_health = each.value.evaluate_target_health
    }
  }

each.value.alias[*] will return [each.value.alias] if each.value.alias is non-null, or [] if it is null.

The other tricky part in this case is that we need to ensure that the records you are iterating over always have the same object type, which means that either records or alias will need to be present and null rather than absent. We can do that normalization with a for expression to transform the result of loading the YAML:

locals {
  zone_data_raw = yamldecode(file("${path.module}/zone_data.yaml"))
  zone_data = {
    zone_id = zone_data_raw.zone_id
    records = [for r in zone_data_raw.records : {
      name    = r.name
      type    = r.type
      ttl     = lookup(r, "ttl", null)
      alias   = lookup(r, "alias", null)
      records = lookup(r, "records", null)
    }]
  }
}

The local.zone_data value should conform to the type constraint you originally wrote for variable "zone_data", and sets alias up in a way that the above technique of using [*] will work.

When working with data loaded from external files (in JSON or YAML format) it’s often a good idea to do something like the above just to ensure that the data in the file conforms to the expected shape and normalize where necessary. This helps ensure that any errors dealing with the file can get reported close to where the file is loaded, rather than downstream when the value is used, and that can be helpful for future maintainers that might not get the YAML structure right on the first try.

5 Likes