Deallocated VMs whose reservations keep billing (detected, not dollar-tagged)
What does ZopNight detect here?
Reserved Instances and Savings Plans bill every hour of their 1- or 3-year term regardless of VM power state, so a deallocated covered VM wastes committed spend. ZopNight detects the condition but abstains from claiming a saving: a sunk commitment has no lever that reduces next month's bill.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1380 |
| Category | discount |
| Severity | high |
| Metric | none — pure configuration read |
| Source | ri_deallocated.go |
Where it applies
Deallocating a reserved VM saves nothing
Deallocation releases compute and stops on-demand billing. The reflex is correct for pay-as-you-go VMs. Under a Reserved Instance or Savings Plan the reflex fails: the commitment bills every hour of its term whether the covered VM runs or not. A deallocated VM with RI coverage is the worst of both worlds: no workload, full committed cost, and reservation utilization quietly falling below 100%.
What the rule checks before saying anything
Three gates run in order: the VM’s power state must be stopped or deallocated (transient starting and stopping states are excluded), billing coverage must attribute the VM to a Reservation or SavingsPlan purchase, and a positive committed cost must be known. Running covered VMs are exactly right and pass silently.
Why the finding carries no dollar figure
Every lever available on a sunk commitment fails the test of being a real, immediate saving. Exchanging the reservation onto another workload re-points the same dollars, for a net $0. Letting it lapse at term end is a future decision, not a monthly delta. Azure’s own unused-reservation billing rows are reconciliation artifacts, not a per-VM waste figure the recommender can stand behind. Rather than print an invented number or a $0 recommendation that downstream filters would drop anyway, the rule abstains by design. The gates stay wired for the day a true reservation-waste feed exists.
Run the two-sided check yourself
az vm list -d --query \ "[?powerState=='VM deallocated' || powerState=='VM stopped'].{name:name, size:hardwareProfile.vmSize}" \ -o tableCross-reference the list against reservation utilization in Cost Management → Reservations, where per-reservation usage percentages make idle coverage visible.
The three real responses
Restart the workload the reservation was sized for; exchange the reservation toward a size or region you do run (Azure permits exchanges within policy limits); or calendar the term end and let it expire. Which one applies is a finance-and-capacity decision no scanner should make alone.
Access footprint
Reader establishes power state; Cost Management Reader supplies the coverage attribution. Nothing here is automated. The rule exists to make committed-spend waste visible early.