Referencing resources created using for_each

I am trying to utilize for_each/for feature added in 0.12 to deploy resources by establishing a tree structure for dependencies.

Structure:

values.tfvars

  subnets = {
    dmz-1 = {
      name              = "subnet-a"
      cidr              = "10.0.1.0/24",
      nsgs = {
        nsg1 = {
            httpallowinbound = {
              name                       = "httpallow"
              priority                   = 100
              direction                  = "Inbound"
              access                     = "Allow"
              protocol                   = "Tcp"
              source_port_range          = "*"
              destination_port_range     = "*"
              source_address_prefix      = "*"
              destination_address_prefix = "*"
            },
            httpdenyinbound = {
              name                       = "httpdeny"
              priority                   = 101
              direction                  = "Inbound"
              access                     = "Deny"
              protocol                   = "Tcp"
              source_port_range          = "*"
              destination_port_range     = "*"
              source_address_prefix      = "*"
              destination_address_prefix = "*"
           },
        },
        nsg2 = {
            test1allowinbound = {
              name                       = "httpallow"
              priority                   = 100
              direction                  = "Inbound"
              access                     = "Allow"
              protocol                   = "Tcp"
              source_port_range          = "*"
              destination_port_range     = "*"
              source_address_prefix      = "*"
              destination_address_prefix = "*"
            },
            test2denyinbound = {
              name                       = "httpdeny"
              priority                   = 101
              direction                  = "Inbound"
              access                     = "Deny"
              protocol                   = "Tcp"
              source_port_range          = "*"
              destination_port_range     = "*"
              source_address_prefix      = "*"
              destination_address_prefix = "*"
           },
        },
      }
    }
  }

locals.tf

  nsgs = flatten([
    for subnets_name, subnets_values in var.subnets : [
      for nsg_name, nsg_values in subnets_values.nsgs : [
        for rule_name, rule_values in nsg_values: {
          nsg_name                   = nsg_name
          rule                       = rule_name
          name                       = rule_values.name
          priority                   = rule_values.priority
          direction                  = rule_values.direction
          access                     = rule_values.access
          protocol                   = rule_values.protocol
          source_port_range          = rule_values.source_port_range
          destination_port_range     = rule_values.destination_port_range
          source_address_prefix      = rule_values.source_address_prefix
          destination_address_prefix = rule_values.destination_address_prefix
       }
      ]
    ]
  ])

resources

resource "azurerm_network_security_group" "this" {
  for_each = {
      for sg in local.nsg_name:
        "${sg.nsg_name}" => sg
  }
  name                 = each.value.nsg_name
  location             = var.location
  resource_group_name  = var.resource_group_name
}


resource "azurerm_network_security_rule" "this" {
  for_each = {
      for sg in local.nsgs:
        "${sg.rule}.${sg.name}" => sg
        //sg.nsg_name => sg...
  }
  name                        = each.value.name
  priority                    = each.value.priority
  direction                   = each.value.direction
  access                      = each.value.access
  protocol                    = each.value.protocol
  source_port_range           = each.value.source_port_range
  destination_port_range      = each.value.destination_port_range
  source_address_prefix       = each.value.source_address_prefix
  destination_address_prefix  = each.value.destination_address_prefix
  resource_group_name         = var.resource_group_name
  network_security_group_name = azurerm_network_security_group.this[each.value.nsg_name]
}

I am unable to attach security rule back to nsg using this:

network_security_group_name = azurerm_network_security_group.this[each.value.nsg_name]

It throws this error:

Error: Incorrect attribute value type

  on ../../vnet.tf line 55, in resource "azurerm_network_security_rule" "this":
  55:   network_security_group_name = azurerm_network_security_group.this[each.value.nsg_name]
    |----------------
    | azurerm_network_security_group.this is object with 2 attributes
    | each.value.nsg_name is "nsg1"

Have tried to use tostring function but that does not help either. Any suggestions please to move forward.

Terraform v0.12.19
+ provider.azurerm v1.42.0
1 Like

Hi Faizan!

Thanks for breaking down your configuration and putting all them together! It is really useful for debugging. Could you check if all of the locals have been declared?

resource "azurerm_network_security_group" "this" {
  for_each = {
      for sg in local.nsg_name:
        "${sg.nsg_name}" => sg
  }
  name                 = each.value.nsg_name
  location             = var.location
  resource_group_name  = var.resource_group_name
}

The configuration above cannot find local.nsg_name and wanted to make sure we are debugging it with the correct names and types!

Rosemary