如何在来自多个来源的资源组ID上以Terraform运行for_each?

时间:2020-01-26 17:31:42

标签: terraform

我首先开始使用以下代码:

resource "azurerm_role_assignment" "pod_sp" {
    for_each = toset(concat(
        [for component in local.components: tostring(azurerm_resource_group.setup[component].id)],
        [tostring(module.component_remote_state.rg_id)]
        ))

    scope              = each.value
    role_definition_id = data.azurerm_role_definition.contributor.id
    principal_id       = azuread_service_principal.pod_sp.id
}

它给了我这个

Error: Invalid for_each set argument

  on ..\..\modules\bootstrap\to_inject.tf line 58, in resource "azurerm_role_assignment" "pod_sp":
  58:     for_each = toset(concat(
  59:         [for component in local.components: tostring(azurerm_resource_group.setup[component].id)],
  60:         [tostring(module.component_remote_state.rg_id)]
  61:         ))

The given "for_each" argument value is unsuitable: "for_each" supports maps
and sets of strings, but you have provided a set containing type dynamic.

然后我发现https://github.com/hashicorp/terraform/issues/22437并将代码更改为:

resource "azurerm_role_assignment" "pod_sp" {
    for_each = {for k in concat(
        [for component in local.components: tostring(azurerm_resource_group.setup[component].id)],
        [tostring(module.component_remote_state.rg_id)]
        ): k => k}

    scope              = each.value
    role_definition_id = data.azurerm_role_definition.contributor.id
    principal_id       = azuread_service_principal.pod_sp.id
}

这是给我的:

Error: Invalid for_each argument


  on ..\..\modules\bootstrap\to_inject.tf line 59, in resource "azurerm_role_assignment" "pod_sp":
  59:     for_each = {for k in concat(
  60:         [for component in local.components: tostring(azurerm_resource_group.setup[component].id)],
  61:         [tostring(module.component_remote_state.rg_id)]
  62:         ): k => k}

The "for_each" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the for_each depends on.

我不明白为什么local.components广为人知时为什么会说“无法预测”:

locals {
    components = toset(["web", "data"])
}

是否可以使其运行而无需先运行目标应用程序?

1 个答案:

答案 0 :(得分:0)

此处不可预测的部分是id中的azurerm_resource_group.setup属性值。由于您将这些键用作地图中的某些键,因此结果是一个其键集不完全清楚的地图,因此Terraform无法确定该图中最终将包含多少个元素以及它们的所有键将是。

为完成这项工作,我建议改用local.components中的字符串作为键,因为您注意到这些在您的配置中是常量,因此可以保证在计划期间被知道:

  for_each = merge(
    { for component in local.components : component => azurerm_resource_group.setup[component].id },
    { "from_remote_state" = module.component_remote_state.rg_id },
  )

上面的方法假设local.components永远不会包含字符串from_remote_state,因此可以安全地用作特殊的组件名称来处理与其他名称不同的额外值。由于您比我更了解此要求,因此您可能会在这里找到一个更合适的名称,但是此处的总体思路是生成一个映射,即使某些值不知道,其键也众所周知:

{
  "web": (known after apply),
  "data": (known after apply),
  "from_remote_state": "<your known rg id from the remote state>",
}

由此,Terraform可以看到您打算创建多少个资源实例以及它们的地址必须是什么:

  • azurerm_role_assignment.pod_sp["web"]
  • azurerm_role_assignment.pod_sp["data"]
  • azurerm_role_assignment.pod_sp["from_remote_state"]