How to use terraform functions within for loop's keys?

Hi Folks,

This is a strinctly non-functional issue, only a cosmetic one.

Time to time I need to create maps, where the unique key contains the ID of resource. This makes the keys very difficult to read in logs and console.

For example:

nic_asg_association = {
  + "NIC_NAME-/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Network/applicationSecurityGroups/APPSEC_GROUP_NAME" = {
      + application_security_group_id = "/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Network/applicationSecurityGroups/APPSEC_GROUP_NAME"
      + name                          = "NIC_NAME"
    }
  + "NIC_NAME-/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Network/applicationSecurityGroups/APPSEC_GROUP_NAME" = {
      + application_security_group_id = "/subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Network/applicationSecurityGroups/APPSEC_GROUP_NAME"
      + name                          = "NIC_NAME"
    }
}

This particular output is generated with:

locals {

  nic_default_asg_association = {
    for asg in var.nic_default_interface.application_security_group_ids :
    "${var.nic_default_interface.name}-${asg}" => {
      name = var.nic_default_interface.name
      application_security_group_id = asg
    }
  }

  nic_additional_asg_association = merge([
    for iface in var.nic_additional_interface :
    iface.application_security_group_ids == null ? {} : {
      for asg in iface.application_security_group_ids :
      "${iface.name}-${asg}" => {
        name = iface.name
        application_security_group_id = asg
      }
    }
  ]...)

  nic_asg_association = merge(local.nic_default_asg_association, local.nic_additional_asg_association)

}

I have tried to simplify the key for better visuals by using string functions during computation of the map. The code runs without error, but nothing happens.

with replace

"${replace(var.nic_default_interface.name, "-.*$", "")}-${asg}"
"${replace(iface.name, "-.*$", "")}-${asg}"
"${replace("${var.nic_default_interface.name}-${asg}", "/.*asgr-", "asgr-")}"
"${replace("${iface.name}-${asg}", "/.*asgr-", "asgr-")}"

Tried also on just a part of the key

"${var.nic_default_interface.name}-${replace(asg, "/[^/]*$", "")}"
"${iface.name}-${replace(asg, "/[^/]*$", "")}"

with element

element(split("/asgr-", "${var.nic_default_interface.name}-${asg}"), 1)

Question:

Am I doing something wrong, or terraform just cant handle to call functions in this situation.

Thanks,
Leder

Hi @Ledermayer,

In your first example you’ve specified the substring to search for as "-.*$", which won’t do anything unless exactly that sequence of characters appears in var.nic_default_interface.name.

I guess what you wanted to do here was to treat that as a regular expression pattern to trim off everything after the first dash. replace only interprets the second argument as a regular expression pattern if the string starts and ends with slash characters /, so you’d need to write that like this:

replace(var.nic_default_interface.name, "/-.*$/", "")

Another way to write it would be to extract just the part before the slash using the regex function:

regex("^[^-]+", var.nic_default_interface.name)

The pattern above matches as many non-dash characters as possible – at least one – at the start of the string, which is a positive alternative to the operation you expressed as “remove the first dash and everything after it”. One difference with this variant is that it’ll raise an error if it encounters a string that doesn’t match the pattern, rather than just quietly leaving the string unchanged, which could either be and advantage or a disadvantage depending on whether non-matching strings would be considered a bug.

Since the regex function is always in “regex mode”, there’s no need for the special slash markers at the start and end of the pattern, and if you did include them then Terraform would assume you wanted to use them as a part of the pattern, rather than as delimiters.