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

如何将YAML列表转换为Terraform动态FortiOS防火墙地址资源

问题描述

我不擅长数据结构与算法,已经编写了一份包含客户端名称、服务、CIDR列表的YAML配置,这个结构更适配不同名称的客户端列表需求。希望基于该YAML在Terraform中创建类似fortios_firewall_address.address["bank_of_bravos-127.0.0.1"]的动态FortiOS防火墙地址资源。尝试通过本地变量将YAML转为Map结构,但在嵌套列表迭代、配置resource的for_each时,出现属性不存在、字符串插值错误等问题,寻求正确实现方法。

YAML配置

---
clients:
  - name: "bank_of_braavos"
    services:
      - "HTTPS"
      - "DNS"
    cidr:
      - "127.0.0.1/32"
      - "127.0.0.2/32"
      - "127.0.0.3/32"
  - name: "tatooine"
    services:
      - "HTTPS"
      - "ICMP"
    cidr:
      - "958.xx.21.1r/aar"
      - "958.xx.21.1g/fwr"

尝试的本地变量配置

locals {
  clients_config = yamldecode(file("clients.yml"))

  clients = {
    for client in local.clients_config.clients :
    client.name => {
      cidr     = client.cidr
      services = client.services
    }
  }
}

尝试的资源配置(报错)

resource "fortios_firewall_address" "clients" {
  for_each = { for client in local.clients : "${client.name}-${client.cidr}" => client }
  allow_routing        = "disable"
  name                 = "Client_${each.client.name}_${each.client.cidr}"
  subnet               = "${each.value.cidr}"
  type                 = "ipmask"
  visibility           = "enable"
}

报错信息

│ Error: Unsupported attribute                                                                                                                                                                                
│ 
│   on policy.tf line 15, in resource "fortios_firewall_address" "clients":
│   15:   for_each = { for client in local.clients : "${client.name}-${client.cidr}" => client }
│ 
│ This object does not have an attribute named "name".
╵
╷
│ Error: Unsupported attribute
│ 
│   on policy.tf line 15, in resource "fortios_firewall_address" "clients":
│   15:   for_each = { for client in local.clients : "${client.name}-${client.cidr}" => client }
│ 
│ This object does not have an attribute named "name".
╵
╷
│ Error: Invalid template interpolation value
│ 
│   on policy.tf line 15, in resource "fortios_firewall_address" "clients":
│   15:   for_each = { for client in local.clients : "${client.name}-${client.cidr}" => client }
│     ├────────────────
│     │ client.cidr is tuple with 3 elements
│ 
│ Cannot include the given value in a string template: string required.
解决方法

核心问题是未正确展开嵌套的CIDR列表,同时遍历local.clients时混淆了Map键值的结构。以下是修正后的完整实现:

1. 修正本地变量(生成扁平化的CIDR条目)

需要将每个客户端的CIDR列表展开,生成「客户端名称+单个CIDR」的独立条目,确保每个防火墙地址资源有唯一标识:

locals {
  clients_config = yamldecode(file("clients.yml"))

  # 扁平化数据:把每个客户端的CIDR拆分为独立键值对
  client_cidrs = merge([
    for client in local.clients_config.clients : {
      for cidr in client.cidr :
      "${client.name}-${cidr}" => {
        client_name = client.name
        cidr        = cidr
        services    = client.services
      }
    }
  ]...)
}

通过嵌套for表达式+merge函数,将原数据中的多CIDR条目拆分为单个条目,键格式与你期望的bank_of_bravos-127.0.0.1一致。

2. 修正防火墙地址资源配置

直接使用扁平化后的local.client_cidrs作为for_each数据源:

resource "fortios_firewall_address" "clients" {
  for_each = local.client_cidrs

  allow_routing = "disable"
  name          = "Client_${each.value.client_name}_${replace(each.value.cidr, "/", "_")}"
  subnet        = each.value.cidr
  type          = "ipmask"
  visibility    = "enable"
}
  • for_each = local.client_cidrs:直接使用扁平化后的Map,每个键都是唯一的「客户端名称-CIDR」组合
  • name字段用replace将CIDR中的/替换为_,避免生成非法资源名称(多数系统不允许名称含/)
  • subnet直接引用each.value.cidr,确保是单个字符串而非列表

关键错误分析

  1. 属性不存在错误:你之前的local.clients是以客户端名称为键的Map,遍历它时client是Map的值(仅包含cidr和services),没有name属性,因此报错。
  2. 字符串插值错误:client.cidr是列表类型,无法直接插入字符串,必须先展开为单个CIDR条目。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 04:06:01