Search code examples
terraformterraform-provider-aws

Terraform - How can I get the availability zone from the ec2 instance to use in aws_ebs_volume resource?


Using a TF module to deploy EC2 instances I want to cater for multiple instances and multiple volume attachments. My issue is I want to use the same az as the instance for the volume but if I have 1 instance and 5 volumes I am having issues using count.

resource "aws_instance" "this" {
  count = var.instance_count > 0 ? var.instance_count : length(var.subnet_ids)

  ami           = var.ami
  instance_type = var.instance_type
  user_data     = var.user_data
  key_name      = var.keypair_name

  subnet_id                   = var.subnet_ids[count.index]
  associate_public_ip_address = var.associate_public_ip_address

  vpc_security_group_ids = var.security_group_ids
  iam_instance_profile   = var.instance_profile_name

  monitoring                           = var.monitoring
  disable_api_termination              = var.disable_api_termination
  instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior

  root_block_device {
    volume_size           = var.root_volume_size
    delete_on_termination = var.root_volume_delete_on_termination
  }
}

data "aws_subnet" "this" {
  for_each = toset(var.subnet_ids)

  id = each.key
}

resource "aws_ebs_volume" "this" {
  count = var.assign_volume ? length(aws_instance.this) * var.volume_count : 0

  availability_zone = data.aws_subnet.this[aws_instance.this[count.index].subnet_id].availability_zone

  encrypted  = var.encryption
  kms_key_id = var.kms_key

  size = element(var.ebs_volume_size, (count.index))
  type = var.ebs_volume_type

  tags = {
    Name = "${local.name}-${count.index + 1}"
    volume_id = var.volume_id[count.index]
  }
}

resource "aws_volume_attachment" "this" {
  count = var.assign_volume ? length(aws_instance.this) * var.volume_count : 0

  device_name = element(var.device_name, (count.index))
  volume_id   = aws_ebs_volume.this[count.index].id
  instance_id = aws_instance.this[count.index].id
}

I need a better method to get the az as this is coming back with an error as I have 1 instance but a count of 5 ebs volumes.

availability_zone = data.aws_subnet.this[aws_instance.this[count.index].subnet_id].availability_zone

ERROR

│ Error: Invalid index
│
│   on main.tf line 104, in resource "aws_ebs_volume" "this":
│  104:   availability_zone = data.aws_subnet.this[aws_instance.this[count.index].subnet_id].availability_zone
│     ├────────────────
│     │ aws_instance.this is tuple with 1 element
│     │ count.index is 2
│
│ The given key does not identify an element in this collection value.

Solution

  • The code definitely is not using the subnet resource ID as a key in the data objects, and so the error message is expected. You would need to couple the subnet data to the EBS volumes. This could be awkward and cumbersome (especially given the different enumerables for iteration), and so it would be easier to use the aws_instance as a source of truth for the AZ instead as it is already coupled to the EBS. Additionally, we would need to match the element of the EBS volume resource to the instance with some arithmetic:

    resource "aws_ebs_volume" "this" {
      count = var.assign_volume ? length(aws_instance.this) * var.volume_count : 0
      ...
      availability_zone = aws_instance.this[floor(count.index / var.volume_count)].availability_zone
      ...
    }
    

    Note also your aws_volume_attachment will have similar element indexing issues and would need to be similarly updated for the instance_id value.