EBS Volume tags keep on rotating with each apply

Hi,
I’m using aws_ebs_volume to create volume and aws_volume_attachment to attach it.
Everytime I execute “terraform apply” the volume tags keep on rotating (veggies name - below is just a snippet out of the code I am executing)!

How can I keep it constant?

resource “aws_ebs_volume” “node” {
count = 2
availability_zone = data.aws_subnet.private[count.index].availability_zone
type = var.ebs_volume_type
size = var.ebs_volume_size
encrypted = true

tags = {
Name = format(“%s-%s-volume-%02s.%s”, “apple”, [“carrot”, “beetroot”][count.index], count.index , “Pineapple”)
}
}

resource “aws_volume_attachment” “node” {
count = 2
device_name = var.device_name
volume_id = aws_ebs_volume.node[count.index].id
instance_id = aws_instance.node[count.index].id
stop_instance_before_detaching = true
}

Thanks,

Hi @Let-itGo!

Can you share an example terraform plan output showing Terraform proposing a change that won’t converge? I’m not sure from this example how exactly the tags could fail to converge here because it seems to depend only on count.index and that’s fixed as part of each instance’s address. I think something else must be happening that I’m not considering, and seeing some real plan output would hopefully help me to understand what that is.

@apparentlymart : here is the screenshot:

code:

# volume creation
resource "aws_ebs_volume" "fruits_node_apple" {
  count = 2

  availability_zone = data.aws_subnet.private[element(tolist(sort(data.aws_subnets.private.ids)), count.index)].availability_zone
  type              = "gp3"
  size              = 50

  tags = {
    Name    = format("%s-apple-volume-%02s", var.name_prefix, count.index+ 1)
  }
}


resource "aws_ebs_volume" "fruits_node_mango" {
  count = 2

  availability_zone = data.aws_subnet.private[element(tolist(sort(data.aws_subnets.private.ids)), count.index)].availability_zone
  type              = "gp3"
  size              = 50

  tags = {
    Name    = format("%s-mango-volume-%02s", var.name_prefix, count.index+ 1)
  }
}


resource "aws_ebs_volume" "fruits_node_grapes" {
  count = 2

  availability_zone = data.aws_subnet.private[element(tolist(sort(data.aws_subnets.private.ids)), count.index)].availability_zone
  type              = "gp3"
  size              = 50

  tags = {
    Name    = format("%s-grapes-volume-%02s", var.name_prefix, count.index+ 1)
  }
}

# volume attachment
resource "aws_volume_attachment" "fruits_apple_vol" {
  count = 2

  device_name                    = "/dev/sdb"
  volume_id                      = aws_ebs_volume.fruits_node_apple[count.index].id
  instance_id                    = aws_instance.fruits_node[count.index].id
  stop_instance_before_detaching = true
}


resource "aws_volume_attachment" "fruits_mango_vol" {
  count = 2

  device_name                    = "/dev/sdh"
  volume_id                      = aws_ebs_volume.fruits_node_mango[count.index].id
  instance_id                    = aws_instance.fruits_node[count.index].id
  stop_instance_before_detaching = true
}


resource "aws_volume_attachment" "fruits_grapes_vol" {
  count = 2

  device_name                    = "/dev/sdi"
  volume_id                      = aws_ebs_volume.fruits_node_grapes[count.index].id
  instance_id                    = aws_instance.fruits_node[count.index].id
  stop_instance_before_detaching = true
}

data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.fruit.id]
  }

  tags = {
    Tier = "Private"
  }
}

data "aws_vpc" "fruit" {
  filter {
    name   = "tag:Name"
    values = ["FRUIT"]
  }
}

data "aws_subnet" "private" {
  for_each = toset(sort(data.aws_subnets.private.ids))
  id       = each.value
}

resource "aws_instance" "fruits_node" {
  count = 2

  ami                    = "ami-00e87074e52e6c9f9"
  instance_type          = "c5.xlarge"
  ebs_optimized          = "true"
  monitoring             = "true"
  subnet_id              = element(tolist(sort(data.aws_subnets.private.ids)), count.index)

  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }

  root_block_device {
    volume_type = "gp3"
    volume_size = 50
    encrypted   = "true"
  }

  volume_tags = {
    Name    = format("%s-root-volume-%02s", var.name_prefix, count.index+ 1)
  }

}

Hi @Let-itGo!

I think this is happening because there is a contradiction in your configuration: the aws_instance.fruits_node resource says that all of the volumes of this instance should be tagged with the name including “root”, but the aws_ebs_volume resources specify different tags that don’t match that rule. Because of the dependency ordering of the operations, on a single run Terraform will first create the EC2 instance with its root volume only and tag it, and then separately create the other volumes and attach them. But then on the next run, the aws_instance implementation notices that some of the volumes don’t match and so it produces a plan to fix that.

I must admit I’m not very familiar with these particular features of the AWS provider, so I’m not sure if there is a way to tell the provider that the volume_tags are only intended for the root_block_device and should not also be applied to the other volumes that were attached later. Since this seems to be an AWS provider behavior thing, I’m going to move this topic over to the AWS provider’s category in the hope that someone there will have more direct experience with these features and will be able to make some suggestions about how to avoid this problem.

I see an old issue in the provider’s repository that seems like it might be related to what you are describing:

That issue seemed to be resolved by adding a new tags argument you can set inside root_block_device to specify tags only for that one device, which seems like it might help with the situation you’re describing, but I don’t have any direct experience with it so I’m not sure if there are caveats with that I’m not considering.

1 Like

This solves the issue.

Just quoting again:
using tags inside root_block_device instead of using volume_tags.