Retrieve output from a data source while iterating over objects in a dynamic block

I have a dynamic block in a resource

dynamic "ip_configuration" {
    for_each = var.nic_ip_configuration

    content {
      gateway_load_balancer_frontend_ip_configuration_id = <DATA_SOURCE>
      public_ip_address_id = <DATA_SOURCE>
    }
  }

The variable nic_ip_configuration would be a list of objects

[
  {
    ip_configuration_name = "1"
    gateway_load_balancer_frontend_ip_configuration_name = "abc"
    public_ip_address_name = "def"
  },
  {
    ip_configuration_name = "2"
    gateway_load_balancer_frontend_ip_configuration_name = "ghi"
    public_ip_address_name = "jkl"
  }
]

I would like to be able to use the public_ip_address_name in each object and pass that to a data block to get the ID, and then use the ID value from the data source when iterating through each object in the dynamic block. This way I’d only need to know the IP address name in my configuration.

I am not sure what the best approach is here.

In my data source where I retrieve the ID value would I want to create a key-value mapping where ip_configuration_name is the key and ID is the value

data "azurerm_public_ip" "example" {
  for_each = { for item in var.nic_ip_configuration: item.name => item }
  
  name = each.value.public_ip_address_name

I was testing with an output like this to see what the end result looks like from the data block

output "test_public_ip" {
 value = { for k,v in data.azurerm_public_ip.example: k => {public_ip_address_id = v.id} }
}

This does get me close to where the output result is

test_public_ip = {
  "1" = {
      public_ip_address_id = "<ID_STRING>"
    }
}

Would I want to move that output logic to a local value, and then attempt to merge that object back into nic_ip_configuration based on the ip_configuration_name being a key for the merge?

What if I also wanted to do the same for the gateway load balancer IP and get that merged in?

If I’m understanding what you’re trying to do correctly, you might be able to directly reference data.azurerm_public_ip.example[name] or similar since you’ve already got the name in scope in nic_ip_configuration? If you do the for_each based on public_ip_address_name, like this:

dynamic "ip_configuration" {
  for_each = [for x in var.nic_ip_configuration : x.public_ip_address_name]

you should then be able to access it that way.

But otherwise, yes, you could probably build everything you need into a single data structure, and in that case, I think you’d want to use a local var as you mentioned to do your merging etc.

What I like to do with stuff like this is make a little example that’s as small / self-contained as possible, using an output - you can also use local.foo and then make the output show local.foo

Of course, the better thing if possible would probably be to define the public IPs in terraform and reference them directly (via the resource attribute and / or via remote state data resource if necessary).

To be able to reference data.azurerm_public_ip.example[name] in the dynamic block I would need to initialize the data source first and iterate through var.nic_ip_configuration to create a data structure that contains the name mapped to an ID correct?

Or does referencing data.azurerm_public_ip.example[name] actually invoke the data source to retrieve that value during iteration on each name value inside the dynamic block?

Since you’re using for_each on the data.azurerm_public_ip.example data source, I think you should have one item for each name in var.nic_ip_configuration if you iterate all the names in that list comprehension. But I could be wrong - you’ll probably just have to play around with it a little.

If it’s available to use within a local var, it should also be available to use at plan time, I would think.