Schedule Azure VM Deallocation and Morning Start
Why az vm stop keeps billing, the exact RBAC actions a scheduler needs, and how to confirm a VM is really Stopped (deallocated).
Azure has two ways to turn a VM off, and only one of them stops the compute bill. This guide sets up a nightly deallocation and a morning start with the Azure CLI, then checks the power state.
Stopped is not the same as deallocated
Microsoft’s billing table is explicit:
| Power state | Compute billed? |
|---|---|
Stopped (also shown as Stopped (Allocated) or PoweredOff), the result of az vm stop or a shutdown inside the guest | Yes |
Deallocated, shown as Stopped (deallocated), the result of az vm deallocate | No |
Even deallocated, disks and networking resources keep incurring charges. The az vm stop reference itself says the VM stays billed and points you to az vm deallocate. Any schedule you build should deallocate.
Permissions
The identity that runs the schedule needs these Azure RBAC actions on the VMs, either through a custom role or a built-in role that includes them:
Microsoft.Compute/virtualMachines/deallocate/actionMicrosoft.Compute/virtualMachines/start/actionMicrosoft.Compute/virtualMachines/read
1. Check what the VM loses
Two things do not survive deallocation:
- Dynamic public IPs. Microsoft’s public IP documentation says a dynamic address is released when the resource is stopped. If anything allowlists the address or points DNS at it, switch the public IP to static first.
- The temporary disk. Data on the temporary disk might be lost when you stop the VM. Anything on
D:(Windows) or the resource disk (Linux) beyond page files, swap or tempdb should move to a managed data disk.
Managed OS and data disks persist and keep billing.
2. Deallocate and start from the CLI
# eveningaz vm deallocate --resource-group rg-dev --name dev-web-01 --no-wait
# morningaz vm start --resource-group rg-dev --name dev-web-01
# every VM in a resource groupaz vm deallocate --ids $(az vm list -g rg-dev --query "[].id" -o tsv)3. Put it on a timetable
Run the two commands on a clock from something that carries an identity with the actions above: an Azure Automation runbook, a scheduled pipeline job, or a cron host with a managed identity. Whatever you use, make the evening job call deallocate, not stop.
az vm auto-shutdown exists, but it only sets a shutdown time (flags --time, --email, --webhook, --off) and has no start option, and Microsoft’s CLI reference does not say which power state it leaves the VM in. If you use it, check the state the next morning with the command below.
4. Verify the state
az vm get-instance-view -g rg-dev -n dev-web-01 \ --query "instanceView.statuses[?starts_with(code,'PowerState')].displayStatus" -o tsvAfter the morning start you should see VM running. Overnight the status should name the deallocated state; if it only says stopped, the VM is still allocated on a host and still billing compute.
Doing it with ZopNight
ZopNight handles the deallocate versus stop distinction for Azure VMs and can put both the evening and morning actions in one schedule: cron start and stop times in an IANA timezone, attached to individual VMs or a resource group. The Azure subscription must be connected with read + write permission for scheduled execution; read-only connections get discovery and recommendations only. The resource notes are on Azure VM scheduling, and scale sets are covered on VMSS scheduling.