Terraform中用for_each循环关联网卡与LB后端池的问题求助
需求
- app-poc-1-nic、app-poc-2-nic 关联至 app-lb 后端池
- db-nic-1-1-nic、db-nic-1-2-nic 关联至 db-lb 后端池
本地配置块(local)
locals { vms = { nodes = { app_node1 = { "vm_name" = "app-poc" "vm_num" = "1" networks = { nic1 = { "vm_name" = "app-poc" "subnet" = "/subscriptions/*****/subnets/app" }, } }, app_node2 = { "vm_name" = "app-poc" "vm_num" = "2" networks = { nic1 = { "vm_name" = "app-poc" "subnet" = "/subscriptions/*****/subnets/app" }, } }, service_node1 = { "vm_name" = "service-poc" "vm_num" = "1" networks = { nic1 = { "vm_name" = "service-poc" "subnet" = "/subscriptions/*****/subnets/app" }, } }, service_node2 = { "vm_name" = "service-poc" "vm_num" = "2" networks = { nic1 = { "vm_name" = "service-poc" "subnet" = "/subscriptions/*****/subnets/app" }, } }, db_node1 = { "vm_name" = "db-poc" "vm_num" = "1" networks = { nic1 = { "vm_name" = "db-nic-1" "subnet" = "/subscriptions/*****/subnets/app" }, nic2 = { "vm_name" = "db-nic-2" "subnet" = "/subscriptions/*****/subnets/db" } } }, db_node2 = { "vm_name" = "db-poc" "vm_num" = "2" networks = { nic1 = { "vm_name" = "db-nic-1" "subnet" = "/subscriptions/*****/subnets/app" }, nic2 = { "vm_name" = "db-nic-2" "subnet" = "/subscriptions/*****/subnets/app" }, } }, }, } lbs = { tiers = { app-lb = { lb_name = "app-lb" fip_name = "app-fip" subnet_id = "/subscriptions/*****/subnets/app" private_ip_type = "Dynamic" address_pool_name = "app-address-pool" lb_probes = { ssh_probe = { protocol = "Tcp" port = "22" } } lb_rules = { ssh_rule = { frontend_port = "22" protocol = "Tcp" backend_port = "22" enable_floating_ip = true frontend_ip_config_name = "app-fip" } } }, db-lb = { lb_name = "db-lb" fip_name = "db-fip" subnet_id = "/subscriptions/*****/subnets/app" private_ip_type = "Dynamic" address_pool_name = "db-address-pool" lb_probes = { ssh_probe = { protocol = "Tcp" port = "22" } } lb_rules = { ssh_rule = { frontend_port = "22" protocol = "Tcp" backend_port = "22" enable_floating_ip = false frontend_ip_config_name = "db-fip" } } } } } }
已实现的Terraform资源代码
data "azurerm_resource_group" "rg" { name = "test-rg" } resource "azurerm_network_interface" "nic-poc" { for_each = { for vm in flatten([ for vm_name, vm in local.vms.nodes : [ for nic_name, nic in vm.networks : { vm_number = vm.vm_num, vm_name = vm_name, nic_value = nic.vm_name, subnet_value = nic.subnet nic_name = nic_name } ] ] ) : "${vm.vm_name}-${vm.nic_name}" => vm } name = "${each.value.nic_value}-${each.value.vm_number}-nic" location = data.azurerm_resource_group.rg.location resource_group_name = data.azurerm_resource_group.rg.name ip_configuration { name = "${each.value.nic_name}-${each.value.vm_number}-ipconfig" subnet_id = each.value.subnet_value private_ip_address_allocation = "Dynamic" } } resource "azurerm_linux_virtual_machine" "vm-poc" { depends_on = [ azurerm_network_interface.nic-poc] for_each = local.vms.nodes name = "${each.value.vm_name}-${each.value.vm_num}" admin_username = "test-admin" admin_password = "password@29" disable_password_authentication = false location = data.azurerm_resource_group.rg.location resource_group_name = data.azurerm_resource_group.rg.name network_interface_ids = [for nic_key, nic in azurerm_network_interface.nic-poc : nic.id if startswith(nic_key, "${each.key}-")] size = "Standard_B2ms" identity { type = "SystemAssigned" } os_disk { name = "${each.value.vm_name}-${each.value.vm_num}-OSdisk" caching = "ReadWrite" storage_account_type = "Standard_LRS" } source_image_reference { publisher = "RedHat" offer = "RHEL" sku = "82gen2" version = "latest" } } resource "azurerm_lb" "lb" { for_each = local.lbs.tiers name = each.value.lb_name location = data.azurerm_resource_group.rg.location resource_group_name = data.azurerm_resource_group.rg.name sku = "Standard" frontend_ip_configuration { name = each.value.fip_name subnet_id = each.value.subnet_id private_ip_address_allocation = each.value.private_ip_type } } resource "azurerm_lb_backend_address_pool" "bepool" { for_each = local.lbs.tiers loadbalancer_id = azurerm_lb.lb[each.key].id name = each.value.address_pool_name } resource "azurerm_lb_probe" "probe" { for_each = { for lb, details in local.lbs.tiers : lb => details.lb_probes } loadbalancer_id = azurerm_lb.lb[each.key].id name = "ssh-probe" protocol = each.value["ssh_probe"].protocol port = each.value["ssh_probe"].port } resource "azurerm_lb_rule" "rule" { for_each = { for lb, details in local.lbs.tiers : lb => details.lb_rules } loadbalancer_id = azurerm_lb.lb[each.key].id name = "ssh-rule" protocol = each.value["ssh_rule"].protocol frontend_port = each.value["ssh_rule"].frontend_port backend_port = each.value["ssh_rule"].backend_port frontend_ip_configuration_name = azurerm_lb.lb[each.key].frontend_ip_configuration[0].name enable_floating_ip = each.value["ssh_rule"].enable_floating_ip backend_address_pool_ids = [azurerm_lb_backend_address_pool.bepool[each.key].id] probe_id = azurerm_lb_probe.probe[each.key].id }
问题代码及报错
关联资源代码
resource "azurerm_network_interface_backend_address_pool_association" "pool1-1" { for_each = local.vms.nodes network_interface_id = [for nic_key, nic in azurerm_network_interface.nic-poc : nic.id if startswith(nic_key, "${each.key}-")] ip_configuration_name = [for nic_key, nic in azurerm_network_interface.nic-poc : nic.ip_configuration.name if startswith(nic_key, "${each.key}-")] backend_address_pool_id = azurerm_lb_backend_address_pool.bepool[each.key].id }
报错信息
1. 索引无效错误
Error: Invalid index
│
│ on main.tf line 238, in resource "azurerm_network_interface_backend_address_pool_association" "pool1-1":
│ 238: backend_address_pool_id = azurerm_lb_backend_address_pool.bepool[each.key].id
│ ├────────────────
│ │ azurerm_lb_backend_address_pool.bepool is object with 2 attributes
│ │ each.key is "db_node2"
│
│ The given key does not identify an element in this collection value.
2. 属性类型错误
Error: Incorrect attribute value type
│
│ on main.tf line 340, in resource "azurerm_network_interface_backend_address_pool_association" "pool1-1":
│ 340: network_interface_id = [for nic_key, nic in azurerm_network_interface.nic-poc : nic.id if startswith(nic_key, "${each.key}-")]
│ ├────────────────
│ │ azurerm_network_interface.nic-poc is object with 8 attributes
│ │ each.key is "service_node1"
│
│ Inappropriate value for attribute "network_interface_id": string required.Error: Incorrect attribute value type
│
│ on main.tf line 341, in resource "azurerm_network_interface_backend_address_pool_association" "pool1-1":
│ 341: ip_configuration_name = [for nic_key, nic in azurerm_network_interface.nic-poc : nic.ip_configuration.name if startswith(nic_key, "${each.key}-")]
│ ├────────────────
│ │ azurerm_network_interface.nic-poc is object with 8 attributes
│ │ each.key is "service_node2"
│
│ Inappropriate value for attribute "ip_configuration_name": string required.
3. 尝试调整后的报错
│ Error: Invalid index
│
│ on main.tf line 199, in resource "azurerm_network_interface_backend_address_pool_association" "nic_lb_association":
│ 199: ip_configuration_name = azurerm_network_interface.nic-poc[each.key].ip_configuration[0].name
│ ├────────────────
│ │ azurerm_network_interface.nic-poc is object with 8 attributes
│ │ each.key is "db_node1"
│
│ The given key does not identify an element in this collection value.
解决方案
问题分析
- 索引无效错误:
azurerm_lb_backend_address_pool.bepool的键是app-lb和db-lb,但代码用local.vms.nodes的键(如db_node2)索引,两者不匹配,导致找不到对应元素。 - 属性类型错误:
network_interface_id和ip_configuration_name要求字符串类型,但代码返回列表,类型不匹配。 - 调整后的索引错误:
azurerm_network_interface.nic-poc的键是${vm_name}-${nic_name}格式(如db_node1-nic1),直接用db_node1作为键索引找不到对应资源。
正确实现代码
先创建本地映射,明确网卡与后端池的对应关系:
locals { nic_lb_associations = { # app网卡关联app-lb "app-poc-1-nic" = "app-lb" "app-poc-2-nic" = "app-lb" # db网卡关联db-lb "db-nic-1-1-nic" = "db-lb" "db-nic-1-2-nic" = "db-lb" } }
再基于映射创建关联资源:
resource "azurerm_network_interface_backend_address_pool_association" "nic_lb_assoc" { for_each = local.nic_lb_associations # 根据网卡名称匹配对应的nic资源ID network_interface_id = [for nic in azurerm_network_interface.nic-poc : nic.id if nic.name == each.key][0] # 取网卡的第一个IP配置名称(你的网卡均只有一个IP配置) ip_configuration_name = [for nic in azurerm_network_interface.nic-poc : nic.ip_configuration[0].name if nic.name == each.key][0] # 根据映射的后端池名称匹配对应ID backend_address_pool_id = azurerm_lb_backend_address_pool.bepool[each.value].id }
优化方案(自动推导关联)
如果不想硬编码网卡名称,可通过本地配置自动生成关联映射:
locals { nic_lb_associations = merge( # 匹配app开头的网卡,关联app-lb { for nic in azurerm_network_interface.nic-poc : nic.name => "app-lb" if startsWith(nic.name, "app-poc-") }, # 匹配db-nic-1开头的网卡,关联db-lb { for nic in azurerm_network_interface.nic-poc : nic.name => "db-lb" if startsWith(nic.name, "db-nic-1-") } ) } resource "azurerm_network_interface_backend_address_pool_association" "nic_lb_assoc" { for_each = local.nic_lb_associations # 根据网卡名称找到对应的nic资源键 nic_key = [for k, v in azurerm_network_interface.nic-poc : k if v.name == each.key][0] network_interface_id = azurerm_network_interface.nic-poc[nic_key].id ip_configuration_name = azurerm_network_interface.nic-poc[nic_key].ip_configuration[0].name backend_address_pool_id = azurerm_lb_backend_address_pool.bepool[each.value].id }
内容的提问来源于stack exchange,提问作者Uday Kiran

