Azure Virtual Desktop Benefits

Terraform Tips: Managing Azure Virtual Desktop Session Hosts with Precision

Managing Azure Virtual Desktop (AVD) environments efficiently is key to maintaining both cost-effectiveness and operational simplicity. Most AVD implementations rely on automatic host assignments. For personal desktop scenarios, when a user departs, the last session host in the pool can generally be removed safely, allowing AVD to automatically reassign remaining resources.

That’s simple enough until it’s not.

Sometimes, you may enable direct assignment on a host pool due to specific application or networking needs. (Sure, you could create separate host pools, but let’s not open that debate here.) In such cases, deleting a specific session host tied to a particular user instead of just the last one in the pool becomes essential.

And if you’ve tried managing your AVD session hosts with Terraform, you’ve probably realized there’s no native azurerm_session_host resource type (yet). You’re still working with good old azurerm_windows_virtual_machine.

Deleting a targeted session host in this setup is tricky but not impossible. After running into this exact problem, we developed a clean, automated approach using a combination of the AzureRM and AzAPI providers.

Let’s break it down.

The Challenge

You need to:

  • Delete a specific AVD session host (say, VM2),
  • Keep other hosts untouched, and
  • Clean up the orphaned host entry from the host pool  automatically.

Step 1: Conditional Host Creation with AzureRM

We start by using a Boolean local variable called create_vm to control which session hosts get created (or destroyed) based on a list of exclusions.

locals {

  # Determine if this VM should be created (not excluded)

  create_vm = !contains(var.vm_names_to_exclude, var.session_host_name)

}
  • var.session_host_name → The session host name to deploy.
  • var.vm_names_to_exclude → List of host names to exclude (i.e., delete).

Now, wrap your AVD VM resources and extensions with this conditional logic:

# Windows Virtual Machines – Only create if VM is not excluded

resource "azurerm_windows_virtual_machine" "this" {

  count = local.create_vm ? 1 : 0

  # existing configuration

}

# AVD Agent Extension – Only create if VM is not excluded

resource "azurerm_virtual_machine_extension" "avd_agent" {

  count = local.create_vm ? 1 : 0

  # existing configuration

}

When you run the pipeline, Terraform destroys the VMs listed in vm_names_to_exclude.

However, there’s a catch.

The host entries still linger in your AVD host pool, marked as “Not Connected”, because Terraform doesn’t yet manage AVD session host objects directly.

That’s where AzAPI comes in.

Step 2: Cleaning Up Host Pool Entries with AzAPI

We use the AzAPI provider to remove those “ghost” entries programmatically.

First, define a local map variable that contains metadata for each host to delete. This map can be automatically populated based on how you’re provisioning other AVD resources, such as host pools.

locals {
  vms_to_remove = {
    for vm_name in var.vm_names_to_exclude :
    vm_name  =>  {
      host_pool_id      = ""  # Host pool resource ID
      session_host_fqdn = ""  # Fully qualified domain name of the host
    }
  }
}

Then, loop through this list and delete the entries using azapi_resource_action:

# Delete session host entries from host pool using AzAPI

resource "azapi_resource_action" "delete_session_hosts" {

  for_each    = local.vms_to_remove

  type        = "Microsoft.DesktopVirtualization/hostPools/sessionHosts@2024-04-03"

  resource_id = "${each.value.host_pool_id}/sessionHosts/${each.value.session_host_fqdn}"

  method      = "DELETE"

  lifecycle {

    create_before_destroy = false

  }

}

With this, both your virtual machines and their session host records stay perfectly synchronized all within Terraform.

Step 3: Watch Out for instance_count

If you use instance_count to deploy multiple session hosts, remember one crucial rule:

Never reduce instance_count when deleting specific hosts.

Terraform interprets a lower count as “remove the last created VMs,” not “remove these particular ones.”

Example: Removing a Specific Host

Current: 3 hosts (VM1, VM2, VM3)
Goal: Remove VM2 only

instance_count      = 3

vm_names_to_exclude = ["VM2"]

Result: VM2 is deleted, VM1 and VM3 remain.

Example: What Not to Do

instance_count      = 2  # Reduced count

vm_names_to_exclude = ["VM2"]

Result: Both VM2 and VM3 are deleted, not what you wanted.

🔁 Re-Adding Removed Hosts

If you later want to recreate the deleted host(s):

instance_count      = 3

vm_names_to_exclude = []

Result: All three hosts (VM1, VM2, VM3) are deployed again.

Best Practices

  1. Keep instance_count stable during targeted removals.
  2. Use vm_names_to_exclude to precisely control deletions.
  3. Clear vm_names_to_exclude ([]) after a successful removal to avoid unintentional deletions.
  4. To scale down permanently, first remove specific hosts, then adjust instance_count in the next run.

Final Thoughts

This approach gives you granular control over AVD session hosts  all through Terraform, without any manual cleanup in the Azure portal. Until Terraform introduces a native azurerm_session_host resource, this combination of AzureRM and AzAPI providers offers a reliable, fully automated workaround.

Whether you’re managing hundreds of personal desktops or just fine-tuning a single host pool, this method ensures your AVD environment stays clean, consistent, and truly infrastructure-as-code.

More insights

Get started on the right path to cloud success today. Our Crew are standing by to answer your questions and get you up and running.