Terraform使用count从列表取值创建Azure子网报错求助
我需要根据启用的环境数量(ENA/DMZ/BDL),从预定义的CIDR列表中取值,动态创建对应数量的Azure子网,但所有尝试的方案都报类型不匹配错误。
初始尝试(列表方式)
locals.tf
locals { build_ena_true = var.build_ena ? 1 : 0 build_dmz_true = var.build_dmz ? 1 : 0 build_bdl_true = var.build_bdl ? 1 : 0 azurerm_cidrs = [ cidrsubnet("10.1.2.0/24", 1, 0), cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 0), cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 1) ] }
network.tf
resource "azurerm_subnet" "subnet" { count = local.build_ena_true + local.build_dmz_true + local.build_bdl_true name = "${var.azurerm_resource_prefix}-${local.env_short_name}-snet-${format("%03d", count.index + 1)}" resource_group_name = var.azurerm_net_resource_group virtual_network_name = var.azurerm_network_name address_prefixes = local.azurerm_cidrs[count.index] }
错误信息
Error: Incorrect attribute value type
on network.tf line 21, in resource "azurerm_subnet" "subnet":
21: address_prefixes = local.azurerm_cidrs[count.index]
├────────────────
│ count.index is 0
│ local.azurerm_cidrs is tuple with 3 elementsInappropriate value for attribute "address_prefixes": list of string required.
第二种尝试(Map+lookup方式)
locals.tf
locals { build_ena_true = var.build_ena ? 1 : 0 build_dmz_true = var.build_dmz ? 1 : 0 build_bdl_true = var.build_bdl ? 1 : 0 azurerm_cidrs = { 0 = cidrsubnet("10.1.2.0/24", 1, 0) 1 = cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 0) 2 = cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 1) } }
network.tf
resource "azurerm_subnet" "subnet" { count = local.build_ena_true + local.build_dmz_true + local.build_bdl_true name = "${var.azurerm_resource_prefix}-${local.env_short_name}-snet-${format("%03d", count.index + 1)}" resource_group_name = var.azurerm_net_resource_group virtual_network_name = var.azurerm_network_name address_prefixes = lookup(local.azurerm_cidrs, tostring(count.index), "") }
错误信息
Error: Incorrect attribute value type
on network.tf line 21, in resource "azurerm_subnet" "subnet":
21: address_prefixes = lookup(local.azurerm_cidrs, tostring(count.index), "")
├────────────────
│ count.index is 1
│ local.azurerm_cidrs is object with 3 attributesInappropriate value for attribute "address_prefixes": list of string required.
使用的Provider配置
terraform { required_version = ">= 1.9.0" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 4.4.0" } } }
问题原因及解决方法
核心问题是AzureRM子网的address_prefixes属性要求传入字符串列表(list(string)),但你直接传入了单个字符串(cidrsubnet函数返回的是string类型),导致类型不匹配。
修正方案1:直接包装为列表
修改network.tf中的address_prefixes字段,把单个CIDR值包装成列表:
resource "azurerm_subnet" "subnet" { count = local.build_ena_true + local.build_dmz_true + local.build_bdl_true name = "${var.azurerm_resource_prefix}-${local.env_short_name}-snet-${format("%03d", count.index + 1)}" resource_group_name = var.azurerm_net_resource_group virtual_network_name = var.azurerm_network_name address_prefixes = [local.azurerm_cidrs[count.index]] }
修正方案2:提前定义嵌套列表
如果想在locals中提前处理好格式,可将azurerm_cidrs定义为列表的列表:
locals { build_ena_true = var.build_ena ? 1 : 0 build_dmz_true = var.build_dmz ? 1 : 0 build_bdl_true = var.build_bdl ? 1 : 0 azurerm_cidrs = [ [cidrsubnet("10.1.2.0/24", 1, 0)], [cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 0)], [cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 1)] ] }
之后network.tf可直接使用原写法:
address_prefixes = local.azurerm_cidrs[count.index]
额外优化:用for_each替代count(更直观)
当前通过count求和的方式不够清晰,可改用for_each直接关联启用的环境与对应CIDR,同时自动生成关联名称:
locals { enabled_environments = merge( var.build_ena ? { ena = cidrsubnet("10.1.2.0/24", 1, 0) } : {}, var.build_dmz ? { dmz = cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 0) } : {}, var.build_bdl ? { bdl = cidrsubnet(cidrsubnet("10.1.2.0/24", 1, 1), 1, 1) } : {} ) } resource "azurerm_subnet" "subnet" { for_each = local.enabled_environments name = "${var.azurerm_resource_prefix}-${local.env_short_name}-snet-${each.key}" resource_group_name = var.azurerm_net_resource_group virtual_network_name = var.azurerm_network_name address_prefixes = [each.value] }
这种方式无需手动维护count值,子网名称直接关联环境类型,更易维护。
内容的提问来源于stack exchange,提问作者Dragonsys

