Terraform: How to use multiple locals and Variables inside "for_each"
The configuration you shared in your question is asking Terraform to manage four instances, each of which has four network interfaces associated with it. That's problematic in two different ways:
- All for of the network interfaces on each instance are configured with the same
device_index
, which is invalid and is what the error message here is reporting. - Even if you were to fix that, it would then try to attach the same four network interfaces to four different EC2 instances, which is invalid: each network interface can be attached to only one instance at a time.
To address that and get the behavior you wanted, you only need one network_interface
block, whose content is different for each of the instances:
locals {
instance_ami = {
A = "ami-11111"
B = "ami-22222"
C = "ami-33333"
D = "ami-4444"
}
}
variable "instance_eni" {
description = "Pre created Network Interfaces"
default = [
{
name = "A"
id = "eni-0a15890a6f567f487"
},
{
name = "B"
id = "eni-089a68a526af5775b"
},
{
name = "C"
id = "eni-09ec8ad891c8e9d91"
},
{
name = "D"
id = "eni-0fd5ca23d3af654a9"
}
]
}
locals {
# This expression is transforming the instance_eni
# value into a more convenient shape: a map from
# instance key to network interface id. You could
# also choose to just change directly the
# definition of variable "instance_eni" to already
# be such a map, but I did it this way to preserve
# your module interface as given.
instance_network_interfaces = {
for ni in var.instance_eni : ni.name => ni.id
}
}
resource "aws_instance" "instance" {
for_each = local.instance_ami
ami = each.value
instance_type = var.instance_type
key_name = var.keypair
root_block_device {
delete_on_termination = true
volume_size = 80
volume_type = "gp2"
}
network_interface {
device_index = 0
network_interface_id = local.instance_network_interfaces[each.key]
delete_on_termination = false
}
}
Now each instance has only one network interface, with each one attaching to the corresponding ENI ID given in your input variable. Referring to each.key
and each.value
is how we can create differences between each of the instances declared when using resource for_each
; we don't need any other repetition constructs inside unless we want to create nested repetitions, like having a dynamic number of network interfaces for each instance.
Comments
-
Prashant Shetage almost 2 years
I have a terraform template that creates multiple EC2 instances. I then create a few Elastic Network interfaces in the AWS console and added them as locals in the terraform template. Now, I want to map the appropriate ENI to the instance hence I added locals and variables as below.
locals { instance_ami = { A = "ami-11111" B = "ami-22222" C = "ami-33333" D = "ami-4444" } } variable "instance_eni" { description = "Pre created Network Interfaces" default = [ { name = "A" id = "eni-0a15890a6f567f487" }, { name = "B" id = "eni-089a68a526af5775b" }, { name = "C" id = "eni-09ec8ad891c8e9d91" }, { name = "D" id = "eni-0fd5ca23d3af654a9" } ] } resource "aws_instance" "instance" { for_each = local.instance_ami ami = each.value instance_type = var.instance_type key_name = var.keypair root_block_device { delete_on_termination = true volume_size = 80 volume_type = "gp2" } dynamic "network_interface" { for_each = [for eni in var.instance_eni : { eni_id = eni.id }] content { device_index = 0 network_interface_id = network_interface.value.eni_id delete_on_termination = false } } }
I am getting below error:
Error: Error launching source instance: InvalidParameterValue: Each network interface requires a unique device index. status code: 400, request id: 4a482753-bddc-4fc3-90f4-2f1c5e2472c7
I think terraform is tyring to attach all 4 ENI's to single instance only. What should be done to attach ENI's to an individual instance?
-
Prashant Shetage about 4 yearsHi Martin, getting below error with the above configuration. "Error: Invalid index network_interface_id = local.instance_network_interfaces[each.key] |---------------- | each.key is "A" | local.instance_network_interfaces is object with 4 attributes The given key does not identify an element in this collection value." However, I am able to achieve the goal with: network_interface_id = lookup(var.instance_eni, each.key)
-
Prashant Shetage about 4 yearsFor the above, I had updated the instance_eni variable as below. variable "instance_eni" { default = { A = "eni-0a15890a6f567f487" B = "eni-089a68a526af5775b" C = "eni-09ec8ad891c8e9d91" D = "eni-0fd5ca23d3af654a9" } }