Multi-value output

Hi, I’m trying to get to grips with a multi-value output as described in the Terraform documentation, i.e. as show in Create Dynamic Expressions > Return multiple values with splat expression:

output "private_addresses" {
  value = aws_instance.ubuntu[*].private_dns
}

But when attempting to use the stated syntax I get the error:

│ Error: Unsupported attribute
│
│   on outputs.tf line 2, in output "sqs_queue":
│    2:   value = aws_sqs_queue.sqs_queue[*].arn
│
│ This object does not have an attribute named "arn".

The documentation for aws_sqs_queue reports that it does have an ARN attribute. What is the correct syntax for a multi-value output?

Hi @glenn.comiskey,

Your code example shows a reference to aws_instance.ubuntu, but the error message shows aws_sqs_queue.sqs_queue, so it seems like the error is in a different place than you indicated.

Based on what you described I assume you’re talking about the Create Dynamic Values tutorial, which indeed has a section Return multiple values with the splat expression.

The content of that section seems to be assuming the examples further up the page where it declares aws_instance.ubuntu as having count = (var.high_availability == true ? 3 : 1), which therefore causes aws_instance.ubuntu to appear as a list with either three elements or one element, depending on that variable value. That would therefore be valid, because the [*] operator works by applying the attribute lookup and index operations to its right to each element of the list on its left.

In your case it sounds like aws_sqs_queue.sqs_queue isn’t using count, and therefore you can’t use the splat operator in the same way. I can only guess because you didn’t show the resource "aws_sqs_queue" "sqs_queue" block, but I think you probably used for_each with it and therefore aws_sqs_queue.sqs_queue is a map of objects instead of a list of objects. If so, you’ll need to use a for expression to decide how to transform that map into a suitable return value.

You could return a map from key to ARN, if the for_each keys would be meaningful to the caller of this module:

output "private_addresses" {
  value = { for k, v in aws_instance.ubuntu : k => v.arn }
}

Alternatively, you could just return a set of all of the ARNs without any indication of which instance each of them belonged to, which would be reasonable to do if the for_each here is just an implementation detail and the individual keys would not be useful or meaningful to the module caller anyway:

output "private_addresses" {
  value = toset([ for v in aws_instance.ubuntu : v.arn ])
}

or alternatively you can write that a slightly different way, using values to get a collection of the map values (discarding the keys) and then using the splat operator on that:

output "private_addresses" {
  value = toset(values(aws_instance.ubuntu)[*].arn)
}

This values-based answer should be equivalent to the one before it in result. Neither is necessarily better than the other, it’s just two different ways to get the same result. I find the for expression version more readable, but of course that’s subjective!