Terraform 输出:如何创建(地图?)以及如何使用地图?地图是正确的工具吗?

Terraform Outputs: How do I create a (map?) and how do I use the map? Is a map even the right tool?

我正在为一些 terraform 概念苦苦挣扎。

我成功地使用 aztfmod/azurecaf 提供程序来命名我的资源组,但这意味着我需要将该名称作为 companynet.resource_group 模块的输出,以便我可以再次使用该名称调用 companynet.key_vault 模块时。

# terraform.tfvars

resource_groups = {
  rg1 = { 
    name     = "resourcegroup1"
    location = "eastus"
  }
  rg2 = { 
    name     = "resourcegroup2"
    location = "eastus"
  }
}
# root main.tf

provider "azurerm" {
  features {}
}

module "companynet" {
  source = "./modules/companynet"

  tenant_id         = var.tenant_id
  environment       = var.environment
  resource_groups   = var.resource_groups
  key_vaults        = var.key_vaults
  storage_accounts  = var.storage_accounts
  app_service_plans = var.app_service_plans
}
# modules/companynet/main.tf

module "resource_group" {
  source          = "../companynet.resource_group"
  environment     = var.environment
  resource_groups = var.resource_groups
}

module "key_vault" {
 source          = "../companynet.key_vault"
 tenant_id       = var.tenant_id
 environment     = var.environment
 resource_groups = "${module.resource_group.resource_groups.companynet}"
 key_vaults      = var.key_vaults
 
}

模块 resource_group 具有以下 main.tf:

# modules/companynet.resource_group/main.tf

resource "azurecaf_name" "resource_group" {
  for_each      = var.resource_groups
  name          = each.value.name
  resource_type = "azurerm_resource_group"
  suffixes      = ["${var.environment}", "001"]
}

resource "azurerm_resource_group" "resource_group" {
  for_each = var.resource_groups
  name     = azurecaf_name.resource_group[each.key].result
  location = each.value.location

}

但我不知道如何获得那个 resource_group 名称的输出。

我尝试了一些不同的方法,但都不起作用

# modules/companynet.resource_group/outputs.tf

output "resource_groups" {
 value = azurerm_resource_group.resource_group[*].name
}

value = azurerm_resource_group.resource_group.name

value = azurerm_resource_group.resource_group.companynet.name

value = azurerm_resource_group.resource_group[companynet].name

每一个都会导致一个或另一个错误,都表明 modules/companynet.resource_group/outputs.tf

有问题

理想情况下,我会得到一个对象,然后我可以在另一个模块中对其进行迭代。我希望能够调用类似的东西来访问其他模块中的那些资源组名称,例如:

# modules/companynet.key_vault/main.tf

resource "azurerm_key_vault" "key_vault" {
  for_each            = var.key_vaults
  name                = azurecaf_name.key_vault[each.key].result
  location            = var.resource_groups.location

  resource_groups     = "${module.resource_group.resource_groups.[companynet]}"

  sku_name            = "standard"
  tenant_id           = var.tenant_id
}

azurerm_resource_group.resource_group 是用 for_each 声明的,因此该表达式引用一个对象映射,其中键与 for_each 表达式的键匹配,值是相应声明的资源实例。

References to Resource Attributes中有各种在不同情况下引用资源属性的例子,包括以下关于使用for_each的资源:

When a resource has the for_each argument set, the resource itself becomes a map of instance objects rather than a single object, and attributes of instances must be specified by key, or can be accessed using a for expression.

  • aws_instance.example["a"].id returns the id of the "a"-keyed resource.
  • [for value in aws_instance.example: value.id] returns a list of all of the ids of each of the instances.

第二项显示了如何使用 for 表达式生成 aws_instance.example 的 ID 的 list,但它没有准确显示如何生成地图,而是希望您参考有关 for 表达式的链接文档以了解相关信息:

The type of brackets around the for expression decide what type of result it produces.

The above example uses [ and ], which produces a tuple. If you use { and } instead, the result is an object and you must provide two result expressions that are separated by the => symbol:

{for s in var.list : s => upper(s)}

This expression produces an object whose attributes are the original elements from var.list and their corresponding values are the uppercase versions. For example, the resulting value might be as follows:

{
  foo = "FOO"
  bar = "BAR"
  baz = "BAZ"
}

A for expression alone can only produce either an object value or a tuple value, but Terraform's automatic type conversion rules mean that you can typically use the results in locations where lists, maps, and sets are expected.

本节介绍如何生成对象,然后说明您可以在需要地图的位置使用结果。在实践中,通常可以在 Terraform 中互换使用 object-typed 值和 mapped-type 值,因为它们的共同点是它们都具有由字符串键标识的元素。不同之处在于对象类型的每个属性都可以有一个单独的类型,而映射的所有属性必须有相同的类型。

根据所有这些信息,我们可以生成一个 object 值来描述每个资源组的名称,如下所示:

output "resource_groups" {
  value = { for k, g in azurerm_resource_group.resource_group : k => g.name }
}

对于大多数目的来说,这是一个 object-typed 结果而不是一个特定的地图并不重要,但是因为我们知道 .name 总是一个字符串,我们可以推断出所有该对象的属性具有 string-typed 值,因此使用 the tomap function 显式转换为字符串映射也是有效的(这是 [...] 映射 [.. .] 是预期的”,根据上述文件):

output "resource_groups" {
  value = tomap({
    for k, g in azurerm_resource_group.resource_group : k => g.name
  })
}