Tuple with 1 element > Can't access attributes on a list of objects

I have an Azure resource that I would like to access some outputs from. It is from azurerm_private_endpoint.

My goal is to convert record_sets to yaml file.

Whenever I try to access record_sets i get the following error: Can't access attributes on a list of objects. Did you mean to access attribute "record_sets" for a specific element of the list, or across all elements of the list? β•΅

record_sets = jsonencode(azurerm_private_endpoint.priv_end.*.private_dns_zone_configs[0])

"record_sets" = jsonencode(
                [
                  + {
                      + id                  = "/subscriptions/75406810-f3e6-42fa-97c6-e9027e0a0a45/resourceGroups/daetestaicoe/providers/Microsoft.Network/privateEndpoints/daetestaicoeaml-api/privateDnsZoneGroups/private-dns-zone-group/privateDnsZoneConfigs/privatelink.api.ml.azure.us"
                      + name                = "privatelink.api.ml.azure.us"
                      + private_dns_zone_id = "/subscriptions/75406810-f3e6-42fa-97c6-e9027e0a0a45/resourceGroups/ai-coe-rg/providers/Microsoft.Network/privateDnsZones/privatelink.api.ml.azure.us"
                      + record_sets         = [
                          + {
                              + fqdn         = "fa060cd5-051f-4093-bb11-4c123ca2aac0.workspace.usgovvirginia.privatelink.api.ml.azure.us"
                              + ip_addresses = [
                                  + "140.20.101.81",
                                ]
                              + name         = "fa060cd5-051f-4093-bb11-4c123ca2aac0.workspace.usgovvirginia"
                              + ttl          = 10
                              + type         = "A"
                            },
                          + {
                              + fqdn         = "fa060cd5-051f-4093-bb11-4c123ca2aac0.workspace.usgovvirginia.cert.privatelink.api.ml.azure.us"
                              + ip_addresses = [
                                  + "140.20.101.81",
                                ]
                              + name         = "fa060cd5-051f-4093-bb11-4c123ca2aac0.workspace.usgovvirginia.cert"
                              + ttl          = 10
                              + type         = "A"
                            },
                          + {
                              + fqdn         = "*.fa060cd5-051f-4093-bb11-4c123ca2aac0.inference.usgovvirginia.privatelink.api.ml.azure.us"
                              + ip_addresses = [
                                  + "140.20.101.83",
                                ]
                              + name         = "*.fa060cd5-051f-4093-bb11-4c123ca2aac0.inference.usgovvirginia"
                              + ttl          = 10
                              + type         = "A"
                            },
                        ]
                    },
                ]
            )
        }

How can I export the record_sets to yaml format?

Hi @rohrerb!

I think what’s making this a bit confusing is that there are two levels of list here. I assume you’re using count in the resource "azurerm_private_endpoint" "priv_end" block and so azurerm_private_endpoint.priv_end is a list, but then the private_dns_zone_configs for each one is also a list.

This situation is the edge case where the different behavior of legacy splat expressions is relevant: the legacy splat operator .* only accepts attribute access on its right-hand side, and so Terraform is parsing your expression as if you wrote it like this:

(azurerm_private_endpoint.priv_end.*.private_dns_zone_configs)[0]

That is: make a list of the private_dns_zone_configs attributes of all of the instances of azurerm_private_endpoint.priv_end – a list of lists – and then take the first element of that list, yielding a list.

I think what you want to do instead is to take the first element of each nested private_dns_zone_configs list, so the [0] at the end would be selecting an element of private_dns_zone_configs rather than an element of azurerm_private_endpoint.priv_end. You can get that result by using the modern splat expression syntax, which is written as [*] instead of .*:

record_sets = jsonencode(azurerm_private_endpoint.priv_end[*].private_dns_zone_configs[0])

Despite the similarity of the grammar, Terraform will parse this variant very differently: the [0] at the end will be understood as being part of the right operand of the [*] operator and so be handled by the splat operator itself, whereas in your original example the [0] was being ignored by the splat operator and then applied afterwards to the splat operator’s result.

I’m sorry that you hit a very subtle edge case here. This quirky behavior of .* is why we replaced it with [*] and recommend against using the legacy version in the documentation.

Thank you @apparentlymart ! That worked for me to at least show the results in a null_resource but not to pass them out of the module.

Within a module I have locals block that looks like this:

record_sets = var.create && length(var.private_dns_zone_ids) > 0 ? flatten(azurerm_private_endpoint.priv_end[*].private_dns_zone_configs[0].record_sets) : null

Then I have the following in output:

output "dns_configs" {
  value = var.create ? local.record_sets : null
}

Going to a local_file:

resource "local_file" "env_info" {
  content = replace(yamlencode(
    {
      aml-api                      = module.aml-endpoint-api.dns_configs
     # aml-notebooks                = module.aml-endpoint-notebooks.dns_configs
    }
  ), "\"", "")

  filename = format("%s/../deployments/%s/env.yaml", path.root, var.environment)

  depends_on = [module.ai-datalake-private-endpoint]
}

If resource_sets has only 1 in the array it works fine but more than one, i get this error:

Error: Unsupported attribute
β”‚
β”‚   on env.tf line 4, in resource "local_file" "env_info":
β”‚    4:       aml-api                      = module.aml-endpoint-api.dns_configs
β”‚     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     β”‚ module.aml-endpoint-api is a list of object
β”‚
β”‚ Can't access attributes on a list of objects. Did you mean to access attribute "dns_configs" for a specific element of the list, or across all elements of the list?

Hi @rohrerb,

The error message mentions that module.aml-endpoint-api itself is a list, which suggests that your module "aml-endpoint-api" block has the count argument too.

I don’t think I’ve seen the source code for that module block yet so I can’t be sure of how many items might appear in that list, but perhaps your goal is to make a flat sequence of all of the DNS configs across all instances of the module, which would be achievable with yet another splat operator and the flatten function:

  aml-api = flatten(module.aml-endpoint-api[*].dns_configs)

The idea here is to first use the splat operator to build a list of lists of objects, where the top level list has one element per instance if your module, and then use flatten to discard the extra nesting level to produce just a single flat list of objects.

1 Like

That fixed it! Thank you so much.