How to use attributes of attributes?

Hi all,

I’m really struggling to get the list of ip addresses returned by the ‘ip_addresses’ attribute in the ‘aws_globalaccelerator_accelerator.xxx.ip_sets’ attribute into a Route 53 resource.

I’ve tried ‘${aws_globalaccelerator_accelerator.xxx.ip_sets.ip_addresses}’ but I’m clearly doing something wrong.

Any suggestions?

Hi @jcardoso-bv!

Could you share a full example of what you’re trying, and what error message (or other unexpected behavior) you saw when you tried it? Thanks!

(Be sure to mark any code examples as code using the “preformatted text” (<>) tool in the editor, or else they can be very hard to read.)

Hi @apparentlymart

Sure thing. I’m essentially trying to do the following:

resource "aws_globalaccelerator_accelerator" "example" {
  enabled         = true
  ip_address_type = "IPV4"
  name            = "example-api-eu"
}

resource "aws_route53_record" "example" {
  name    = "api.eu.test.com"
  records = [
    "${aws_globalaccelerator_accelerator.example.ip_sets.ip_addresses}",
  ]
  ttl     = "600"
  type    = "A"
  zone_id = "123456789012"
}

output "globalaccelerator_ip_addresses" {
  value = "${aws_globalaccelerator_accelerator.example.ip_sets}"
}

Without the route53 resource, the output gives me something similar to the following:

globalaccelerator_ip_addresses = [
  {
    "ip_addresses" = [
      "192.168.0.1",
      "192.168.0.2",
    ]
    "ip_family" = "IPv4"
  },
]

The error I get when trying to populate records for the route53 resource as above is as follows:

Error: Unsupported attribute

  on route53.tf line 4, in resource "aws_route53_record" "example":
   4:     "${aws_globalaccelerator_accelerator.example.ip_sets.ip_addresses}",

This value does not have any attributes.

Now this contradicts the documentation which states ‘ip_addresses’ is an attribute of ‘ip_sets’ for that resource but I’ve tried all sorts of ways of parsing it and get similar errors.

Clearly I’m doing some thing wrong or misunderstanding how I should be using the ‘ip_addresses’ attribute. Any help appreciated.

So after a little more trial and error, I got the following to work:

resource "aws_route53_record" "example" {
  name    = "api.eu.test.com"
  records = "${aws_globalaccelerator_accelerator.example.ip_sets[0].ip_addresses}"
  ttl     = "600"
  type    = "A"
  zone_id = "123456789012"
}

As far as I can tell this seems to be the cleanest way of returning the IP addresses I need.

Hi @jcardoso-bv! Thanks for sharing the additional context, and I’m glad you figured it out.

The clue in what you shared was the square brackets around the value of the globalaccelerator_ip_addresses output, which suggests that ip_sets is a list of objects rather than a single object. That’s presumably to allow for the possibility of multiple ip_sets blocks, which is a common way to represent blocks in expressions.

By adding [0] here you selected the first (and, in this case, only) element of that list, and then accessed the ip_addresses attribute of that object.

In general, when a particular block type supports multiple block instances, it behaves like a resource with count set, in that it appears as some sort of collection value in expressions. The documentation section References to Resource Attributes talks about this some more, and also gives some general patterns that should work for all block types, regardless of how exactly they are represented in the data structure. The advice there leads to a different way to get a similar result:

  records = flatten(aws_globalaccelerator_accelerator.example.ip_sets[*].ip_addresses)

The [*] here is a shorthand for accessing the .ip_addresses attribute for each block, returning a list of those values. Since ip_addresses is already a list, that gives a list of lists, which we then flatten down into a single list using the flatten function here. The result then is the same as you had before in this situation where there’s only one ip_sets block, but if there were more than one block then it would collect all of the IP addresses from all of the blocks and use them all together.

Both of these approaches are equally valid here, and the distinction only matters if you might have multiple ip_set blocks and thus need to decide whether to take the IP addresses only from the first one or from all of them.

Hi @apparentlymart

Many thanks for the detailed explanation. I’ve been adjusting to recent Terraform 0.12 changes and the list of objects being returned by the aws_globalaccelerator_accelerator resource threw me off-guard.

I’ve amended my code to use the suggested flatten function as you’re correct in that AWS could return further blocks at a later stage.

Thanks for your help and have a great week. :slight_smile: