For_each depends_on previous item

Good afternoon terraform community. I have a challenge I am trying to solve and I have not found a good solution yet… other than break my for_each into seperate resources.

I am trying to mount a variable number of data disks on an Azure VM with the following code:

resource azurerm_virtual_machine_data_disk_attachment data_disks {
  for_each = var.data_disks

  managed_disk_id    = azurerm_managed_disk.data_disks[each.key].id
  virtual_machine_id = azurerm_linux_virtual_machine.VM.id
  lun                = each.value.lun
  caching            = "ReadWrite"
}

The code above work fine but sometime the disks will get attached out of order to the VM… like LUN1 will be attached before LUN0. This appear to be an Azure issue where it does not necessarily create resources in the order it received.

So to fix this I would need to add a depends_on inside each item of the for_each loop to make it depend on the precedent item… if it is not the 1st one.

It appear depends_on apply before the for_each in a resource and not inside each item.

Any suggestion? Breaking the code in separate resource would be awful as I don’t know how many data disks will be asked to be created…

With ARM you could control the loop parallelism and make it serial… not available in terraform. And I don’t want to go back to ARM :wink: Will probably take separate resources before considering that :wink:

Hi @bernardmaltais,

Unfortunately as you’ve seen for_each declares a number of instances that Terraform considers independent of one another. Even if you were able to control the concurrency to ensure that only one would happen at a time, that would not necessarily control the ordering because there is no natural ordering for a for_each.

With Terraform as it exists today, I think separate resource blocks is unfortunately the best way to get this done. As you mention though, to do that you’d need to impose a maximum number of disks that are possible, because you could only have a finite number of blocks:

resource "azurerm_virtual_machine_data_disk_attachment" "disks_lun0" {
  for_each = {
    for k, v in var.data_disks : k => v if v.lun == 0
  }

  # ...
}

resource "azurerm_virtual_machine_data_disk_attachment" "disks_lun1" {
  for_each = {
    for k, v in var.data_disks : k => v if v.lun == 1
  }
  depends_on = [azurerm_virtual_machine_data_disk_attachment.disks_lun0]

  # ...
}

resource "azurerm_virtual_machine_data_disk_attachment" "disks_lun2" {
  for_each = {
    for k, v in var.data_disks : k => v if v.lun == 2
  }
  depends_on = [azurerm_virtual_machine_data_disk_attachment.disks_lun1]

  # ...
}

I’m not very familiar with these resource types in particular, but from reviewing the documentation it seems like the intent is that if you want to attach many disks in a particular order then you would declare them inline in a azurerm_virtual_machine resource, which you could do dynamically from your var.data_disks like this:

resource "azurerm_virtual_machine" "example" {
  # ...

  dynamic "storage_data_disk" {
    for_each = var.data_disks
    content {
      name            = storage_data_disk.key
      create_option   = "Attach"
      managed_disk_id = azurerm_managed_disk.data_disks[storage_data_disk.key].id
      lun             = storage_data_disk.value.lun
      caching         = "ReadWrite"
    }
  }
}

I expect this approach will not serve all cases because it requires all of the disks to be known when the virtual machine is initially launched, rather than allowing them to be attached later. Given that the underlying API enforces a specific order of operations here I think the only way this could work with Terraform as currently designed is for the provider to also offer a plural azurerm_virtual_machine_data_disk_attachments resource that has a sequence of nested disk blocks and iterates through them in the given order, but as far as I can tell the provider doesn’t offer such a thing today.