Optimize Azure VMs across all five Well-Architected Framework pillars — from deallocating stopped VMs and right-sizing compute to hardening security, enabling backup, and retiring legacy agents.
Azure Virtual Machines are usually the single largest line item on an Azure invoice -- and also where the most optimization opportunities hide in plain sight. A comprehensive VM optimization strategy works across five dimensions at once: eliminating compute waste through right-sizing and deallocation, aligning disk tiers with actual I/O demand, hardening security by removing public exposure and closing patch gaps, protecting business continuity with zone-redundant deployments and validated backups, and retiring legacy agents and end-of-life operating systems before they become liabilities.
Spotto's continuous analysis across all five Well-Architected Framework pillars surfaces these opportunities automatically -- flagging stopped-but-allocated VMs still burning compute budget, Premium SSDs running at 5% of provisioned IOPS, machines with 95%+ CPU saturation headed for an outage, and publicly exposed endpoints that have no business being on the internet. The result is a fleet that costs less, performs better, and passes audits without scrambling.
Key Takeaways at a Glance:
Most Azure VM fleets accumulate inefficiency gradually. A VM gets provisioned for a migration, sized generously "just in case," and never revisited. A Premium SSD gets attached because someone copied a template. A public IP gets assigned for a quick test and stays forever. Multiply that across hundreds or thousands of VMs managed by an MSP, and the waste compounds:
For Managed Service Providers managing multiple tenants via Azure Lighthouse, these issues multiply across every customer environment. Without continuous, automated detection, optimization becomes a quarterly fire drill rather than a standing practice.
30 Recommendations Across 5 Well-Architected Framework Pillars
──────────────────────────────────────────────────────────────────────
COST (10) PERFORMANCE (6) SECURITY (5)
├─ Stopped VMs ├─ CPU critical ├─ Public IP exposure
├─ CPU oversized ├─ CPU high ├─ Public access risk
├─ Memory oversized ├─ CPU low/oversized ├─ Network anomalies
├─ Intel → AMD ├─ Memory high ├─ Guest non-compliant
├─ Spot instances ├─ Memory low └─ OS patching gaps
├─ Disk downgrades(4) └─ Size optimization
├─ Disk upgrades (2) RELIABILITY (4) OPS EXCELLENCE (5)
└─ MMA → AMA ├─ No backup ├─ Large OS disk
├─ Stale backup ├─ OS end-of-life
├─ No Avail. Zones ├─ SQL end-of-life
└─ No Site Recovery ├─ SQL disk layout
└─ SQL disk cachingBefore diving into optimization actions, here is the SLA baseline that drives many of these decisions:
| Deployment Model | SLA | Monthly Downtime |
|---|---|---|
| Single Instance (Standard HDD) | 95.0% | ~36 hours |
| Single Instance (Standard SSD) | 99.5% | ~3.6 hours |
| Single Instance (Premium SSD / Ultra) | 99.9% | ~43 minutes |
| Availability Set (2+ VMs) | 99.95% | ~22 minutes |
| Availability Zones (2+ VMs, 2+ zones) | 99.99% | ~4.3 minutes |
Note: Single-instance SLA is determined by the lowest-tier disk attached. One Standard HDD data disk drops the entire VM to 95%.
Are your VMs sized for what they actually do, or for what someone imagined they might do two years ago? Azure VM right-sizing is the highest-impact, lowest-risk cloud cost optimization for most VM fleets. It addresses six distinct patterns of compute waste.
A VM in the "Stopped (allocated)" state continues to reserve host capacity and incur full compute charges -- identical to a running VM. It is essentially paying rent on an apartment nobody lives in. This is the easiest cost recovery in Azure:
PowerState/stopped (not PowerState/deallocated) via Azure Resource Graph# Find all stopped-but-allocated VMs across subscriptions
Search-AzGraph -Query @"
resources
| where type == 'microsoft.compute/virtualmachines'
| where properties.extended.instanceView.powerState.code == 'PowerState/stopped'
| project name, resourceGroup, subscriptionId, location
"@| Charge | Stopped (Allocated) | Stopped (Deallocated) |
|---|---|---|
| Triggered by | Guest OS shutdown | Portal / CLI / PS |
| Compute charges | YES (full rate) | NO |
| Managed disks | YES | YES |
| Static public IPs | YES | YES |
| Host reservation | Retained | Released |
| IP address | Retained | May change (dynamic) |
As of June 2025, Azure enforces a minimum 5-minute billing duration per VM start. Starting and immediately stopping a VM incurs 5 minutes of compute charges.
VMs with sustained CPU utilization below 20% or available memory above 70% for 95% of the past 30 days are likely oversized:
Considerations before resizing: Confirm headroom is not reserved for seasonal peaks, DR failover, or batch jobs. Check application-level memory reservations (SQL Server buffer pools, JVM heap, Redis maxmemory). Verify the target SKU preserves required NIC count, disk count, and accelerated networking. Resize requires VM deallocation -- schedule during maintenance windows.
AMD EPYC-based VMs (Dasv5 series) offer approximately 10% lower on-demand pricing than Intel equivalents (Dsv5 series) at comparable performance:
| Series | Processor | $/hr | $/month | Savings |
|---|---|---|---|---|
| D8s v5 | Intel Xeon | $0.384 | $280 | baseline |
| D8as v5 | AMD EPYC | $0.344 | $251 | ~10% |
On Spot pricing, the AMD advantage widens to ~24%. Blockers to check: AVX-512 dependency, Intel SGX requirements, per-socket licensing tied to specific processor vendors, and region availability for AMD SKUs.
Azure Spot VMs offer 60-90% discounts over on-demand pricing for workloads that can tolerate interruption:
-1 to avoid price-based eviction (only capacity-based). Microsoft reports over 90% of Spot workloads complete before evictionAre you paying Premium prices for Standard workloads, or throttling Standard disks on Premium workloads? Azure disk cost optimization has two directions: downgrade over-provisioned disks to save money, or upgrade throttled disks to fix performance.
Premium SSD disks running at a small fraction of provisioned IOPS represent pure waste. Data disks and OS disks with consistently low IOPS utilization (below 20% of provisioned capacity) can be downgraded to smaller Premium SKUs or Standard SSD for 30-60% savings.
| Feature | Premium SSD (P-series) | Standard SSD (E-series) |
|---|---|---|
| Max IOPS (P30/E30) | 5,000 | 500 |
| Max throughput | 200 MB/s | 60 MB/s |
| Single-instance SLA | 99.9% | 99.5% |
| Cost (256 GiB) | ~$38.40/month | ~$19.20/month |
| Best for | Production, I/O-heavy | Dev/test, low-I/O prod |
If your Premium disk uses less than 500 IOPS consistently, Standard SSD delivers the same performance at roughly half the price.
Disks consistently hitting IOPS or throughput limits cause storage throttling that cascades into application latency. Upgrade options include increasing Premium SSD size (P30 to P40 doubles IOPS from 5,000 to 7,500), Premium SSD v2 for variable demand, or Ultra Disk for extreme I/O (up to 160,000 IOPS, sub-millisecond latency).
| Utilization Level | Signal | Recommended Action |
|---|---|---|
| < 20% (30-day P95) | Over-provisioned | Right-size down |
| 20-70% (30-day P95) | Healthy headroom | Monitor, no action |
| 70-90% (30-day P95) | Elevated | Plan proactive resize |
| > 95% (sustained) | Critical saturation | Scale up/out urgently |
Before scaling up, rule out runaway processes or memory leaks consuming CPU, misconfigured thread pools or connection limits, and cron jobs or scheduled tasks that overlap with peak hours.
Low available memory manifests as kernel paging, garbage collection thrashing, and out-of-memory kills. Before scaling, confirm whether high utilization is intentional (database buffer pools, in-memory caches are supposed to use most of available RAM) and investigate for memory leaks before throwing hardware at the problem. If scaling is needed, memory-optimized SKUs (Esv5 series) provide more RAM per vCPU. Target 20-30% free memory at the 95th percentile as operating headroom.
How many of your VMs are directly reachable from the internet right now -- and how many of those should be? If you manage Azure infrastructure for multiple customers, the honest answer is probably "more than you think."
| Risk Level | Configuration | Time to First Attack |
|---|---|---|
| CRITICAL | Public IP + open RDP/SSH (0.0.0.0) | Minutes |
| HIGH | Public IP + restricted ports | Hours |
| MODERATE | Public IP + NSG allowlist | Days (port scanning) |
| LOW | Private IP + Bastion/VPN only | Requires internal access |
| MINIMAL | Private IP + NSG + no public route | Lateral movement only |
Remediation path:
Both issues breach PCI DSS, SOC 2, ISO 27001, and CIS benchmark requirements. Auditors flag these as high-severity findings.
When was the last time you tested a restore from backup? If the answer takes more than two seconds, that is the problem.
Recovery time: Days to weeks
Data loss: Since last manual copy
Ransomware defense: None
Effort: Full rebuild from docs
Cost: $0
Recovery time: Hours
Data loss: Since last recovery point
Ransomware defense: Soft delete + immutable
Effort: Point-in-time restore
Cost: ~$0.05/GB/month (GRS)
VMs without cross-region disaster recovery depend on manual rebuilds from backup after a regional outage. ASR provides automated failover in minutes with RPO measured in configured replication intervals, at ~$25/month per protected instance (first 31 days free).
The Microsoft Monitoring Agent (MMA/OMS) was officially retired on August 31, 2024. VMs still running MMA face higher ingestion costs, feature gaps, and increasing stability risk.
| Capability | MMA (Retired) | AMA (Current) |
|---|---|---|
| Source-side filtering | No (send all) | Yes (via DCRs) |
| Private Link | No | Yes |
| Configuration method | Workspace-based | Data Collection Rules |
| Cost impact | Higher ingestion | 20-40% lower ingestion |
| Auto-update | No | Yes |
VMs running OS versions past mainstream support no longer receive security patches. The same risk applies to SQL Server versions past mainstream support. Validate application compatibility in a non-production environment before upgrading, and check whether Extended Security Updates are available as a temporary measure.
SQL Server disk misconfiguration is one of the most common -- and most fixable -- performance problems in Azure VM environments.
| Role | Disk Type | Caching | Notes |
|---|---|---|---|
| OS | Standard SSD | ReadWrite | Default, adequate for OS |
| Data | Premium SSD | ReadOnly | Striped pool for large DBs |
| Log | Premium SSD | None | Write-ordering critical |
| TempDB | Local SSD / Premium | None | Startup script if local |
| Backup | Standard HDD | None | Cost-optimized, sequential |
Important: Ultra Disk and Premium SSD v2 do not support host caching. Clustered/shared disks must use None.
| Optimization | Typical Savings | Complexity |
|---|---|---|
| Deallocate stopped VMs | 70-80% per VM | Low |
| Downsize oversized VMs (CPU/memory) | 30-50% per VM | Medium |
| Switch Intel to AMD | ~10% per VM | Medium |
| Adopt Spot for interruptible workloads | 60-90% per VM | Medium |
| Downgrade Premium SSD to Standard SSD | 40-60% per disk | Medium |
| Migrate MMA to AMA (log ingestion) | 20-40% on logs | Medium |
| Remove unnecessary public IPs | $3.65/month each | Low |
Aggregate: A fleet of 200 VMs with typical inefficiencies can recover $30,000-$80,000/month through systematic optimization.
Initial state
800+ VMs across tenants, no standardized optimization
Outcome
$53,000/month recovered
45 stopped-allocated VMs ($18,000/month waste) deallocated immediately. Right-sizing and disk downgrades recovered an additional $35,000/month over 90 days.
Before
$42,000/mo
60 developer VMs running 24/7 on D4s_v5 with Premium SSD
After
$11,000/mo
Auto-shutdown, burstable VMs, Standard SSD
10 SQL Server VMs with data, logs, and tempdb on a single P30 disk per VM. Disk caching set to ReadWrite on all disks including log volumes.
Outcome: SQL query throughput improves 40-60%. WRITELOG waits drop by 70%. Backup windows shorten from 4 hours to 90 minutes due to smaller OS disk snapshots.
Before
$120,000/mo
30 D16s_v5 VMs, all running 24/7 on Premium SSD
After
$45,000/mo
Spot instances, AMD migration, scheduled scaling
# Weekly Azure VM fleet health check
# Run as an Azure Automation runbook on a weekly schedule
# Requires: Az.ResourceGraph, Az.Compute, Az.Monitor modules
Connect-AzAccount -Identity
# 1. Find stopped-but-allocated VMs
$stoppedAllocated = Search-AzGraph -Query @"
resources
| where type == 'microsoft.compute/virtualmachines'
| where properties.extended.instanceView.powerState.code == 'PowerState/stopped'
| project name, resourceGroup, subscriptionId, location,
vmSize = tostring(properties.hardwareProfile.vmSize)
"@
# 2. Find VMs with public IPs
$publicIpVms = Search-AzGraph -Query @"
resources
| where type == 'microsoft.network/publicipaddresses'
| where isnotempty(properties.ipConfiguration.id)
| extend nicId = tostring(split(properties.ipConfiguration.id, '/ipConfigurations/')[0])
| join kind=inner (
resources
| where type == 'microsoft.compute/virtualmachines'
| mv-expand nic = properties.networkProfile.networkInterfaces
| extend nicId = tostring(nic.id)
) on nicId
| project vmName=name1, publicIp=properties.ipAddress
"@
# 3. Find VMs without backup
$noBackup = Search-AzGraph -Query @"
resources
| where type == 'microsoft.compute/virtualmachines'
| join kind=leftanti (
recoveryservicesresources
| where type =~ 'microsoft.recoveryservices/vaults/backupfabrics/protectioncontainers/protecteditems'
| where properties.backupManagementType == 'AzureIaasVM'
| extend vmId = tolower(tostring(properties.sourceResourceId))
) on \$left.id == \$right.vmId
| project name, resourceGroup, subscriptionId, location
"@
Write-Output "=== VM Fleet Health Check ==="
Write-Output "Stopped (allocated): $($stoppedAllocated.Count) VMs"
Write-Output "Public IP attached: $($publicIpVms.Count) VMs"
Write-Output "No backup: $($noBackup.Count) VMs"Use Azure Resource Graph with properties.extended.instanceView.powerState.code == 'PowerState/stopped'. This query works across all subscriptions accessible to the caller, including Lighthouse-delegated tenants. Deallocated VMs show PowerState/deallocated and do not incur compute charges.
Typical range: 30-50% per VM when dropping one or two size tiers. Example: D8s_v5 (8 vCPU / 32 GiB) at ~$280/month to D4s_v5 (4 vCPU / 16 GiB) at ~$140/month. Across 200 oversized VMs at 40% average savings: ~$22,400/month. Always validate against 4-6 weeks of telemetry including peak periods, batch runs, and month-end processing.
For workloads with low I/O demand (less than 20% of provisioned IOPS), generally yes. Key considerations: single-instance SLA drops from 99.9% to 99.5%, boot time may increase slightly, and backup/ASR replication windows may extend. Always snapshot the disk before migrating and test in non-production first.
Right-size first, then commit. Reserving an oversized VM locks in waste for 1-3 years. After right-sizing, evaluate Reserved Instances for stable workloads (up to 72% savings) or Savings Plans for dynamic environments (up to 65% savings).
Start with the highest-cost, lowest-risk wins: (1) Deallocate stopped VMs, (2) Downgrade under-utilized Premium disks, (3) Right-size oversized VMs with consistent low utilization, (4) Migrate MMA to AMA, (5) Address security gaps.
Not all VMs warrant zone-redundant deployment. Production workloads with SLA requirements: yes, target 99.99%. Dev/test environments: probably not worth the added cost and complexity. Stateful single-instance workloads: evaluate ASR as an alternative to multi-zone deployment.
The challenges outlined in this article — stopped-but-allocated VMs silently burning compute budget, Premium SSDs running at a fraction of provisioned IOPS, publicly exposed endpoints that should be behind Bastion — are the same ones our customers work through across their Azure environments. The difference is they use Spotto to surface these opportunities automatically, rather than running the analysis by hand.
Spotto continuously analyzes your Azure VM fleet across all 30 optimization dimensions -- from stopped-allocated VMs and disk tier misalignment to missing backups and end-of-life operating systems -- surfacing prioritized actions with estimated cost impact and implementation risk.
Free TrialDisclaimer: This article is provided for informational purposes only and does not constitute professional advice. Azure pricing, features, and service behaviour may have changed since publication. Always validate recommendations against your specific environment and consult Microsoft's official documentation before making changes to production infrastructure. Spotto is not liable for any actions taken based on the content of this article.