How to Assign Unique, Sticky, Gap-Filling Subnets to VMs Without Reassigning on Removal?

Hi all,

I’m trying to solve a subnet assignment problem in Terraform and want to double-check if there’s a way to meet all my requirements using only Terraform (no external scripts or manual mapping).

In short:
I want to solve the challenge of dynamic, stateful assignment of subnets to resources over time —specifically, to achieve both sticky assignment (so each resource keeps its subnet) and gap-filling (so freed subnets are reused for new resources).

Requirements:

  • Each VM must get a unique subnet from a pool of available subnets (no two VMs share the same subnet).
  • The assignment must be sticky: if I remove a VM, the other VMs must keep their assigned subnets (no shifting/reassignment).
  • The assignment should be gap-filling/non-fragmenting: if a VM is removed, its subnet should become available for the next new VM (i.e., the assignment should fill gaps and not leave unused subnets indefinitely).
  • No shifting: Removing one VM should not cause other VMs to be reassigned to different subnets.
  • No external allocation: Preferably, this should be achieved using only Terraform (no external scripts, files, or manual mapping).

What I’ve tried:
The only way I’ve found to guarantee unique assignments is to use a sequential/index-based approach, like this:

locals {
  subnets = ["subnet-1", "subnet-2", "subnet-3"]
  vms     = ["vm-1", "vm-2", "vm-3"]

  # Assign subnets by index
  vm_subnet_map = {
    for idx, vm in local.vms :
    vm => local.subnets[idx]
  }
}

(This code is a simplified example of what I’m actually doing in my real setup.)

This works for uniqueness, but if I remove (for example) vm-2, then vm-3 will be reassigned from subnet-3 to subnet-2 on the next apply. This is not acceptable for my use case, as I need the assignments to be sticky and not change for existing VMs.

I’ve also tried various approaches to make the assignments sticky, such as:

  • Using hash/modulo logic (e.g., assigning subnets based on a hash of the VM name or resource key), but this can lead to collisions and isn’t gap-filling.
  • Trying to use Terraform outputs or resource attributes to track assignments, but outputs are only available after apply and can’t be used to influence resource creation in the same plan/apply run.
  • Attempting to filter or dynamically select the “first available” subnet, but Terraform’s declarative model and lack of state introspection make this impossible within a single run.

Am I overlooking something very simple or straightforward here?

If there’s not a way in pure Terraform only, what is the recommended best practice way of achieving this?

Thanks for any advice or patterns!