You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform使用for_each批量创建子网遇问题,请求排查

Terraform使用for_each创建子网问题排查

背景

此问题为之前Terraform对象/映射类型值引用问题的延续。

目标

使用Terraform的for_each循环创建子网。

terraform.tfvars.json相关配置

"subnets" : {
    "Dev" :
    [  
    {"gw_snet":{
      "name"                 : "GatewaySubnet",
      "address_prefixes"     : ["10.1.1.0/24"]
    },
    "dns-snet" : {
      "name"                 : "InboundDNSSubnet",
      "address_prefixes"     : ["10.1.2.0/24"]
    },
    "common_snet" : {
      "name"                 : "Common",
      "address_prefixes"     : ["10.1.3.0/24"]
    },
    "clientdata_snet" : {
      "name"                 : "ClientDataSubnet",
      "address_prefixes"     : ["10.1.4.0/20"]
    }}
    ],
    "Stage" :
    [  
    {"gw_snet":{
      "name"                 : "GatewaySubnet",
      "address_prefixes"     : ["10.2.1.0/24"]
    },
    "dns-snet" : {
      "name"                 : "InboundDNSSubnet",
      "address_prefixes"     : ["10.2.2.0/24"]
    },
    "common_snet" : {
      "name"                 : "Common",
      "address_prefixes"     : ["10.2.3.0/24"]
    },
    "clientdata_snet" : {
      "name"                 : "ClientDataSubnet",
      "address_prefixes"     : ["10.2.4.0/20"]
    }}
    ],
    "Prod" :
    [  
    {"gw_snet":{
      "name"                 : "GatewaySubnet",
      "address_prefixes"     : ["10.3.1.0/24"]
    },
    "dns-snet" : {
      "name"                 : "InboundDNSSubnet",
      "address_prefixes"     : ["10.3.2.0/24"]
    },
    "common_snet" : {
      "name"                 : "Common",
      "address_prefixes"     : ["10.3.3.0/24"]
    },
    "clientdata_snet" : {
      "name"                 : "ClientDataSubnet",
      "address_prefixes"     : ["10.3.4.0/20"]
    }}
    ]
}  

VNet创建代码

resource "azurerm_virtual_network" "vnet" {
  name                = var.hub_vnet_name
  location            = azurerm_resource_group.rg[0].location
  resource_group_name = azurerm_resource_group.rg[0].name
  for_each = {for k,v in var.vnet_address_space: k=>v if k == "Dev"}
  address_space       =  each.value
  dns_servers         = var.dns_servers
  tags     = {
    environment = "${var.env}"
    costcentre = "14500"
  }
  dynamic "ddos_protection_plan" {
    for_each = local.if_ddos_enabled

    content {
      id     = azurerm_network_ddos_protection_plan.ddos[0].id
      enable = false
    }
  }
}

子网创建尝试代码

resource "azurerm_subnet" "mysubnet" {
  for_each = {for k,v in var.subnets: k=>v if k == "Dev"}
  name                 = each.value.name
  address_prefixes     = [each.value.address_prefixes]
  virtual_network_name = var.hub_vnet_name
  resource_group_name  = var.resource_group_name
}

遇到的问题

Terraform plan无报错,但无法创建VNet,计划未通过验证。怀疑子网变量定义存在问题,同时不确定如何正确访问嵌套值,当前的each.value.name和[each.value.address_prefixes]无法正常工作。


问题根源与解决方案

1. 变量结构嵌套过深导致访问失败

var.subnets的结构中,每个环境(如Dev)对应的值是包含单个对象的数组,数组内的对象才是各个子网的键值对集合。当前for_each仅拿到了Dev对应的数组,而数组本身没有name和address_prefixes属性,导致引用失败。

2. 修正方案:扁平化子网结构

先通过locals将嵌套的子网结构扁平化,方便for_each遍历:

locals {
  flattened_subnets = merge([
    for env, subnets_list in var.subnets : {
      for subnet_key, subnet in subnets_list[0] : "${env}-${subnet_key}" => merge(subnet, { environment = env })
    }
  ]...)
}

这个本地值会将所有子网展开为一个映射,键格式为环境-子网标识(如Dev-gw_snet),值包含子网属性及所属环境。

3. 修正子网资源代码

使用扁平化后的本地值,并正确引用VNet资源实例(而非变量):

resource "azurerm_subnet" "mysubnet" {
  for_each = {
    for k, v in local.flattened_subnets : k => v
    if v.environment == "Dev"
  }

  name                 = each.value.name
  address_prefixes     = each.value.address_prefixes
  virtual_network_name = azurerm_virtual_network.vnet["Dev"].name
  resource_group_name  = azurerm_resource_group.rg[0].name
}

4. 修正VNet资源的命名问题

原VNet代码中name = var.hub_vnet_name会导致多环境下重名,需添加环境后缀确保唯一性:

resource "azurerm_virtual_network" "vnet" {
  for_each = {for k,v in var.vnet_address_space: k=>v if k == "Dev"}
  name                = "${var.hub_vnet_name}-${each.key}"
  location            = azurerm_resource_group.rg[0].location
  resource_group_name = azurerm_resource_group.rg[0].name
  address_space       = each.value
  dns_servers         = var.dns_servers
  tags = {
    environment = each.key
    costcentre = "14500"
  }
  dynamic "ddos_protection_plan" {
    for_each = local.if_ddos_enabled
    content {
      id     = azurerm_network_ddos_protection_plan.ddos[0].id
      enable = false
    }
  }
}

内容的提问来源于stack exchange,提问作者asp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 11:45:34