通过 Terraform 输出多个 Azure 应用服务的主体 ID

Output for Principal ID for multiple Azure App Services through Terraform

使用: 地形 v0.12.6 + provider.azurerm v1.37.0

我正在通过 Terraform 创建多个 Azure 应用服务并添加身份块以使该应用成为 AD 应用。

resource "azurerm_app_service" "apiApp" {
  count               = "${length(var.apiName)}"
  name                = "${var.apiName[count.index]}-${var.environment}"
  .
  .
  .

  identity  {
    type  = "SystemAssigned"
  }
}

根据 Terraform 文档,https://www.terraform.io/docs/providers/azurerm/r/app_service.html#principal_id 我们可以通过以下方式在 output.tf 文件中导出应用程序的主体 ID:

${azurerm_app_service.apiApp.identity.0.principal_id}

因为在这种情况下我们有多个应用程序服务,所以我在 output.tf 文件中这样写:

output "ApiAppPrincipalId" {
  value = "${azurerm_app_service.apiApp.*.identity.0.principal_id}"
}

当我执行 terraform plan 时,出现此错误:

Error: Invalid index

  on modules\app-service\output.tf line 10, in output "ApiAppPrincipalId":
  10:   value = "${azurerm_app_service.apiApp.*.identity.0.principal_id}"

The given key does not identify an element in this collection value.

我也不确定 0 指的是什么。请帮忙。

当我们在表达式中引用它时,Azure 提供程序似乎将 identity 块表示为一个列表。知道这一点后,我们可以编写一个表达式,通过访问所有应用程序服务实例中的所有 identity 块来生成原则 ID 的平面列表,这是两级列表:

output "ApiAppPrincipalId" {
  value = flatten([
    for identity in azurerm_app_service.apiApp[*].identity : identity[*].principal_id
  ])
}

上面使用了几种不同的Terraform表达式特征,所以我会分解成更小的部分来解释:

  • azurerm_app_service.apiApp[*].identity 检索每个应用程序服务实例的 identity 属性,生成结果列表。如上所述,identity 块似乎表示为一个列表,因此该表达式的结果是一个 对象列表列表 描述每个 [=12] 的内容=]块。
  • [ for ... : ... ] 表达式通过访问给定列表的每个元素(在本例中为前一个表达式的结果)并计算 : 之后的表达式(结果表达式) 反对它。因为这是对象列表的列表,所以结果表达式中的 identity 是对象列表。
  • identity[*].principal_id 检索表示 identity 块的每个对象的 principal_id 属性,再次生成结果列表。在这种情况下,因为 principal_id 是一个字符串属性,所以结果是一个字符串列表。
  • flatten(...) 函数处理这样一个事实,即我们的 for .... 表达式单独会 return 一个字符串列表列表。 flatten 通过丢弃中间列表嵌套将结果转换为平面字符串列表。结果是所有应用程序服务实例中所有 identity 的所有 principal_id 值。

这与您的问题没有直接关系,但请注意,使用下划线分隔的词来命名对象是 Terraform 中的惯用风格,例如 api_app_principal_id 而不是 ApiAppPrincipalId。对集合值使用复数名称也是惯例,例如 api_app_principal_ids 而不是 api_app_principal_id.

当然,这里的命名不会影响 Terraform 的功能,但是如果您将自己的 Terraform 模块与其他人编写的模块混合使用,使用一致的命名会有所帮助,并提高您的模块对那些正在使用的人的可用性通过在其他地方使用它已经熟悉了 Terraform。

output "api_app_principal_ids" {
  value = flatten([
    for identity in azurerm_app_service.api_app[*].identity : identity[*].principal_id
  ])
}