Hizzy
Hizzy

Reputation: 879

Terraform - How To Flatten A List With In a List Of Objects

I am trying to set up my DNS zones and records with in those zones and I can't get the structured collection I am looking for to pass into the tf file that will generate all the zones and records. I have a list of DNS objects and each object will have multiple lists of record objects. I am trying to create lists of just records (a, cname, txt, ...). Im trying merges and flattening and I just not sure how to get what I need. I keep trying different patterns with merge and flatten but keep getting errors in different configurations.

Errors:

Error: Invalid 'for' expression. Extra characters after the end of the 'for' expression.
Error: Missing attribute value. Expected an attribute value, introduced by an equals sign ("=").
Error: Invalid 'for' expression. Key expression is required when building an object.
Error: Missing key/value separator. Expected an equals sign ("=") to mark the beginning of the attribute value.

Variable Definition:

variable "dns_target_child_zones" {
  type = list(object({
    name      = string
    a_records = list(object({
      name        = string
      ttl         = number
      ip_address  = string
    }))
    cname_records = list(object({
      name        = string
      ttl         = number
      record      = string
    }))
    mx_records = list(object({
      name        = string
      ttl         = number
      record      = list(object({
        preference  = number
        exchange    = string
      }))
    }))
    txt_records = list(object({
      name        = string
      ttl         = number
      record      = list(object({
        value  = string
      }))
    }))
  }))
}

Locals file:

locals {
  a_records = flatten ([

  ])
  cname_records = flatten ([

  ])
  mx_records = flatten ([

  ])
  txt_records = flatten ([

  ])        
}

The output structure I am trying to produce so I can do something like count = length(locals.a_records)

[
 {
   name                = 
   zone_name           = 
   ttl                 = 
   records             = 
 },
 {
   name                = 
   zone_name           = 
   ttl                 = 
   records             = 
 }
 ...
]

Upvotes: 3

Views: 3825

Answers (1)

alwalms
alwalms

Reputation: 198

Here's an example of the local object you could create for a_records (note: I included the ip_address attribute but you have records which doesn't appear in the a_records variable object):

  a_records = flatten([
    for v in var.dns_target_child_zones : [
      for record in v.a_records : {
        name       = record.name
        zone_name  = v.name
        ttl        = record.ttl
        ip_address = record.ip_address
      }
    ]
  ])

This will product an output like the following:

  + a_records = [
  + {
      + ip_address = "ip1"
      + name       = "a1"
      + ttl        = 10
      + zone_name  = "dns1"
    },
  + {
      + ip_address = "ip2"
      + name       = "a2"
      + ttl        = 10
      + zone_name  = "dns1"
    },
  ]

I used a default value and an output to test the expected result. Here's my full test, which you can run terraform plan for to show the output

variable "dns_target_child_zones" {
  type = list(object({
    name = string
    a_records = list(object({
      name       = string
      ttl        = number
      ip_address = string
    }))
    cname_records = list(object({
      name   = string
      ttl    = number
      record = string
    }))
    mx_records = list(object({
      name = string
      ttl  = number
      record = list(object({
        preference = number
        exchange   = string
      }))
    }))
    txt_records = list(object({
      name = string
      ttl  = number
      record = list(object({
        value = string
      }))
    }))
  }))
  default = [{
    name = "dns1",
    a_records = [{
      name       = "a1",
      ttl        = 10
      ip_address = "ip1"
      },
      {
        name       = "a2",
        ttl        = 10
        ip_address = "ip2"
      }
    ],
    cname_records = [{
      name   = "cname1",
      ttl    = 10
      record = "rec1"
    }],
    mx_records = [{
      name = "mx1",
      ttl  = 10,
      record = [{
        preference = 1,
        exchange   = "exchange1"
        }, {
        preference = 2,
        exchange   = "exchange2"
      }]
    }],
    txt_records = [{
      name = "txt1",
      ttl  = 10,
      record = [{
        value = "val1"
      }]
    }]
  }]
}


locals {
    a_records = flatten([
        for v in var.dns_target_child_zones: [
            for record in v.a_records: {
                name       = record.name
                zone_name  = v.name
                ttl        = record.ttl
                ip_address = record.ip_address
            }
        ]
    ])
}

output "a_records" {
    value = local.a_records
}

For further nesting extraction e.g with mx_records then you can add another for loop in your local like the below

  mx_records = flatten([
    for v in var.dns_target_child_zones : [
      for mx in v.mx_records : [
        for r in mx.record : {
          name       = mx.name
          zone_name  = v.name
          ttl        = mx.ttl
          preference = r.preference
          exchange   = r.exchange
        }
      ]
    ]
  ])

The above local outputs this result

  + mx_records = [
      + {
          + exchange   = "exchange1"
          + name       = "mx1"
          + preference = 1
          + ttl        = 10
          + zone_name  = "dns1"
        },
      + {
          + exchange   = "exchange2"
          + name       = "mx1"
          + preference = 2
          + ttl        = 10
          + zone_name  = "dns1"
        },
    ]

Upvotes: 3

Related Questions