Issue
I have this terraform code where im trying to create 4 ec2 instances which has different configurations for each ec2
resource "aws_instance" "ec2" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
key_name = each.value.key_name
user_data = file("${path.module}/templates/${coalesce(each.value.userdata, "user_data")}.sh")
associate_public_ip_address = false
subnet_id = var.private_subnet_id
availability_zone = var.az
root_block_device {
volume_size = each.value.volume_size
delete_on_termination = true
encrypted = each.value.enable_volume_encryption
tags = merge(var.tags, {
Name = "${each.key}-vol"
})
}
dynamic "ebs_block_device" {
for_each = each.value.block_devices
content {
volume_type = ebs_block_device.value.volume_type
volume_size = ebs_block_device.value.volume_size
delete_on_termination = false
tags = ebs_block_device.value.tags
device_name = ebs_block_device.value.device_name
}
}
tags = merge(var.tags, {
Name = "${each.key}"
})
}
and iam passing the values as
instances = {
instance01 = {
ami = "ami-0da7657fe73215c0c"
key_name = "dev-key"
instance_type = "t3a.medium"
volume_size = 16
enable_volume_encryption = false
userdata = "instance01"
block_devices = [
{
device_name = "/dev/sdf"
volume_size = 16
volume_type = "gp2"
tags = {
Name = "instance01-vol-1"
}
},
{
device_name = "/dev/sdp"
volume_size = 16
volume_type = "gp2"
tags = {
Name = "instance01-vol-2"
}
}
]
},
instance02 = {
ami = "ami-0da7657fe73215c0c"
key_name = "dev-key"
instance_type = "t3a.medium"
volume_size = 16
enable_volume_encryption = false
userdata = "instance02"
block_devices = [
{
device_name = "/dev/sdf"
volume_size = 16
volume_type = "gp2"
tags = {
Name = "instance02-vol-1"
}
},
{
device_name = "/dev/sdp"
volume_size = 16
volume_type = "gp2"
tags = {
Name = "instance02-vol-2"
}
}
]
},
node01 = {
ami = "ami-0da7657fe73215c0c"
key_name = "dev-key"
instance_type = "t3a.medium"
volume_size = 16
enable_volume_encryption = false
userdata = "node01"
block_devices = [
{
device_name = "/dev/sdf"
volume_size = 16
volume_type = "gp2"
tags = {
Name = "node01-vol-1"
}
}
]
},
node02 = {
ami = "ami-0da7657fe73215c0c"
key_name = "dev-key"
instance_type = "t3a.medium"
volume_size = 16
enable_volume_encryption = false
userdata = "instance04"
block_devices = [
{
device_name = "/dev/sdp"
volume_size = 16
volume_type = "gp2"
tags = {
Name = "node02-vol-1"
}
}
]
}
}
the problem with this code is when i tried to change the ebs volume size, the changes are not automatically detected by Terraform, and terraform document says
NOTE: Currently, changes to the ebs_block_device configuration of existing resources cannot be automatically detected by Terraform. To manage changes and attachments of an EBS block to an instance, use the aws_ebs_volume and aws_volume_attachment resources instead. If you use ebs_block_device on an aws_instance, Terraform will assume management over the full set of non-root EBS block devices for the instance, treating additional block devices as drift. For this reason, ebs_block_device cannot be mixed with external aws_ebs_volume and aws_volume_attachment resources for a given instance.
How should i use these "aws_ebs_volume" and "aws_volume_attachment" resources for above code without using "dynamic ebs_block_device".
Can someone please help me here?
Tried using them but it creates only one additional volume for each ec2
resource "aws_ebs_volume" "additional_volumes" {
for_each = var.instances
size = each.value.additional_volume_size
type = each.value.additional_volume_type
availability_zone = var.az
encrypted = each.value.encrypted
tags = {
Name = "${each.key}"
}
}
resource "aws_volume_attachment" "ec2_attachment" {
for_each = var.instances
device_name = each.value.device_name
instance_id = aws_instance.example[each.key].id
volume_id = aws_ebs_volume.additional_volumes[each.key].id
}
Solution
You can try something like the following, "flattening" the device list to create. In this way, you create a list of the devices to create, adding a reference to the instance.
In your module, add a locals.tf
file with the content:
locals {
ebs_volumes = flatten([
for k, v in var.instances : [
for device, device in v.block_devices : {
device_key = "${k}-${device.tags.Name}"
instance_key = k
ebs_device = device
}
]
])
}
Then, in the main file, remove the dynamic block from ec2 instance and iterate the list of the EBS resources to create:
resource "aws_ebs_volume" "volume" {
for_each = { for elem in local.ebs_volumes : elem.device_key => elem }
availability_zone = var.az
size = each.value.ebs_device.volume_size
tags = each.value.ebs_device.tags
}
resource "aws_volume_attachment" "ebs_att" {
for_each = { for elem in local.ebs_volumes : elem.device_key => elem }
device_name = each.value.ebs_device.device_name
volume_id = aws_ebs_volume.volume[each.value.device_key].id
instance_id = aws_instance.ec2[each.value.instance_key].id
}
The plan shows something like the following. Note the key of each resource created.
# aws_ebs_volume.volume["instance01-instance01-vol-1"] will be created
+ resource "aws_ebs_volume" "volume" {
+ arn = (known after apply)
+ availability_zone = "us-east-2a"
+ encrypted = (known after apply)
+ final_snapshot = false
+ id = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ size = 16
+ snapshot_id = (known after apply)
+ tags = {
+ "Name" = "instance01-vol-1"
}
+ tags_all = {
+ "Name" = "instance01-vol-1"
}
+ throughput = (known after apply)
+ type = (known after apply)
}
# aws_ebs_volume.volume["instance01-instance01-vol-2"] will be created
+ resource "aws_ebs_volume" "volume" {
+ arn = (known after apply)
+ availability_zone = "us-east-2a"
+ encrypted = (known after apply)
+ final_snapshot = false
+ id = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ size = 16
+ snapshot_id = (known after apply)
+ tags = {
+ "Name" = "instance01-vol-2"
}
+ tags_all = {
+ "Name" = "instance01-vol-2"
}
+ throughput = (known after apply)
+ type = (known after apply)
}
# aws_ebs_volume.volume["instance02-instance02-vol-1"] will be created
+ resource "aws_ebs_volume" "volume" {
+ arn = (known after apply)
+ availability_zone = "us-east-2a"
+ encrypted = (known after apply)
+ final_snapshot = false
+ id = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ size = 16
+ snapshot_id = (known after apply)
+ tags = {
+ "Name" = "instance02-vol-1"
}
+ tags_all = {
+ "Name" = "instance02-vol-1"
}
+ throughput = (known after apply)
+ type = (known after apply)
}
[...]
# aws_volume_attachment.ebs_att["instance01-instance01-vol-1"] will be created
+ resource "aws_volume_attachment" "ebs_att" {
+ device_name = "/dev/sdf"
+ id = (known after apply)
+ instance_id = (known after apply)
+ volume_id = (known after apply)
}
# aws_volume_attachment.ebs_att["instance01-instance01-vol-2"] will be created
+ resource "aws_volume_attachment" "ebs_att" {
+ device_name = "/dev/sdp"
+ id = (known after apply)
+ instance_id = (known after apply)
+ volume_id = (known after apply)
}
# aws_volume_attachment.ebs_att["instance02-instance02-vol-1"] will be created
+ resource "aws_volume_attachment" "ebs_att" {
+ device_name = "/dev/sdf"
+ id = (known after apply)
+ instance_id = (known after apply)
+ volume_id = (known after apply)
}
# aws_volume_attachment.ebs_att["instance02-instance02-vol-2"] will be created
+ resource "aws_volume_attachment" "ebs_att" {
+ device_name = "/dev/sdp"
+ id = (known after apply)
+ instance_id = (known after apply)
+ volume_id = (known after apply)
}
# aws_volume_attachment.ebs_att["instance03-instance03-vol-1"] will be created
+ resource "aws_volume_attachment" "ebs_att" {
+ device_name = "/dev/sdf"
+ id = (known after apply)
+ instance_id = (known after apply)
+ volume_id = (known after apply)
}
# aws_volume_attachment.ebs_att["instance04-instance04-vol-1"] will be created
+ resource "aws_volume_attachment" "ebs_att" {
+ device_name = "/dev/sdp"
+ id = (known after apply)
+ instance_id = (known after apply)
+ volume_id = (known after apply)
}
Answered By - Marco Caberletti Answer Checked By - Clifford M. (WPSolving Volunteer)