I have a terraform tfvars file with a map of values that looks like this:
name_map = [
{
name = "devbox"
device_names = ["/dev/xvdg", "/dev/xvdh"]
volume_size = ["900", "200"]
group = "hosts"
instance_type = "m5a.2xlarge"
},
{
name = "devbox2"
device_names = ["/dev/xvdg", "/dev/xvdh"]
volume_size = ["300", "200"]
group = "hosts"
instance_type = "m5a.2xlarge"
}
]
] My tf file looks like this:
resource "aws_instance" "node" {
count = length(var.name_map)
dynamic "ebs_block_device" {
for_each = [for device in var.name_map.*.device_names[count.index] : {
device_name = device,
volume_size = var.name_map.*.volume_size[count.index]
}]
content {
device_name = ebs_block_device.value.device_name
volume_type = "gp2"
volume_size = ebs_block_device.value.volume_size
delete_on_termination = true
}
}
So basically for the "devbox" instance I'd like "/dev/xvdg" to be 900 gbs, and "/dev/xvdh" to be 200 gbs. I'd like The current setup works to iterate through the device names of each mapping and get a single volume size but I'm trying to expand it to include different volume sizes for each device.
How would I do this?
I've tried a nested for_each statement but I keep getting errors. Would a flatten structure be the solution here? I'd love to see an example of what this would look like.
CodePudding user response:
I think what you want to do is the following:
resource "aws_instance" "node" {
count = length(var.name_map)
instance_type = var.name_map[count.index].instance_type
ami = "..." # Fill in a valid AMI here
dynamic "ebs_block_device" {
for_each = [for i, device in var.name_map[count.index].device_names : {
device_name = device,
volume_size = var.name_map[count.index].volume_size[i]
}]
content {
device_name = ebs_block_device.value.device_name
volume_type = "gp2"
volume_size = ebs_block_device.value.volume_size
delete_on_termination = true
}
}
}
While this works, I suggest you do the following:
variable "name_map" {
default = [
{
name = "devbox"
devices = [
{
device_name = "/dev/xvdg",
volume_size = 900
},
{
device_name = "/dev/xvdh",
volume_size = 200
}
]
group = "hosts"
instance_type = "m5a.2xlarge"
},
{
name = "devbox2"
devices = [
{
device_name = "/dev/xvdg",
volume_size = 900
},
{
device_name = "/dev/xvdh",
volume_size = 200
}
]
group = "hosts"
instance_type = "m5a.2xlarge"
}
]
}
Note, the device_name
and the volume_size
are grouped together. Now we can use a simple foor
loop where we don't have to rely on indexing:
resource "aws_instance" "node" {
count = length(var.name_map)
instance_type = var.name_map[count.index].instance_type
ami = "..." # fill in a valid AMI name
dynamic "ebs_block_device" {
# Notice the i variable (index) was dropped here
for_each = [for device in var.name_map[count.index].devices : {
device_name = device.device_name,
volume_size = device.volume_size
}]
content {
device_name = ebs_block_device.value.device_name
volume_type = "gp2"
volume_size = ebs_block_device.value.volume_size
delete_on_termination = true
}
}
}
CodePudding user response:
I would nest your map further to create something like this:
name_map = [
{
name = "devbox"
root_block_device = {
...settings
}
ebs_block_devices = toSet([
{
name = "/dev/xvdg"
size = "900"
},{
name = "/dev/xvdh"
size = "200"
}
])
group = "hosts"
instance_type = "m5a.2xlarge"
},
...
]
and then in your resource code you can loop over the set for each instance:
resource "aws_instance" "instance" {
count = length(var.name_map)
...
root_block_device {
...settings from var.name_map[count.index].root_block_device
}
dynamic "ebs_block_device" {
for_each = var.name_map[count.index].ebs_block_devices
content {
device_name = ebs_block_device.value.name
volume_size = ebs_block_device.value.size
}
}
}
If you want the root volume to persist post termination I would suggest adding an EBS root volume, otherwise you can ignore the root_block_device and it will create an ephemeral device that contains the image.