Azure Savings Plans for Compute went generally available in May, landing in our queue at an awkward moment: three clients had just gotten their April Azure invoices and wanted to know why an Azure Virtual Desktop estate — built as temporary pandemic infrastructure — was still the largest line on the bill. AVD host pools stood up in a hurry in 2020 and 2021, sized for the worst Monday anyone could imagine and left on wizard-default autoscale, are three budget cycles old now, and June is when finance stops accepting "it's cloud, it optimizes itself" as an answer.
We ran a version of this review for a 3,000-seat regional insurer in May and a 900-seat professional services firm the month before — nobody had done anything indefensible, they had simply never revisited the host pool once the project that stood it up closed out.
This is the order we work in when a client asks us to find the waste: where the spend actually sits, what autoscale is supposed to do versus what it's usually doing, when a reservation or the newer savings plan pays for itself, how we right-size session hosts without a fresh round of help-desk tickets, and the numbers we watch afterward so the savings survive next quarter.
Where the AVD bill actually lands
Compute is the number everyone stares at, and it's the biggest single line, but it's rarely the reason a bill runs high. Three things we check before touching a scaling plan:
Orphaned disks. Every golden-image refresh leaves a trail — managed disks from decommissioned hosts, snapshot chains nobody deleted, OS disks still on Premium SSD from an old sysprep template. We routinely find four figures a month in pure disk waste on estates nobody suspected had a storage problem.
FSLogix profile storage, which people forget to re-price. A container share sized for the pilot's fifty users rarely gets revisited at real headcount, and Azure Files premium tier bills on provisioned capacity whether or not it's used.
Personal desktops that never sleep. Pooled pools get the autoscale attention; one-to-one personal desktops routinely don't, because nobody wants to be the one who shuts down a VP's desktop mid-session. Start VM on Connect handles wake-up; the sleep half is usually missing.
Autoscale scaling plans, configured to actually scale down
Scaling plans for pooled host pools have been generally available long enough that "we don't have autoscale" is rare to hear now. "We have autoscale that never scales anything down" is not rare at all, and it's almost always the same root cause: the load-balancing algorithm chosen for each phase.
A scaling plan has four phases — ramp-up, peak hours, ramp-down, off-peak — and each lets you choose breadth-first or depth-first placement independently. Breadth-first spreads new sessions across every host, right for ramp-up and peak so no single host gets hammered while everyone signs in at once. Depth-first fills one host to its capacity threshold before starting the next — right for ramp-down and off-peak, because it consolidates users onto the fewest hosts and lets the rest drain to zero and deallocate.
The insurer's estate had breadth-first configured on all four phases, almost certainly a wizard default nobody revisited. At 11 p.m., with forty active users still connected, those forty sat spread almost evenly across eighteen hosts instead of packed onto four. Nothing ever emptied out far enough to shut down. Changing the algorithm on two of the four phases, nothing else, took the nightly host count from eighteen to four.
A rough version of the schedule we landed on:
Ramp-up 06:00 threshold 70% breadth-first min hosts 20%
Peak 08:00 threshold 80% breadth-first min hosts 40%
Ramp-down 17:00 threshold 60% depth-first min hosts 10%
Off-peak 20:00 threshold 60% depth-first min hosts 5%
One more gap: scaling plans only cover pooled pools. For personal desktops we still pair Start VM on Connect with a scheduled Azure Automation runbook calling the same Az.DesktopVirtualization cmdlets, checking sign-in state before any shutdown.
Reserved instances vs. the new savings-plan math
Azure Savings Plans for Compute reaching general availability in May changes the reservation conversation for AVD, because the old advice — reserve the baseline, pay as you go for burst — was always in tension with the right-sizing work above.
A reserved instance is a bet on a specific VM size, family, and region for one or three years; it pays off best when that bet doesn't move. A savings plan is a bet on an hourly dollar amount of compute spend that Azure applies automatically to whatever size, family, or region is running that hour, including a resize made mid-term. The savings-plan discount typically lands a little behind a matched reservation, but a little behind on a size that's still correct beats a deep discount on a size the pool abandoned months ago.
Our current default for AVD capacity:
| Capacity | Commitment vehicle | Why |
|---|---|---|
| Personal desktops, stable assignment | Reserved instance, 1-year | Size rarely changes once assigned |
| Pooled off-peak floor (never scales to zero) | Reserved instance, 1-year | Predictable count; revisit before renewal |
| Pooled ramp-up/peak burst capacity | Savings plan | Size and host count still moving |
| New or unproven pools | Pay-as-you-go | No commitment until sizing is finished |
Renewal discipline matters more than the initial purchase. We calendar a reminder thirty days ahead of every renewal, tied to the current right-sizing review, because the fastest way to lose the discount is auto-renewing against a size the pool moved off two quarters ago. The insurer's blended purchase landed close to the 35 percent average infrastructure cost reduction we've tracked across engagements for years — AVD isn't special, it just makes the waste easier to see because the host count sits right in the portal.
Right-sizing session hosts without creating new tickets
The biggest lever after autoscale is admitting one host pool sized for the heaviest user subsidizes everyone else's compute. The professional-services firm mentioned earlier ran exactly one pool, sized around a handful of estimators running heavy spreadsheet models and CAD viewers, with every task worker doing email and light line-of-business work riding the same oversized VM series. Splitting the population into profiles, each with its own host pool and scaling plan, is unglamorous work and where most durable savings live.
Our starting targets, before pilot tuning:
| User profile | vCPU per user | RAM per user | Typical VM series | Users per host (Windows 11 multi-session) |
|---|---|---|---|---|
| Task worker | about 1:6 | about 2 GB | Dv5, general purpose | 12-18 |
| Knowledge worker | about 1:4 | about 4 GB | Dv5 or Ev5 | 8-12 |
| Power user (CAD, modeling) | about 1:1 to 1:2 | 8 GB or more | Ev5, or NVadsA10 v5 for GPU | 1-4, or personal |
Skipping the profiling step produces two failure modes: task workers overspending on a power-user pool, and power users under-provisioned into a pool sized for the average — which surfaces as tickets about AVD being slow that never trace back to sizing, because average CPU utilization looks fine across eleven other people having a normal day. We pull session-host CPU, memory, and GPU counters from the AVD Insights workbook before and after any resize; averages hide the latency spikes that generate tickets.
Storage tiers and the login-storm tax
FSLogix put user profiles on network storage so pooled desktops could be genuinely stateless, which moved the login-performance problem from local disk to whatever share holds the profile containers. Provisioned IOPS on Azure Files premium tier scale with share size, not with how many people log in at once — so a share right-sized for capacity during the pilot quietly becomes a throughput bottleneck once the whole department signs in inside the same fifteen-minute window every Monday.
The symptom is always the same: help-desk tickets about logons hanging on "applying user settings," clustered around 08:00 to 08:15, that nobody connects to storage because the dashboard shows the share is nowhere near full. Capacity and performance are different axes on Azure Files premium tier, and a share can have plenty of room left and still be starved for IOPS during a login storm.
Standard-tier Azure Files is fine for a pilot. Once a pool carries real concurrent logon volume, we move FSLogix containers to premium shares sized for the IOPS the ramp-up window needs, not the capacity the profiles consume; for the largest estates, Azure NetApp Files is the better fit because its performance tiers scale independently of the capacity pool.
The other recurring cost in profile storage has nothing to do with tier: orphaned VHDX containers for users who left eighteen months ago, still billed at whatever tier the share runs, because profile cleanup was never wired into the offboarding checklist next to the mailbox and license steps everyone already remembers.
The telemetry that turns opinions into cost decisions
None of the levers above survive without a number attached, and the number that changes behavior is cost per named user per month — not cost per host, not the total AVD line on the invoice. A host-hour number lets an autoscale fix look successful just because the host count dropped.
Getting there needs two things most estates haven't wired together: consistent tags on every VM and disk — host pool name, user profile category, cost center — so Azure Cost Management + Billing can slice spend by pool instead of resource group alone, and an active-user count from the same period to divide by. We pull the user count straight from the Az.DesktopVirtualization module rather than wait on a Log Analytics ingestion delay:
Get-AzWvdHostPool -ResourceGroupName "rg-avd-prod" | ForEach-Object {
$sessions = Get-AzWvdUserSession -ResourceGroupName "rg-avd-prod" -HostPoolName $_.Name
[PSCustomObject]@{
HostPool = $_.Name
ActiveUsers = ($sessions.UserPrincipalName | Select-Object -Unique).Count
}
}
Divide the monthly Cost Management export for that resource group by the active-user count above and you have the number worth reporting upward. Clients running Nerdio Manager for Enterprise get a version of this without building it — its cost reporting attributes spend by pool and by user on top of its own autoscale engine, which some estates run instead of native scaling plans. We stay vendor-agnostic about which engine does the scaling as long as somebody owns the number afterward: the estates that keep the savings are the ones where a named person reviews cost-per-user monthly, not the ones that ran one optimization pass and moved on.
If you're facing this
If your AVD estate grew fast in 2020 and 2021 and nobody has gone back to match host pool size, autoscale configuration, and storage tier to how people actually use it now, the FinOps review is usually a two-to-three week engagement, not a re-architecture. Bring us last month's Azure invoice by resource group and your current scaling plan configuration; we can usually tell you within the first week which levers will move the number before you commit to a reservation or savings plan you might regret. Reach out if you want a second set of eyes on the math before your next renewal date.