将变量(地图)转换/组合为地形中的局部变量

transforming / combining variables (maps) into locals in terraform

为了简化变量定义,我将子网和属性(如安全组)分组到字典列表中......然后有一层字典等......(或maps/objects)

这是一个示例定义 JSON:

"subnets": [
{
  "name": "inside",
  "prefix": "10.248.0.0/28",
  "route_table": true,
  "nsg": true,
  "security_group_rules": [
    {
      "name": "AllowAllInbound",
      "properties": {
        "priority": 100,
        "access": "Allow",
        "direction": "Inbound",
        "protocol": "*",
        "sourcePortRange": "*",
        "destinationPortRange": "*",
        "sourceAddressPrefix": "*",
        "destinationAddressPrefix": "*"
      }
    }
  ]
}

对于这个单一的块,我希望能够 运行 通过循环构建子网,然后使用不同的资源块构建安全组。我遇到了各种各样的问题,无法对此进行迭代。

我想我应该和当地人一起做点什么,这样我就可以为每次操作提取我需要的东西

在子网端:

resource "azurerm_subnet" "subnet" {
  for_each = var.subnets
  name                 = each.value.name
  resource_group_name  = azurerm_resource_group.hub_network_rg.name
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  address_prefixes     = [each.value.prefix] 
}

由于变量类型的问题而不起作用:我将 'subnets' 变量定义为:

variable "subnets" {
  type = list(object({
    name = string
    prefix = string
    route_table = bool
    nsg = bool
    security_group_rules = map(list(string))
  }))
}

我知道 TF 是松散类型的,但它不想遍历对象,因为它遇到了其他事情。

选项: 1.break 将其分解为单独的元素 - 我将再次 运行 进行分解,只是规模较小。 2. 找到一种方法来创建一个 local.subnets 地图和一个 local.security_groups 地图,该地图引用了我拥有的 var.subnets。

正在寻求有关 #2 的帮助。

类似于:

locals {
  subnets = for k,v in var.subnets: [
    {
      name = var.subnet.name
      prefix = var.subnet.prefix
    }

我离题太远了,但我认为这已经解决了问题。我的大脑开始 Python 和 list/dict 理解,但我可以弄清楚如何使用 TF 做到这一点。感觉就在眼前,就是拼不出来语法。

Terraform for_each 只接受字符串集或字符串映射。传递 var 之类的列表之类的东西根本行不通。我建议做的是:

# Create a JSON like variable for your subnets. Notice, this variable is a list, the type of it is inferred, we don't need to complicate ourselves here
variable "subnets" {
  default = [{
    "name" : "inside",
    "prefix" : "10.248.0.0/28",
    "route_table" : true,
    "nsg" : true,
    "security_group_rules" : [
      {
        "name" : "AllowAllInbound",
        "properties" : {
          "priority" : 100,
          "access" : "Allow",
          "direction" : "Inbound",
          "protocol" : "*",
          "sourcePortRange" : "*",
          "destinationPortRange" : "*",
          "sourceAddressPrefix" : "*",
          "destinationAddressPrefix" : "*"
        }
      }
    ]
  }]
}

# Create a local which maps the name of the the subnet to the subnet itself
locals {
  subnet_map =  { for s in var.subnets: s.name => s }
}

# Create the resources
resource "azurerm_subnet" "subnet" {
  for_each             = toset([
                             for s in var.subnets: s.name
                         ]) # Note, we create a set from the subnet names. We can use these names to read out properties from the map created above
  name                 = each.value.name
  resource_group_name  = azurerm_resource_group.hub_network_rg.name
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  address_prefixes     = [local.subnet_map[each.value].prefix] # We use the subnet names here
}

更新:

我们需要subnet_map的原因是必须要有以下映射:

subnet_map = {
  "inside" = {
    "name" = "inside"
    "nsg" = true
    "prefix" = "10.248.0.0/28"
    ...
  },
  # you have only one subnet as the input, but if you had more than one the map will contain it 
  "other_subnet" = {
  ... 
  }
}

本质上,我们要将每个子网的name映射到子网本身。正如我上面所说,我们需要它的原因是 for_each 只接受字符串集。此 toset([for s in var.subnets: s.name]) 的值是一组子网名称,因此 set("inside"),这意味着 for_each 将遍历子网名称并为每个子网名称创建一个 azurerm_subnet .显然,我们需要有关子网的更多信息,而不仅仅是其名称。我们可以从 subnet_map 中获取我们需要的所有信息。这是因为如果我们提供子网的名称,我们可以从中获取一个对象。这正是该语句的作用:local.subnet_map[each.value].prefix