如何使用 for 循环连接 Terraform 输出中的字符串?
How to concatenate strings in Terraform output with for loop?
我有多个 aws_glue_catalog_table
资源,我想创建一个 output
循环遍历所有资源以显示每个资源的 S3 存储桶位置。这样做的目的是测试我是否为 Terratest 中的每个资源使用了正确的 location
(因为它是变量的串联)。我不能使用 aws_glue_catalog_table.*
或 aws_glue_catalog_table.[]
,因为 Terraform 不允许在不指定名称的情况下引用资源。
所以我用 r1
、r2
、rx
创建了一个 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
中的所有 table 位置,然后使用 Terratest 对其进行测试?
一旦我可以遍历 tables 并收集 S3 位置,我就可以使用 Terratest 进行以下测试:
athenaTablesLocation := terraform.Output(t, terraformOpts, "athena_tables_location")
assert.Contains(t, athenaTablesLocation, "s3://rX/test-config/rX",)
您似乎在这里混合了静态和动态的不同寻常:您已经静态定义了固定数量的 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.tables
,local.tables
依赖all相关资源,等等评估顺序将是正确的。
但是,您的 table 定义似乎也是基于 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
将所有 table 一起表示为具有多个实例的单个资源,每个实例都由 table 名称标识。 for_each
资源以映射的形式出现在表达式中,因此这将声明具有如下地址的资源实例:
aws_glue_catalog_table.all["r1"]
aws_glue_catalog_table.all["r2"]
aws_glue_catalog_table.all["r3"]
- ...
因为这已经是一个地图,这次我们不需要在本地值中构建地图的额外步骤,而是直接访问这个地图来构建输出值,这将是一个地图从 table 名称到存储位置:
{
r1 = "s3://BUCKETNAME/ENVNAME-config/r1"
r2 = "s3://BUCKETNAME/ENVNAME-config/r2"
r3 = "s3://BUCKETNAME/ENVNAME-config/r3"
# ...
}
在这个例子中,我假设所有 table 除了它们的名字之外都是相同的,我预计这在实践中是不正确的,但我只是根据你在问题中包含的内容.如果 tables do 需要不同的设置,那么您可以将 var.table_names
更改为 variable "tables"
,其类型是对象类型的映射其中的值描述了 table 之间的差异,但这是一个不同的主题,有点超出了这个问题的范围,所以我不会在这里详细介绍。
我有多个 aws_glue_catalog_table
资源,我想创建一个 output
循环遍历所有资源以显示每个资源的 S3 存储桶位置。这样做的目的是测试我是否为 Terratest 中的每个资源使用了正确的 location
(因为它是变量的串联)。我不能使用 aws_glue_catalog_table.*
或 aws_glue_catalog_table.[]
,因为 Terraform 不允许在不指定名称的情况下引用资源。
所以我用 r1
、r2
、rx
创建了一个 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
中的所有 table 位置,然后使用 Terratest 对其进行测试?
一旦我可以遍历 tables 并收集 S3 位置,我就可以使用 Terratest 进行以下测试:
athenaTablesLocation := terraform.Output(t, terraformOpts, "athena_tables_location")
assert.Contains(t, athenaTablesLocation, "s3://rX/test-config/rX",)
您似乎在这里混合了静态和动态的不同寻常:您已经静态定义了固定数量的 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.tables
,local.tables
依赖all相关资源,等等评估顺序将是正确的。
但是,您的 table 定义似乎也是基于 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
将所有 table 一起表示为具有多个实例的单个资源,每个实例都由 table 名称标识。 for_each
资源以映射的形式出现在表达式中,因此这将声明具有如下地址的资源实例:
aws_glue_catalog_table.all["r1"]
aws_glue_catalog_table.all["r2"]
aws_glue_catalog_table.all["r3"]
- ...
因为这已经是一个地图,这次我们不需要在本地值中构建地图的额外步骤,而是直接访问这个地图来构建输出值,这将是一个地图从 table 名称到存储位置:
{
r1 = "s3://BUCKETNAME/ENVNAME-config/r1"
r2 = "s3://BUCKETNAME/ENVNAME-config/r2"
r3 = "s3://BUCKETNAME/ENVNAME-config/r3"
# ...
}
在这个例子中,我假设所有 table 除了它们的名字之外都是相同的,我预计这在实践中是不正确的,但我只是根据你在问题中包含的内容.如果 tables do 需要不同的设置,那么您可以将 var.table_names
更改为 variable "tables"
,其类型是对象类型的映射其中的值描述了 table 之间的差异,但这是一个不同的主题,有点超出了这个问题的范围,所以我不会在这里详细介绍。