Pay-as-you-go dev and test VMs that could run as Azure Spot VMs
What does ZopNight detect here?
ZopNight flags a running, pay-as-you-go VM that carries a dev or test signal in its environment tag, name or resource group, and prices a move to `--priority Spot` from live Spot and pay-as-you-go rates. VMs recorded as Windows or SQL Server are excluded, and so are database and standby VMs, which cope poorly with 30 seconds' notice.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-210 |
| Category | discount |
| Severity | low |
| Metric | Percentage CPU, Network In Total (evidence only) |
| Threshold | positive dev/test signal, no commitment coverage |
| Evaluation window | 30d |
| Source | ZopNight |
| Permissions used | Microsoft.Compute/virtualMachines/read · Microsoft.Resources/subscriptions/resourceGroups/read |
Where it applies
The price of guaranteed capacity on a dev box
A regular VM pays for capacity Azure will not take back. A Spot VM pays a variable price for
capacity Azure can reclaim at any time. Microsoft’s
Spot VM documentation sets
out the terms: eviction with up to 30 seconds’ notice through Scheduled Events, no SLA, a
separate quota pool, and no support for B-series or promo sizes. With max price set to -1, a
Spot VM is never evicted for price and never pays more than the standard rate.
A development VM that someone can rebuild in minutes does not need that capacity guarantee, and paying for it is the cost this rule points at.
Comparing Spot and regular prices for your sizes
When you create a VM in the portal and tick Run with Azure Spot discount, a link shows price history and eviction rates for the size. Eviction rates are quoted per hour. The same data is in Azure Resource Graph:
az graph query -q "SpotResources| where type =~ 'microsoft.compute/skuspotevictionrate/location'| where sku.name in~ ('standard_d2s_v4','standard_d4s_v4') and location in~ ('eastus')| project skuName=tostring(sku.name), location, evictionRate=tostring(properties.evictionRate)"What makes a VM a Spot candidate
- It is running on pay-as-you-go: no reservation or savings plan covers it, and it is not
already tagged
priority=Spot. - A positive non-production signal, in order: a production environment tag vetoes everything; otherwise a dev or test environment tag, a dev/test name, or a dev/test resource group name.
- The operating system is not recorded as Windows or SQL Server.
- The name does not suggest a data store or a disaster-recovery or standby role.
- The size has priced pay-as-you-go and Spot rates.
VMs deliberately left on regular priority
The operating system check only excludes VMs whose OS is recorded as Windows or SQL Server; a VM
with no OS recorded is not excluded on that ground, so confirm the OS before acting. Without Spot and
pay-as-you-go rates for the size, there is no finding at all: ZopNight does not fall back to a
generic discount, because the firing signal is a name you control and the magnitude must come
from real prices. The dev/test name match is a substring match, so a name such as
attestation-service can be read as test; tag the VM env=prod to rule it out. Committed VMs are
handled by
Azure RI to Spot Migration for Non-Production.
Spot discount from live rates
discount = 1 - (Spot hourly rate / pay-as-you-go hourly rate) for the VM's size and regionsaving = current monthly VM cost x discountA discount outside 5% to 92% is discarded as unreliable. ZopNight also avoids stacking this with other compute levers on the same VM, so the total shown for a VM is not double counted.
Rebuilding the VM as Spot
- Confirm it is non-production and can lose its in-memory state at short notice.
- Snapshot the OS disk, then create a replacement with
az vm create ... --priority Spot --max-price -1 --eviction-policy Deallocate(orDelete). - Add a handler that polls Scheduled Events for a
Preemptevent and shuts down cleanly. - Remove the original VM after the Spot one is in use.