需要对 Terraform 资源进行多次计数?
Needing multiple counts on a Terraform resource?
我有以下 Terraform 资源,它旨在为每个仓库创建一个单独的加密 EFS 卷,然后在两个子网中为每个创建挂载目标:
resource "aws_efs_file_system" "efs-data-share" {
count = "${length(var.warehouses)}"
encrypted = "${var.encrypted}"
kms_key_id = "${element(data.aws_kms_key.encryption_key.*.arn,count.index)}"
performance_mode = "${var.performance_mode}"
tags {
Name = "${element(split(".",var.warehouses[count.index]),0)}-${var.name_suffix}"
Warehouse = "${element(split(".",var.warehouses[count.index]),0)}"
Environment = "${var.environment}"
Purpose = "${var.purpose}"
}
}
resource "aws_efs_mount_target" "mounts" {
count = "${length(var.subnets)}"
file_system_id = "${aws_efs_file_system.efs-data-share.*.id}"
subnet_id = "${element(var.subnets, count.index)}"
security_groups = ["${var.efs_security_groups}"]
}
data "aws_kms_key" "encryption_key" {
count = "${length(var.warehouses)}"
key_id = "alias/${element(split(".",var.warehouses[count.index]),0)}-${var.key_alias_suffix}"
}
EFS 本身启动正常,但安装失败,因为 file_system_id 必须是单个资源,而不是列表。
* module.prod_multi_efs.aws_efs_mount_target.mounts[1]: file_system_id must be a single value, not a list
* module.prod_multi_efs.aws_efs_mount_target.mounts[0]: file_system_id must be a single value, not a list
我不确定在 Terraform 语法方面我的资源是什么。在每种情况下我都需要两个安装,所以我计算的是子网变量的长度,为此我传入了 subnet-123456 和 subnet-567890。我怎样才能让系统为每个制作的 EFS(按仓库列表的长度总共七个)制作两个安装件,而不能在 aws_efs_mount_target?
中使用第二个计数
这是我调用模块的方式:
module "prod_multi_efs" {
source = "../../../../Modules/Datastore/MultiEFS"
efs_security_groups = ["${aws_security_group.prod-efs-group.id}"]
environment = "Prod"
name_suffix = "${module.static_variables.prod_efs_name}"
key_alias_suffix = "prod-efs-${module.static_variables.account_id}-us-east-1"
subnets = ["${module.prod_private_subnet_1.subnet_id}", "${module.prod_private_subnet_2.subnet_id}"]
warehouses = "${module.static_variables.warehouses}" #The list of seven warehouses
}
这是我的计划,尝试根据下面的答案添加 [count.index] - 它执行两次安装,但仅安装到 7 个 FS ID 中的前 2 个:
+ module.stg_multi_efs.aws_efs_mount_target.mounts[0]
id: <computed>
dns_name: <computed>
file_system_arn: <computed>
file_system_id: "fs-123456"
ip_address: <computed>
network_interface_id: <computed>
security_groups.#: "1"
security_groups.4286231943: "sg-xxxxxxxxxx"
subnet_id: "subnet-aaaaaaaa"
+ module.stg_multi_efs.aws_efs_mount_target.mounts[1]
id: <computed>
dns_name: <computed>
file_system_arn: <computed>
file_system_id: "fs-567890"
ip_address: <computed>
network_interface_id: <computed>
security_groups.#: "1"
security_groups.4286231943: "sg-xxxxxxxxxxx"
subnet_id: "subnet-bbbbbbbbbb"
Terraform 0.12 引入了一个新函数 setproduct
用于需要一组对象来表示来自两个或多个集合的元素的每个组合的情况:
locals {
data_share_subnets = [
for pair in setproduct(aws_efs_file_system.efs-data-share, var.subnets) : {
file_system_id = pair[0].id
subnet_id = pair[1]
}
]
}
上面将 local.data_share_subnets
声明为这样的数据结构:
[
{"file_system_id": "fs-123456", "subnet_id": "subnet-aaaaaaaa"},
{"file_system_id": "fs-123456", "subnet_id": "subnet-bbbbbbbb"}
{"file_system_id": "fs-567890", "subnet_id": "subnet-aaaaaaaa"},
{"file_system_id": "fs-567890", "subnet_id": "subnet-bbbbbbbb"}
]
然后您可以将其与 0.12.6 中引入的新 for_each
功能一起使用,以在该集合中为每个条目声明一个 aws_efs_mount_target
实例:
resource "aws_efs_mount_target" "mounts" {
for_each = { for o in local.data_share_subnets : "${o.file_system_id}:${o.subnet_id}" => o }
file_system_id = each.value.file_system_id
subnet_id = each.value.subnet_id
security_groups = var.efs_security_groups
}
for_each
参数中的 for
表达式将上面显示的列表转换为一个映射,其键类似于 "fs-123456:subnet-aaaaaaaa"
。 Terraform 然后将使用如下地址跟踪这些实例:
aws_efs_mount_target.mounts["fs-123456:subnet-aaaaaaaa"]
aws_efs_mount_target.mounts["fs-123456:subnet-bbbbbbbb"]
...这意味着您可以安全地添加和删除单个文件系统和子网,而不会干扰任何其他不相关的挂载目标实例。 (如果使用 count
,则情况并非如此,因为实例将由它们在列表中的位置来标识。)
Terraform 0.11 或更早版本中没有与上述内容直接等效的内容。
如果我这样做,我会收到以下错误
"for_each" 值取决于无法确定的资源属性
until apply,因此 Terraform 无法预测将创建多少个实例。
要解决此问题,请使用 -target 参数首先仅应用
for_each 依赖的资源。"
我有以下 Terraform 资源,它旨在为每个仓库创建一个单独的加密 EFS 卷,然后在两个子网中为每个创建挂载目标:
resource "aws_efs_file_system" "efs-data-share" {
count = "${length(var.warehouses)}"
encrypted = "${var.encrypted}"
kms_key_id = "${element(data.aws_kms_key.encryption_key.*.arn,count.index)}"
performance_mode = "${var.performance_mode}"
tags {
Name = "${element(split(".",var.warehouses[count.index]),0)}-${var.name_suffix}"
Warehouse = "${element(split(".",var.warehouses[count.index]),0)}"
Environment = "${var.environment}"
Purpose = "${var.purpose}"
}
}
resource "aws_efs_mount_target" "mounts" {
count = "${length(var.subnets)}"
file_system_id = "${aws_efs_file_system.efs-data-share.*.id}"
subnet_id = "${element(var.subnets, count.index)}"
security_groups = ["${var.efs_security_groups}"]
}
data "aws_kms_key" "encryption_key" {
count = "${length(var.warehouses)}"
key_id = "alias/${element(split(".",var.warehouses[count.index]),0)}-${var.key_alias_suffix}"
}
EFS 本身启动正常,但安装失败,因为 file_system_id 必须是单个资源,而不是列表。
* module.prod_multi_efs.aws_efs_mount_target.mounts[1]: file_system_id must be a single value, not a list
* module.prod_multi_efs.aws_efs_mount_target.mounts[0]: file_system_id must be a single value, not a list
我不确定在 Terraform 语法方面我的资源是什么。在每种情况下我都需要两个安装,所以我计算的是子网变量的长度,为此我传入了 subnet-123456 和 subnet-567890。我怎样才能让系统为每个制作的 EFS(按仓库列表的长度总共七个)制作两个安装件,而不能在 aws_efs_mount_target?
中使用第二个计数这是我调用模块的方式:
module "prod_multi_efs" {
source = "../../../../Modules/Datastore/MultiEFS"
efs_security_groups = ["${aws_security_group.prod-efs-group.id}"]
environment = "Prod"
name_suffix = "${module.static_variables.prod_efs_name}"
key_alias_suffix = "prod-efs-${module.static_variables.account_id}-us-east-1"
subnets = ["${module.prod_private_subnet_1.subnet_id}", "${module.prod_private_subnet_2.subnet_id}"]
warehouses = "${module.static_variables.warehouses}" #The list of seven warehouses
}
这是我的计划,尝试根据下面的答案添加 [count.index] - 它执行两次安装,但仅安装到 7 个 FS ID 中的前 2 个:
+ module.stg_multi_efs.aws_efs_mount_target.mounts[0]
id: <computed>
dns_name: <computed>
file_system_arn: <computed>
file_system_id: "fs-123456"
ip_address: <computed>
network_interface_id: <computed>
security_groups.#: "1"
security_groups.4286231943: "sg-xxxxxxxxxx"
subnet_id: "subnet-aaaaaaaa"
+ module.stg_multi_efs.aws_efs_mount_target.mounts[1]
id: <computed>
dns_name: <computed>
file_system_arn: <computed>
file_system_id: "fs-567890"
ip_address: <computed>
network_interface_id: <computed>
security_groups.#: "1"
security_groups.4286231943: "sg-xxxxxxxxxxx"
subnet_id: "subnet-bbbbbbbbbb"
Terraform 0.12 引入了一个新函数 setproduct
用于需要一组对象来表示来自两个或多个集合的元素的每个组合的情况:
locals {
data_share_subnets = [
for pair in setproduct(aws_efs_file_system.efs-data-share, var.subnets) : {
file_system_id = pair[0].id
subnet_id = pair[1]
}
]
}
上面将 local.data_share_subnets
声明为这样的数据结构:
[
{"file_system_id": "fs-123456", "subnet_id": "subnet-aaaaaaaa"},
{"file_system_id": "fs-123456", "subnet_id": "subnet-bbbbbbbb"}
{"file_system_id": "fs-567890", "subnet_id": "subnet-aaaaaaaa"},
{"file_system_id": "fs-567890", "subnet_id": "subnet-bbbbbbbb"}
]
然后您可以将其与 0.12.6 中引入的新 for_each
功能一起使用,以在该集合中为每个条目声明一个 aws_efs_mount_target
实例:
resource "aws_efs_mount_target" "mounts" {
for_each = { for o in local.data_share_subnets : "${o.file_system_id}:${o.subnet_id}" => o }
file_system_id = each.value.file_system_id
subnet_id = each.value.subnet_id
security_groups = var.efs_security_groups
}
for_each
参数中的 for
表达式将上面显示的列表转换为一个映射,其键类似于 "fs-123456:subnet-aaaaaaaa"
。 Terraform 然后将使用如下地址跟踪这些实例:
aws_efs_mount_target.mounts["fs-123456:subnet-aaaaaaaa"]
aws_efs_mount_target.mounts["fs-123456:subnet-bbbbbbbb"]
...这意味着您可以安全地添加和删除单个文件系统和子网,而不会干扰任何其他不相关的挂载目标实例。 (如果使用 count
,则情况并非如此,因为实例将由它们在列表中的位置来标识。)
Terraform 0.11 或更早版本中没有与上述内容直接等效的内容。
如果我这样做,我会收到以下错误 "for_each" 值取决于无法确定的资源属性 until apply,因此 Terraform 无法预测将创建多少个实例。 要解决此问题,请使用 -target 参数首先仅应用 for_each 依赖的资源。"