Reputation: 2700
I have the following Terraform resource which is intended to create a separate encrypted EFS volume per warehouse, and then create mount targets in two subnets for each:
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}"
}
The EFS themselves launch fine, but the mounts fail, because the file_system_id must be a single resource, not a list.
* 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
I'm not sure what my recourse is here in terms of Terraform syntax. I need two mounts in each case, so my count is on the length of the subnets variable, for which I'm passing in subnet-123456 and subnet-567890. How can I get the system to make both mounts for each EFS made (seven in total by length of warehouses list) without being able to use a second count within the aws_efs_mount_target?
Here is how I am calling the module:
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
}
Here is my plan with the attempt of adding [count.index] as per the answer below - it does two mounts, but only to the first 2 of the 7 FS IDs:
+ 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"
Upvotes: 5
Views: 11371
Reputation: 1
If I do this , I get the following error The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on."
Upvotes: 0
Reputation: 74219
Terraform 0.12 introduced a new function setproduct
for situations like this where you need a set of objects representing every combination of elements from two or more collections:
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]
}
]
}
The above declares local.data_share_subnets
as a data structure something like this:
[
{"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"}
]
You can then use that with the new for_each
feature introduced in 0.12.6 to declare one aws_efs_mount_target
instance per entry in that collection:
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
}
The for
expression in the for_each
argument transforms the list shown above into a map whose keys are like "fs-123456:subnet-aaaaaaaa"
. Terraform will then track these instances using addresses like this:
aws_efs_mount_target.mounts["fs-123456:subnet-aaaaaaaa"]
aws_efs_mount_target.mounts["fs-123456:subnet-bbbbbbbb"]
...which means you can safely add and remove individual filesystems and subnets without disturbing any other unrelated mount target instances. (that would not be true using count
, because the instances would be identified by their positions in the list.)
There is no direct equivalent of the above in Terraform 0.11 or earlier.
Upvotes: 12