使用 Terraform 基于一组变量创建多个资源

Using terraform to create multiple resources based on a set of variables

我在terraform.tfvars中有一组变量:

resource_groups = {
    cow = { 
        name     = "Cow"
        location = "eastus" 
    },    
    horse = { 
        name     = "Horse"
        location = "eastus" 
    },    
    chicken = { 
        name     = "Chicken"
        location = "westus2" 
    },    
}

我的 main.tf 看起来像这样:


...
module "myapp" {
 source = "./modules/myapp"
 resource_groups = var.resource_groups
}


variable "resource_groups" {}
...

./modules/myapp.main.tf 看起来像这样:

module "resource_group" {
  source = "../myapp.resource_group"
  resource_groups = var.resource_groups

  for_each = {
  for key, value in try(var.resource_groups, {}) : key => value
  if try(value.reuse, false) == false
  }
  
}

variable "resource_groups" {}

../myapp.resource_group 看起来像这样:

resource "azurerm_resource_group" "resource_group" {
name      = var.resource_groups.cow.name
location  = var.resource_groups.cow.location

}

variable "resource_groups" {}

我希望在 terraform plan 之后我会看到三个新的 RG 将被设置为添加。事实上我确实得到了三个新的,但它们都使用牛 RG 的名称和位置,因为我指定了 var.resource_groups.cow.name 问题是我已经尝试了各种不同的迭代器来代替 .cow。而且我无法让 terraform 使用 terraform.tfvars 文件中的其他变量。我试过方括号、星号和其他通配符。我卡住了。

我希望在一个地方定义一个资源,然后使用它为每个变量映射创建该资源的多个实例。

非常感谢指导。

谢谢。

比尔

对于这种情况,您需要决定您的模块是代表一个资源组还是代表多个资源组。对于一个无论如何只包含一个资源的模块来说,这个决定并不是特别重要,但我假设你把它分解到一个单独的模块中,因为除了单个资源组资源之外还有更多的东西,所以你可以在两者之间做出决定这两个基于此模块代表的其他内容:您要重复此模块中的所有内容,还是仅重复资源组资源?


如果您需要模块代表单个资源组,那么您应该更改其输入变量以仅获取有关单个资源组的数据,然后在您的调用中传递当前资源组的数据module块。

模块内部:

variable "resource_group" {
  type = object({
    name     = string
    location = string
  })
}

resource "azurerm_resource_group" "resource_group" {
  name      = var.resource_group.name
  location  = var.resource_group.location
}

调用模块时:

variable "resource_groups" {
  type = map(
    object({
      name     = string
      location = string
    })
  )
}

module "resource_group" {
  source   = "../myapp.resource_group"
  for_each = var.resource_groups

  # each.value is the value of the current
  # element of var.resource_groups, and
  # so it's just a single resource group.
  resource_group = each.value
}

使用此策略,您将使用以下地址声明资源实例,表明重复发生在整个模块级别而不是其中的单个资源:

  • module.resource_group["cow"].azurerm_resource_group.resource_group
  • module.resource_group["horse"].azurerm_resource_group.resource_group
  • module.resource_group["chicken"].azurerm_resource_group.resource_group

如果您需要模块来表示完整的资源组集,那么模块会将资源组的完整映射作为输入变量而不是 使用for_eachmodule 块上。 for_each 参数将属于嵌套资源。

模块内部:

variable "resource_groups" {
  type = map(
    object({
      name     = string
      location = string
    })
  )
}

resource "azurerm_resource_group" "resource_group" {
  for_each = var.resource_groups

  name      = each.value.name
  location  = each.value.location
}

调用模块时:

variable "resource_groups" {
  type = map(
    object({
      name     = string
      location = string
    })
  )
}

module "resource_group" {
  source   = "../myapp.resource_group"

  # NOTE: No for_each here, because we need only
  # one instance of this module which will itself
  # then contain multiple instances of the resource.

  resource_group = var.resource_groups
}

使用此策略,您将使用以下地址声明资源实例,表明模块只有一个实例,但其中有多个资源实例:

  • module.resource_group.azurerm_resource_group.resource_group["cow"]
  • module.resource_group.azurerm_resource_group.resource_group["horse"]
  • module.resource_group.azurerm_resource_group.resource_group["chicken"]

从您分享的信息中不清楚哪种策略更适合您的情况,因为您将此模块描述为好像它只是 azurerm_resource_group 资源类型的薄包装器,并且因此并不清楚这个模块 代表什么 ,以及为什么它比在根模块中只写一个内联 resource "azurerm_resource_group" 块更有帮助。

在考虑上述哪种设计最适合您的 use-case 时,我建议考虑 Terraform 文档中 When to Write a Module 中的建议。编写一个仅包含单个 resource 块的模块可能没问题,但这通常用于更复杂的资源类型,其中模块 hard-codes 一些本地约定,因此它们不需要 re-specified 整个组织的 Terraform 配置。

如果您只是将值直接传递给资源参数,而没有额外的转换和额外的 hard-coded 设置,那么这表明该模块没有用,而且编写起来会更简单resource 内联块。