Aditya Garg
Aditya Garg

Reputation: 66

Azure/Terraform:Link subnets to NSGs(ERROR-for_each map includes keys derived from resource attributes that cannot be determined until apply)

Objective:Link multiple subnets in the environment to corresponding NSGs using a module (NSGs and Subnets have been created using separate modules)

Root Module:
1.main.tf

resource "azurerm_subnet_network_security_group_association" "root_subnet_nsg_association" {
  subnet_id                 = var.subnet_id
  network_security_group_id = var.nsg_id
}

2.variables.tf

variable "subnet_id"{
    type=number
    description="ID of the subnet which is to be attached to NSG"
    #default=""
}

variable "nsg_id"{
    type=number
    description="ID of the NSG which is to be associated with a subnet"
    #default=""
}

Calling Module in Projects Folder:
(for_each used to iterate the module)

1.nsg_subnet_association.tf

module "nsg_subnet_asosciation_module"{
source="../../Modules/network/nsg_subnet_association"

#Variable names to be passed into the root module:
#Use for_each to loop the module:

#for_each accepts a set or map but not list as a value

for_each          = local.nsg_subnet_association

subnet_id=each.key
nsg_id=each.value
}

2.locals block passing in values to the calling module:
NOTE:It is possible to have dynamic keys in the map using parenthesis ()

locals{ //Key in subnet name and NSG name for each element of the LIST
        //Implicit dependence on Subnet and NSG being created before attempt to associate

     #It is possible to have dynamic keys using parenthesis () as seen on left below   
     nsg_subnet_association={
        (module.subnet_module["MGT-Subnet-1"].subnet_id)= module.nsg_module["HUB-NSG"].nsg_id
        (module.subnet_module["MGT-Subnet-1"].subnet_id) = module.nsg_module["MGT-NSG"].nsg_id
        (module.subnet_module["SEC-Subnet-1"].subnet_id) = module.nsg_module["SEC-NSG"].nsg_id
    }

}

This ends up with the following error:
The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.
When working with unknown values in for_each, it's better to define the map keys statically in your configuration and place apply-time results only in the map values.
Alternatively, you could use the -target planning option to first apply only the resources that the for_each value depends on, and then apply a second time to fully converge.

Upvotes: 0

Views: 1137

Answers (1)

kavya Saraboju
kavya Saraboju

Reputation: 10871

In Terraform , when dynamically getting the value of vnets or subnets , it may take time to create and the rest of the dependent resources cannot get desired values and so this error occurs.

Error:

The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so Terraform cannot determine the full set of keys that will identify the instances of this resource.

Use a code where the values are defined statically to resolve the error:

example:

Code:

Variables.tf:

variable "virtual_network_name" {
  type    = string
  default = "my-virtual-network"
}

variable "subnet_address_prefixes" {
  type    = list(string)
  default = ["10.0.1.0/24", "10.0.2.0/24"]
}

variable "subnet_names" {
  type    = set(string)
  default = ["subnet1", "subnet2"]
}

variable "nsg_names" {
  type    = set(string)
  default = ["nsg1", "nsg2"]
}


variable "subnet_nsg_mappings" {
  type    = map(string)
  default = {
    "subnet1" = "nsg1"
    "subnet2" = "nsg2"
  }
}

Main.tf

resource "azurerm_virtual_network" "virtual_network" {
  name                = var.virtual_network_name
  address_space       = ["10.0.0.0/16"]
  location                    = data.azurerm_resource_group.example.location
   resource_group_name  = data.azurerm_resource_group.example.name
   
}

resource "azurerm_network_security_group" "nsg" {
  for_each = toset(var.nsg_names)

  name                = each.value
  location                    = data.azurerm_resource_group.example.location
   resource_group_name  = data.azurerm_resource_group.example.name

    security_rule {
    name                       = "allow_http"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

resource "azurerm_subnet" "subnet" {
  for_each = toset(var.subnet_names)

  name                 = each.value
  virtual_network_name = azurerm_virtual_network.virtual_network.name
  address_prefixes     = var.subnet_address_prefixes
  resource_group_name  = data.azurerm_resource_group.example.name
 // enable_multiple_address_prefixes = true
  
}

# Associate each subnet with its corresponding NSG
resource "azurerm_subnet_network_security_group_association" "subnet_nsg" {
 
 for_each = var.subnet_nsg_mappings

  subnet_id              = azurerm_subnet.subnet[each.key].id
  network_security_group_id = azurerm_network_security_group.nsg[each.value].id
  //subnet_id              = azurerm_subnet.subnet[each.key].id
 // network_security_group_id = azurerm_network_security_group.nsg[var.subnet_nsg_mappings[each.value]].id
}

Or

Define locals for mappings .

locals {
  subnet_nsg_mappings = {
    "subnet1" = "nsg1",
    "subnet2" = "nsg2",
    "subnet3" = "nsg3"
  }
}

resource "azurerm_subnet_network_security_group_association" "subnet_nsg" {
  for_each = toset(var.subnet_names)

  subnet_id                = azurerm_subnet.subnet[each.value].id
  network_security_group_id = azurerm_network_security_group.nsg[local.subnet_nsg_mappings[each.value]].id
}

enter image description here

enter image description here

enter image description here

If dynamic values must be used for_each keys cannot be determined during apply time. In that case use the -target option to first apply vnet and subnet values i.e; the resources that the for_each value depends on and apply completely.

terraform apply -target="azurerm_virtual_network.virtual_network" -target="azurerm_subnet.subnet" 

enter image description here

Reference: azurerm_subnet_network_security_group_association | Resources | hashicorp/azurerm | Terraform Registry

Upvotes: 0

Related Questions