'running a loop on values index in terraform

I am creating a nsg for azure and currently stuck with values function. for eg

my variable is

variable "subnets" {
  description = "A list of subnets inside the VPC"
  type        = map(any)
  default = {
    test = {
      name           = "cs"
      address_prefix = ["x.x.x.x/x"]
      attached_nat   = "false"
      inbound_rules = {
        # [name, priority, direction, access, protocol, destination_port_range, source_address_prefix, destination_address_prefix]
        test = {name ="http", priority= "100",direction= "Inbound",access= "Allow", protocol="Tcp", destination_port_range="80", source_address_prefix="*", destination_address_prefix="0.0.0.0/0"}
        test = {name ="https", priority= "107",direction= "Inbound",access= "Allow", protocol="Tcp", destination_port_range="443", source_address_prefix="*", destination_address_prefix="0.0.0.0/0"}
      }
    }
 }
test2 = {
      name           = "cs2"
      address_prefix = ["x.x.x.x/x"]
      attached_nat   = "false"
      inbound_rules = {
        # [name, priority, direction, access, protocol, destination_port_range, source_address_prefix, destination_address_prefix]
        test = {name ="http", priority= "100",direction= "Inbound",access= "Allow", protocol="Tcp", destination_port_range="80", source_address_prefix="*", destination_address_prefix="0.0.0.0/0"}
        test = {name ="https", priority= "107",direction= "Inbound",access= "Allow", protocol="Tcp", destination_port_range="443", source_address_prefix="*", destination_address_prefix="0.0.0.0/0"}
      }
    }
 }
}

my resource block is like -

resource "azurerm_network_security_group" "subnet" {
  for_each = var.subnets

  name                = format("%s-${each.value.name}")
  location            = var.location
  resource_group_name = var.resource_group_name  
}

resource "azurerm_network_security_rule" "subnet" {
  for_each = lookup(values(var.subnets)[0], "inbound_rules", [])
  name                       = each.value.name
  priority                   = each.value.priority
  direction                  = each.value.direction
  access                     = each.value.access
  protocol                   = each.value.protocol
  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
  description                = "${each.value.direction}_Port_${each.value.destination_port_range}"
  resource_group_name        = var.resource_group_name
  network_security_group_name = azurerm_network_security_group.subnet[each.key].name
}

if you see my for_each loop above i have hard coded [0] in values(var.subnets)[0] to work for single subnet but I need it work it if multiple subnet blocks are provided in the variable like it should loop with [0],[1] etc. so that it can get attached to the nsg created for that subnet Can someone help me with the loop here?



Solution 1:[1]

You have to flatten subnets, for examples as:

locals {
  subnets_flat = merge([
      for subnet_key, subnet in var.subnets: {
        for rule_name, rule in subnet["inbound_rules"]: 
          "${subnet_key}-${rule_name}" => {
             subnet_key     = subnet_key
             name           = subnet["name"]
             address_prefix = subnet["address_prefix"]
             attached_nat   = subnet["attached_nat"]
             rule = rule 
        }
      }]...) # please do NOT remove the dots
}
}

then

resource "azurerm_network_security_rule" "subnet" {
  for_each                   = local.subnets_flat
  name                       = each.value.rule.name
  priority                   = each.value.rule.priority
  direction                  = each.value.ruledirection
  access                     = each.value.rule.access
  protocol                   = each.value.rule.protocol
  source_port_range          = "*"
  destination_port_range     = each.value.rule.destination_port_range
  source_address_prefix      = each.value.rule.source_address_prefix
  destination_address_prefix = each.value.rule.destination_address_prefix
  description                = "${each.value.rule.direction}_Port_${each.value.rule.destination_port_range}"
  resource_group_name        = var.resource_group_name
  network_security_group_name = azurerm_network_security_group.subnet[each.value.subnet_key].name
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Marcin