【问题标题】:How to concatenate strings in Terraform output with for loop?如何使用 for 循环连接 Terraform 输出中的字符串?
【发布时间】:2021-12-24 21:37:22
【问题描述】:

我有多个 aws_glue_catalog_table 资源,我想创建一个 output 循环遍历所有资源以显示每个资源的 S3 存储桶位置。这样做的目的是测试我是否为 Terratest 中的每个资源使用了正确的location(因为它是变量的串联)。我不能使用 aws_glue_catalog_table.*aws_glue_catalog_table.[],因为 Terraform 不允许在不指定名称的情况下引用资源。

所以我用r1r2rx 创建了一个variable "table_names"。然后,我可以遍历名称。我想动态创建字符串aws_glue_catalog_table.r1.storage_descriptor[0].location,这样我可以检查location是否正确。

resource "aws_glue_catalog_table" "r1" {
  name          = "r1"
  database_name = var.db_name
  storage_descriptor {
    location      = "s3://${var.bucket_name}/${var.environment}-config/r1"
  }
...
}
resource "aws_glue_catalog_table" "rX" {
  name          = "rX"
  database_name = var.db_name
  storage_descriptor {
    location      = "s3://${var.bucket_name}/${var.environment}-config/rX"
  }
}

variable "table_names" {
  description = "The list of Athena table names"
  type        = list(string)
  default     = ["r1", "r2", "r3", "rx"]
}
output "athena_tables" {
  description = "Athena tables"
  value = [for n in var.table_names : n]
}

第一次尝试:我尝试使用语法 aws_glue_catalog_table.${table} 创建一个 output "athena_tables_location",但确实做到了。

output "athena_tables_location" {
  // HOW DO I ITERATE OVER ALL TABLES?
  value = [for t in var.table_names : aws_glue_catalog_table.${t}.storage_descriptor[0].location"]
}

第二次尝试:我尝试创建 variable "table_name_locations",但 IntelliJ 已经在 for 循环 [for t in var.table_names : "aws_glue_catalog_table.${t}.storage_descriptor[0].location"] 中显示错误 ${t}

variable "table_name_locations" {
  description = "The list of Athena table locations"
  type        = list(string)
  // THIS ALSO DOES NOT WORK
  default     = [for t in var.table_names : "aws_glue_catalog_table.${t}.storage_descriptor[0].location"]
}

如何列出output 中的所有表格位置,然后使用 Terratest 对其进行测试? 一旦我可以遍历表并收集 S3 位置,我就可以使用 Terratest 进行以下测试:

athenaTablesLocation := terraform.Output(t, terraformOpts, "athena_tables_location")
assert.Contains(t, athenaTablesLocation, "s3://rX/test-config/rX",)

【问题讨论】:

  • 试试:aws_glue_catalog_table[t].storage_descriptor[0].location
  • 我必须在aws_glue_catalog_table.[t].storage_descriptor[0].location 后面加上. aws_glue_catalog_table。但是 I get the error 期望得到 '['`
  • 不,[ 之前的点是无效的语法,这就是您收到该错误的原因。因此,您绝对不必在aws_glue_catalog_table 之后执行.。你真的尝试了我的建议吗?
  • 是的,我试过了。如果我不使用.,我会收到错误Unresolved reference storage_descriptor
  • 当我使用 aws_glue_catalog_table[t]. 并执行 Ctrl + 空格时,IntelliJ 建议使用表的名称自动完成 r1 ... rX。 IntelliJ 认为我与没有[t]aws_glue_catalog_table 处于同一水平。顺便说一句,我正在为 IntelliJ 使用 golang 插件。所以我想问题不在于 IntelliJ。它仍然是语法。

标签: terraform terratest


【解决方案1】:

您在这里似乎有一个不寻常的静态和动态混合:您静态定义了固定数量的 aws_glue_catalog_table 资源,但您想根据输入变量的值动态使用它们。

Terraform 不允许动态引用资源,因为它的执行模型需要在所有对象之间构建依赖关系图,因此它需要知道特定表达式中涉及哪些确切的资源。但是,原则上您可以构建自己的包含所有这些对象的单个值,然后从中动态选择:

locals {
  tables = {
    r1 = aws_glue_catalog_table.r1
    r2 = aws_glue_catalog_table.r2
    r3 = aws_glue_catalog_table.r3
    # etc
  }
}

output "table_locations" {
  value = {
    for t in var.table_names : t => local.tables[t].storage_descriptor[0].location
  }
}

通过这种结构,Terraform 可以看到 output "table_locations" 依赖于 local.tableslocal.tables 依赖于所有相关资源,因此评估顺序将是正确的。


但是,您的表定义似乎也是基于var.table_names 的系统化,因此可能会从动态本身中受益。您可以使用 resource for_each 功能来声明单个资源的多个实例:

variable "table_names" {
  description = "Athena table names to create"
  type        = set(string)
  default     = ["r1", "r2", "r3", "rx"]
}

resource "aws_glue_catalog_table" "all" {
  for_each = var.table_names

  name          = each.key
  database_name = var.db_name
  storage_descriptor {
    location      = "s3://${var.bucket_name}/${var.environment}-config/${each.key}"
  }
  ...
}

output "table_locations" {
  value = {
    for k, t in aws_glue_catalog_table.all : k => t.storage_descriptor[0].location
  }
}

在这种情况下,aws_glue_catalog_table.all 将所有表一起表示为具有多个实例的单个资源,每个实例由表名标识。 for_each 资源在表达式中以映射的形式出现,因此这将声明具有如下地址的资源实例:

  • aws_glue_catalog_table.all["r1"]
  • aws_glue_catalog_table.all["r2"]
  • aws_glue_catalog_table.all["r3"]
  • ...

因为这已经是一张地图了,这次我们不需要在本地值中构建地图的额外步骤,而可以直接访问这张地图来构建输出值,这将是一个来自表的地图存储位置的名称:

{
  r1 = "s3://BUCKETNAME/ENVNAME-config/r1"
  r2 = "s3://BUCKETNAME/ENVNAME-config/r2"
  r3 = "s3://BUCKETNAME/ENVNAME-config/r3"
  # ...
}

在此示例中,我假设所有表除了名称之外都是相同的,我希望这在实践中并不正确,但我只是按照您在问题中包含的内容进行了分析。如果表确实需要有不同的设置,那么您可以将var.table_names 更改为variable "tables",其类型是对象类型的映射,其中值描述表之间的差异,但这是一个不同的话题有点超出了这个问题的范围,所以我不会在这里详细介绍。

【讨论】:

  • 谢谢。我会尝试第一种方法。第二个似乎是实现 DRY 的理想选择。但是我的每一张桌子都有不同的列。我不知道如何重构每个表有一组不同的列。
  • 真棒@Martin。第一个解决方案效果很好。奇怪的是,IntelliJ 无法识别表达式 for t in var.table_names : t => local.tables[t].storage_descriptor[0].location 末尾的 storage_descriptor[0].location。它可能是我正在使用的 GO 插件。一开始我很怀疑,但我试了一下,它奏效了!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2022-06-27
  • 1970-01-01
  • 2020-01-16
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多