Azure RI to Spot Migration for Non-Production
What does ZopNight detect here?
Reservation- or savings-plan-covered VMs with a positive non-production signal are candidates for Spot redeployment. The discount fraction is 1 minus the Spot-to-RI-1yr rate ratio, sanity-banded between 0.05 and 0.92; Windows/SQL Server, M-series, DR/standby, and stateful workloads are vetoed, and missing live rates cause abstention.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1381 |
| Category | discount |
| Severity | medium |
| Metric | none — pure configuration read |
| Source | ri_to_spot.go |
Where it applies
Reserved non-prod VMs that Spot can actually take
- ReservationCoverage = Reservation/SavingsPlan (OnDemand VMs are RC-210’s); already-Spot skip; interruptible-lifecycle (Metadata[“lifecycle”]=spot/low_priority) false-RI-premise guard (fail-closed)
- Positive non-prod signal (isNonProdResource, audit 2026-06-26 pattern A): gating on !isProdResource fired Spot on neutral-named prod VMs; abstains otherwise
- M-series skip (isMSeriesVM, since Spot is unavailable); Databricks / VMSS-member skip. isVMSSMember only recognizes Uniform-orchestration members (nested …/virtualMachineScaleSets/…/virtualMachines/N UID); a Flexible-orchestration member is a flat Microsoft.Compute/virtualMachines UID linked via properties.virtualMachineScaleSet.id, which the discoverer does not currently stamp into Metadata, so such a member is not excluded today (audit 2026-07-01, low severity; a per-instance Spot migration on a Flex member is a policy-conflict risk with the scale set’s own scaling/HA policy, not a factually wrong claim). Extend the exclusion once the discoverer stamps VMSS-Flex membership.
- stateful data-service veto (matchesStatefulWorkload)
- critical-role name veto (#1906 B2): shares matchesCriticalRole (dr, standby, segment-boundary), and a DR/standby box is never migrated onto Spot.
- OS/license veto (#1906 B4): shares isSpotIncompatibleOS, so Windows / SQL-Server (Metadata[“os_family”]/platform) are skipped; absent OS signal abstains.
The Spot to RI one-year rate ratio
cost-driven discount. The fraction is derived solely from live tier rates: frac = 1 − Spot/RI1yrNoUpfront, sanity-banded [0.05, 0.92] (riToSpotFractionFromTiers, rate-inversion guarded); SavingsUSD = cost × frac. The denominator is RI1yrNoUpfront (Aryan B3: the VM’s current cost is already RI-discounted, so OnDemand would over-state by the RI-discount fraction). The previous DeriveCommitmentToSpotFraction(…, azureRIToSpotSavingsFraction) flat 0.60 fallback is removed; when the aggregator has not priced this SKU’s RI1yr/Spot tiers (ok=false) or pricing is absent (cost ≤ 0) the rule abstains (returns nil), never falling back to a fabricated fraction.
Redeploying as Spot and freeing the reservation
- Confirm the workload tolerates interruption
- Re-deploy the VM as Spot priority
- Release the reservation back to the pool for production use
CPU and network as background evidence
CPUUtilization + VMNetworkIn (30d) are evidence only; the firing signal is structural (RI/SP coverage plus a positive non-prod signal)