TFNFR7 - Count & for_each Use

ID: TFNFR7 - Category: Code Style - count & for_each Use

We can use count and for_each to deploy multiple resources, but the improper use of count can lead to anti pattern.

You can use count to create some kind of resources under certain conditions, for example:

resource "azapi_resource" "network_security_group" {
  count     = local.create_new_security_group ? 1 : 0
  type      = "Microsoft.Network/networkSecurityGroups@2023-11-01"
  name      = coalesce(var.new_network_security_group_name, "${var.subnet_name}-nsg")
  parent_id = var.parent_id
  location  = local.location
  tags      = var.new_network_security_group_tags
  body = {
    properties = {}
  }
  response_export_values = []
}

The module’s owners MUST use map(xxx) or set(xxx) as resource’s for_each collection, the map’s key or set’s element MUST be static literals.

Good example:

resource "azapi_resource" "subnet_pair" {
  for_each  = var.subnet_map // `map(string)`, when user call this module, it could be: `{ "subnet0": "subnet0" }`, or `{ "subnet0": azapi_resource.subnet0.name }`
  type      = "Microsoft.Network/virtualNetworks/subnets@2023-11-01"
  name      = "${each.value}-pair"
  parent_id = azapi_resource.virtual_network.id
  body = {
    properties = {
      addressPrefixes = ["10.0.1.0/24"]
    }
  }
  response_export_values = []
}

Bad example:

resource "azapi_resource" "subnet_pair" {
  for_each  = var.subnet_name_set // `set(string)`, when user use `toset([azapi_resource.subnet0.name])`, it would cause an error.
  type      = "Microsoft.Network/virtualNetworks/subnets@2023-11-01"
  name      = "${each.value}-pair"
  parent_id = azapi_resource.virtual_network.id
  body = {
    properties = {
      addressPrefixes = ["10.0.1.0/24"]
    }
  }
  response_export_values = []
}