Skip to main content
schedule · azure

Azure VM Non-Production Scheduling

resource types
1
rule IDs covered
1
severity
medium

What does ZopNight detect here?

Positively non-production Azure VMs earn a recurring off-hours start/stop schedule when a 30-day weekly heatmap measures at least 20% idle time. Savings equal cost times the observed idle fraction; neutral-named untagged VMs, reserved-instance-covered VMs, and workloads with critical, stateful, or spot lifecycles are excluded, and missing heatmap data abstains.

Signal and threshold

How ZopNight evaluates Azure VM Non-Production Scheduling.
Field Value
Rule IDsRC-213
Categoryschedule
Severitymedium
Metricnone — pure configuration read
Sourcescheduling_vm.go

Requiring a positive dev/test signal to fire

  • resource.Status: statusSucceeded
  • Non-prod gate (audit 2026-06-26 pattern A, fixed): fires only on a POSITIVE non-production signal via isNonProdResource: a dev/test env tag, a dev/test resource name, or a dev/test resource-group name, with a prod env tag as an absolute veto. A neutral-named, untagged VM (“app-server-07”) now abstains instead of being assumed non-production (the prior if isProdResource(…) { return nil } gate fired on the absence of a prod signal, not on a positive non-prod one). Matches ri_to_spot.go / sql_idle_vm.go.
  • Critical-role + stateful-workload vetoes; interruptible (Metadata[“lifecycle”]=spot/low_priority) veto; Databricks / VMSS-member skip
  • ReservationCoverage: RI/SP-covered VMs skipped (stopping a reserved VM does not save; commitment continues as unused-reservation waste)
  • MetricsAware (primary) and/or ScheduleAware (secondary, aggregator-precomputed ScheduleData)

Idle slots in the observed weekly heatmap

savings = cost × idlePct, where idlePct is the observed idle fraction from the 30-day weekly heatmap (buildWeeklyHeatmap, idle slots / observed slots, never the fixed 168, so a short-window VM is not falsely flagged) on the primary path, or sd.IdlePercentage on the secondary aggregator-ScheduleData path. Gated on idlePct >= schedulingIdleMinPercent (20%). With neither a heatmap measurement nor pre-computed schedule data (and on the no-active-slot sentinel from DeriveStartStopCron), the rule abstains (returns nil). Never a fabricated flat fraction (the schedulingFallbackSavings ~67% literal lives in constants.go and is not consumed by the data-driven path), never a $0/advisory dump. Abstains on pricing absent (cost ≤ 0).

Applying the start and stop cron

  1. Review the recommended start/stop schedule (start cron, stop cron, timezone) and apply it from this recommendation

The three series that build the heatmap

CPUUtilization + VMNetworkIn + VMNetworkOut (30d): drive the weekly heatmap

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·