Deallocate Stopped Azure Virtual Desktops With Function App

Here’s an example of how you can automatically check for stopped virtual machines and if any are found the script will Deallocate them so you don’t continue to get billed.

Occasionally we will have users that shutdown their Personal Azure Virtual Desktops when they are done for the day, but this doesn’t actually stop the billing for compute resources.

Power State: Stopped = Billed
The virtual machine is allocated on a host but not running. Also called PoweredOff state or Stopped (Allocated). This state can be result of invoking the PowerOff API operation or invoking shutdown from within the guest OS. The Stopped state may also be observed briefly during VM creation or while starting a VM from Deallocated state.

Power State: Deallocated = Not Billed
The virtual machine has released the lease on the underlying hardware and is powered off. This state is also referred to as Stopped (Deallocated).
States and billing status – Azure Virtual Machines | Microsoft Learn

Script Summary:
The provided code is a PowerShell script that is designed to run in response to a timer trigger in Azure Functions. The script uses Azure Resource Graph to find stopped virtual machines because its the fastest method I found when there’s thousands of virtual machines in a subscription.
The script first retrieves the current universal time, and if the function is running late, it displays a message to the console. The script then searches for virtual machines that are in the stopped state and captures their names and resource groups. Finally, it uses the Stop-AzVM cmdlet to deallocate each virtual machine.
Write a summary of this.
# Input bindings are passed in via param block.

# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()

# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
    Write-Host "PowerShell timer is running late!"

# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
# Run the query and capture the results
$query = "Resources | where type == 'microsoft.compute/virtualmachines' | where properties.extended.instanceView.powerState.code == 'PowerState/stopped' | project name, resourceGroup"
$results = Search-AzGraph -Query $query

# Loop through the results and deallocate each virtual machine
foreach ($result in $results) {
    $vmName = $
    $vmResourceGroup = $result.resourceGroup
    Stop-AzVM -Name $vmName -ResourceGroupName $vmResourceGroup -Force -Nowait

After you create your FunctionApp and Function, you can paste the code and save.

You can modify the timer to run the script as many times as needed.

Timer trigger for Azure Functions | Microsoft Learn

Share or Save this: