问题描述
我有一个 common.tfvars 文件,其中定义了一个变量:
bqtable_date_partition = [
{ dataset = "d1",table_name = "d1-t1",part_col = "partition_date",part_type = "DAY",schema_file = "data_tables/d1-t1.json" },{ dataset = "d1",table_name = "d1-t2",part_col = "tran_dt",schema_file = "data_tables/d1-t2.json" },{ dataset = "d2",table_name = "d2-t1",schema_file = "data_tables/d2-t1.json" },]
并且我在 main.tf 文件中使用以下资源定义引用此变量:
resource "google_bigquery_table" "bq_tables_dt_pt" {
count = length(var.bqtable_date_partition)
project = var.project_id
dataset_id = "${var.bqtable_date_partition[count.index].dataset}_${var.env}"
table_id = var.bqtable_date_partition[count.index].table_name
time_partitioning {
type = var.bqtable_date_partition[count.index].part_type
field = var.bqtable_date_partition[count.index].part_col
}
schema = file("${path.module}/tables/${var.bqtable_date_partition[count.index].schema_file}")
depends_on = [google_bigquery_dataset.crte_bq_dataset]
labels = {
env = var.env
ind = "corp"
}
}
我想更改资源定义以使用“for_each”而不是“count”来循环遍历列表:
我从 count 更改为 for_each 的动机是消除对我编写变量“bqtable_date_partition”元素的顺序的依赖
我是这样做的:
resource "google_bigquery_table" "bq_tables_dt_pt" {
for_each = var.bqtable_date_partition
project = var.project_id
dataset_id = "${each.value.dataset}_${var.env}"
table_id = each.value.table_name
time_partitioning {
type = each.value.part_type
field = each.value.part_col
}
schema = file("${path.module}/tables/${each.value.schema_file}")
depends_on = [google_bigquery_dataset.crte_bq_dataset]
labels = {
env = var.env
ind = "corp"
}
}
我按预期收到以下错误:
给定的“for_each”参数值不合适:“for_each” 参数必须是一个映射或一组字符串,并且您提供了一个 字符串映射类型列表的值。
谁能帮助我在资源定义中进行哪些更改才能使用“for_each”?
terraform 版本 - 0.14.x
解决方法
错误说它只接受地图或字符串集。因此,我们必须将输入变量转换为映射或字符串集。
https://www.terraform.io/docs/language/expressions/for.html
resource "google_bigquery_table" "bq_tables_dt_pt" {
for_each = { for index,data_partition in var.bqtable_date_partition : index => data_partition }
project = var.project_id
dataset_id = "${each.value.dataset}_${var.env}"
table_id = each.value.table_name
time_partitioning {
type = each.value.part_type
field = each.value.part_col
}
schema = file("${path.module}/tables/${each.value.schema_file}")
depends_on = [google_bigquery_dataset.crte_bq_dataset]
labels = {
env = var.env
ind = "corp"
}
}
所以基本上,这里我们将 for_each 输入转换为以下格式。并且只从新创建的地图中引用值。
{
"0" = {
"dataset" = "d1"
"part_col" = "partition_date"
"part_type" = "DAY"
"schema_file" = "data_tables/d1-t1.json"
"table_name" = "d1-t1"
}
"1" = {
"dataset" = "d1"
"part_col" = "tran_dt"
"part_type" = "DAY"
"schema_file" = "data_tables/d1-t2.json"
"table_name" = "d1-t2"
}
"2" = {
"dataset" = "d2"
"part_col" = "tran_dt"
"part_type" = "DAY"
"schema_file" = "data_tables/d2-t1.json"
"table_name" = "d2-t1"
}
}
,
使用 for_each
有两个主要要求:
- 对于要声明的每个资源实例,您必须拥有一个包含一个元素的集合。
- 必须有某种方法可以从该集合的每个元素派生唯一标识符,然后 Terraform 将使用该标识符作为唯一实例键。
您的集合似乎满足这两个条件,假设 table_name
是所有这些值中的唯一字符串,因此剩下的就是将集合投影到地图中,以便 Terraform 可以从您打算将 table_name
用作唯一跟踪键的键:
resource "google_bigquery_table" "bq_tables_dt_pt" {
for_each = {
for o in var.bqtable_date_partition : o.table_name => o
}
# ...
}
在这里,我使用 for
expression 将序列投影到映射,其中每个元素由其 table_name
属性中的值标识。
如果您处于能够更改此模块的接口的情况,那么您可以通过更改变量的声明以期望映射而不是列表来简化事情,这将避免对投影和向模块调用者明确表明表 ID 必须是唯一的:
variable "bqtable_date_partition" {
type = map(object({
dataset = string
part_col = string
part_type = string
schema_file = string
}))
}
然后您可以像之前尝试的那样将 var.bqtable_date_partition
直接分配给 for_each
,因为它已经是合适的类型了。但是还需要更改您的调用模块以传递映射值而不是列表值,因此如果您的模块有许多调用者都需要更新以保持兼容,这可能不切实际。