Dynamic nested resources (not blocks)

Terraform 0.12.6

Can I use two-level (or more) nesting when creating resources?

I’m trying to create AWS Route 53 DNS records for many zones in one go. It would probably be better if my module handled a single zone, but then I can’t yet use for_each to instantiate multiple modules, ref terraform issue 17519.

So I’m passing in a list like this:

type = list(object({
  zone_name = string
  records = list(object({
    name    = string
    type    = string
    ttl     = number
    records = list(string)
  }))
}))

Now, making the aws_route53_zone resources is easy enough, but then I want to create the records, like “for each zone: for each zone.records: create aws_route53_record”:

resource "aws_route53_record" "record" {
  for_each = { for z in var.zone_data : for v in z.records : z.zone_name => v } # "Invalid for expression"
  ...

Is there an alternative syntax that might work in this case?

Hi @sveniu!

Resource instances are a flat namespace of keys within each resource, so in order to do this you’ll need to first produce a flatter structure where each element corresponds to all of the settings for one record instance.

For example:

locals {
  flat_records = flatten([
    for z in var.zone_data : [
      for r in z.records : {
        zone_name = z.zone_name
        record = r
      }
    ]
  ])
}

resource "aws_route53_zone" "zone" {
  for_each = {
    for z in var.zone_data : z.zone_name => z
  }

  name = each.key
}

resource "aws_route53_record" "record" {
  for_each = {
    # We need a compound key for these to make the keys unique
    for r in local.flat_records : "${r.record.name}.${r.zone_name} ${r.record.type}" => r
  }

  zone_id = aws_route53_zone.zone[each.value.zone_name].id

  name    = each.value.record.name
  type    = each.value.record.type
  ttl     = each.value.record.ttl
  records = each.value.record.records
}

This would lead to zone instances like aws_route53_zone.zone["example.com"] and record instances like aws_route53_record.record["foo.example.com CNAME"], assuming I guessed correctly that your zone_name attribute contains the DNS name of the zone to be created.

1 Like