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

Terraform Flatten序列问题:Traffic Manager端点按索引而非名称创建

问题:Terraform Azure Traffic Manager 端点按索引而非名称创建

当前Terraform Plan输出显示端点以["0"]、["1"]的索引形式创建,而非使用输入中定义的端点名称(如tfendpoint1、tfendpoint2)。需要调整flatten逻辑,确保端点资源按名称进行索引。

现有Plan输出

# module.infra.azurerm_traffic_manager_azure_endpoint.tm_azure_endpoint["0"] will be created
  + resource "azurerm_traffic_manager_azure_endpoint" "tm_azure_endpoint" {
      + enabled            = true
      + geo_mappings       = [
          + "US",
        ]
      + id                 = (known after apply)
      + name               = "0"
      + priority           = 1
      + profile_id         = "/subscriptions/3f128ee2-eef5-4ede-ac7e-42cf4a4f8632/resourceGroups/rg-eastus-tmprofile/providers/Microsoft.Network/trafficManagerProfiles/traf-eastus-01"
      + target_resource_id = "/subscriptions/3f128ee2-eef5-4ede-ac7e-42cf4a4f8632/resourceGroups/rg-eastus-tmprofile/providers/Microsoft.Network/publicIPAddresses/pip-core-vng-eus-01"
      + weight             = 1
    }

  # module.infra.azurerm_traffic_manager_azure_endpoint.tm_azure_endpoint["1"] will be created
  + resource "azurerm_traffic_manager_azure_endpoint" "tm_azure_endpoint" {
      + enabled            = true
      + geo_mappings       = [
          + "US",
        ]
      + id                 = (known after apply)
      + name               = "1"
      + priority           = 1
      + profile_id         = "/subscriptions/3f128ee2-eef5-4ede-ac7e-42cf4a4f8632/resourceGroups/rg-eastus-taf-eastus-01"
      + target_resource_id = (known after apply)
      + weight             = 1
    }

现有代码(MAIN)

locals {
  tm_profile = {
    for k, v in try(local.inputs.tm_profiles, {}) : k => merge(
      {
        existing                     = false
        traffic_view_enabled         = false //optional feature. $2 per million data points processed. Data points are essentially queries into Traffic Manager.
        interval_in_seconds          = 30
        timeout_in_seconds           = 10
        tolerated_number_of_failures = 3
      },
      v,
      {
        tags = merge(
          local.tags,
          try(v.tags, {})
        )
      }
    )
  }
  tmprofile_endpoints = flatten([
    for tm_k, tm_v in try(local.inputs.tm_profiles, {}) : [
      for tm_endpoints_k, tm_endpoints_v in try(tm_v.tm_endpoints, {}) : merge(
        tm_endpoints_v,
        {
          name               = tm_endpoints_k
          profile_id         = azurerm_traffic_manager_profile.traffic_manager_profile[tm_endpoints_v["tm_profile_name"]].id
          target_resource_id = azurerm_public_ip.pub_ips[tm_endpoints_v["pub_ip"]].id
          enabled            = true
          protocol           = tm_v.protocol
        }
      )
    ]
  ])
}

resource "azurerm_traffic_manager_profile" "traffic_manager_profile" {
  for_each = {
    for k, v in local.tm_profile : k => v if !v.existing
  }
  name                   = each.key
  resource_group_name    = each.value.rg
  profile_status         = each.value.profile_status
  traffic_routing_method = each.value.traffic_routing_method
  dns_config {
    relative_name = each.value.relative_name
    ttl           = each.value.ttl
  }
  monitor_config {
    protocol                    = each.value.protocol
    port                        = each.value.port
    path                        = (each.value.protocol != "TCP") ? each.value.path : lookup(each.value, "path", null)
    expected_status_code_ranges = try(each.value.expected_status_code_ranges, "200")
    dynamic "custom_header" {
      for_each = (each.value.protocol == "HTTP" || each.value.protocol == "HTTPS") ? try(each.value.custom_headers, {}) : {}
      content {
        name  = each.value.name
        value = each.value.value
      }
    }
    interval_in_seconds          = each.value.interval_in_seconds
    timeout_in_seconds           = each.value.timeout_in_seconds
    tolerated_number_of_failures = each.value.tolerated_number_of_failures
  }
  tags = each.value.tags
  depends_on = [
    azurerm_resource_group.rgs
  ]
}

resource "azurerm_traffic_manager_azure_endpoint" "tm_azure_endpoint" {
  for_each = {
  for k, v in local.tmprofile_endpoints : k => v }
  name               = each.key
  profile_id         = each.value.profile_id
  target_resource_id = each.value.target_resource_id
  weight             = each.value.weight
  dynamic "custom_header" {
    for_each = (each.value.protocol == "HTTP" || each.value.protocol == "HTTPS") ? try(each.value.custom_headers, {}) : {}
    content {
      name  = each.value.name
      value = each.value.value
    }
  }
  enabled      = each.value.enabled
  geo_mappings = each.value.geo_mappings
  priority     = each.value.priority
  depends_on = [
    azurerm_traffic_manager_profile.traffic_manager_profile
  ]
}
/*
output "tmprofile_endpoints_out" {
  value = local.tmprofile_endpoints
}
*/

示例输入(Sample Input)

"tm_profiles": {
            "traf-eastus-01": {
                "rg": "rg-eastus-tmprofile",
                "profile_status": "Enabled",
                "traffic_routing_method": "Performance",
                "relative_name": "azmech",
                "ttl": 3600,
                "protocol": "HTTPS",
                "port": 443,
                "path": "/",
                "expected_status_code_ranges": [
                    "200-201"
                ],
                "custom_header": {
                    "name": "customheader",
                    "value": null
                },
                "tags": {
                    "tm_profile": "tm_test"
                },
                "tm_endpoints": {
                    "tfendpoint1": {
                        "tm_profile_name": "traf-eastus-01",
                        "weight": "1",
                        "custom_header": {
                            "name": "customheader",
                            "value": null
                        },
                        "geo_mappings": [
                            "US"
                        ],
                        "priority": "1",
                        "pub_ip": "pip-core-vng-eus-01"
                    },
                    "tfendpoint2": {
                        "tm_profile_name": "traf-eastus-01",
                        "weight": "1",
                        "custom_header": {
                            "name": "customheader",
                            "value": null
                        },
                        "geo_mappings": [
                            "US"
                        ],
                        "priority": "1",
                        "pub_ip": "pip-core-vng-wus-01"
                    }
                }
            }
        }

解决方案

问题出在tmprofile_endpoints的flatten处理后得到的是列表,而非带键名的映射。当你用for k, v in local.tmprofile_endpoints时,k是列表的索引(0、1),而非端点名称。

修改后的tmprofile_endpoints局部变量

tmprofile_endpoints = {
  for tm_k, tm_v in try(local.inputs.tm_profiles, {}) :
  for ep_k, ep_v in try(tm_v.tm_endpoints, {}) :
  "${tm_k}-${ep_k}" => merge(
    ep_v,
    {
      name               = ep_k
      profile_id         = azurerm_traffic_manager_profile.traffic_manager_profile[ep_v["tm_profile_name"]].id
      target_resource_id = azurerm_public_ip.pub_ips[ep_v["pub_ip"]].id
      enabled            = true
      protocol           = tm_v.protocol
    }
  )
}

逻辑说明

  • 使用嵌套for循环直接构建映射,键名采用${tm_k}-${ep_k}(配置文件名-端点名称),确保全局唯一,避免不同配置文件下同名端点冲突
  • 无需再用flatten,直接得到以复合名称为键的映射结构

对应调整端点资源的for_each

因为tmprofile_endpoints已经是映射,可直接简化:

resource "azurerm_traffic_manager_azure_endpoint" "tm_azure_endpoint" {
  for_each = local.tmprofile_endpoints
  name               = each.value.name
  profile_id         = each.value.profile_id
  target_resource_id = each.value.target_resource_id
  weight             = each.value.weight
  dynamic "custom_header" {
    for_each = (each.value.protocol == "HTTP" || each.value.protocol == "HTTPS") ? try(each.value.custom_headers, {}) : {}
    content {
      name  = custom_header.value.name
      value = custom_header.value.value
    }
  }
  enabled      = each.value.enabled
  geo_mappings = each.value.geo_mappings
  priority     = each.value.priority
  depends_on = [
    azurerm_traffic_manager_profile.traffic_manager_profile
  ]
}

注意:动态块custom_header中需使用custom_header.value.name而非each.value.name,否则会引用外部each值导致错误。

修改后的Plan效果

端点资源会以["traf-eastus-01-tfendpoint1"]、["traf-eastus-01-tfendpoint2"]的形式创建,资源索引清晰对应输入中定义的端点名称。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 09:00:51