AzureAzure Virtual Desktopmonitoringlog-analytics

Monitoring Azure Virtual Desktop: What to Watch and What to Ignore

Parveen Singh
July 3, 2026
8 min read
Monitoring Azure Virtual Desktop: What to Watch and What to Ignore

The most frustrating support ticket in AVD is "it's slow". Every dashboard is green, the host pool has capacity, CPU is at 30 percent, and a user is sitting there watching a spinner. I have been on calls where three engineers stared at a healthy-looking Insights workbook while the person on the other end could not open a spreadsheet.

The reason is that the default monitoring answers a question nobody asked. It tells you whether the infrastructure is up. Users do not experience infrastructure, they experience sign-in time and input lag, and neither of those is a VM metric. You can run a host pool at 20 percent CPU and deliver a terrible session.

This post is about the small number of signals that actually correlate with user experience, how to collect them, and the much larger number of metrics that will fill a dashboard and tell you nothing. I have kept it to what I check first when someone says the environment is slow.


The Four Signals That Matter

Everything else is supporting evidence. These four are what I look at, in this order.

SignalWhere it livesWhat a bad number means
Sign-in durationWVDConnectionsProfile, storage or image problem
Round trip timeWVDConnectionNetworkDataNetwork path, not the host
Available memory per sessionPerformance countersHost is oversubscribed
Profile attach timeFSLogix event logShare is undersized

Notice what is not on that list. CPU percentage is not there, because a pooled host at 90 percent CPU with responsive sessions is fine and a host at 40 percent with memory pressure is not. Disk queue length is not there either, because on a modern premium disk it is almost never your problem.


Step 1: Enable Diagnostics on the AVD Objects

This is the part people half do. Diagnostics are enabled per Azure Resource Manager object, so enabling them on the host pool and stopping leaves you blind on the workspace and the application groups.

RG="rg-avd-prod"
POOL="hp-avd-prod"
WORKSPACE="ws-avd-prod"
LAW_ID=$(az monitor log-analytics workspace show \
  --resource-group $RG --workspace-name "law-avd" --query id -o tsv)

# Host pool: connections, errors, checkpoints, agent health
az monitor diagnostic-settings create \
  --name "diag-avd" \
  --resource "/subscriptions/$SUB_ID/resourceGroups/$RG/providers/Microsoft.DesktopVirtualization/hostPools/$POOL" \
  --workspace $LAW_ID \
  --logs '[
    {"category":"Checkpoint","enabled":true},
    {"category":"Error","enabled":true},
    {"category":"Management","enabled":true},
    {"category":"Connection","enabled":true},
    {"category":"HostRegistration","enabled":true},
    {"category":"AgentHealthStatus","enabled":true},
    {"category":"NetworkData","enabled":true},
    {"category":"ConnectionGraphicsData","enabled":true}
  ]'

NetworkData is the one worth calling out. It is what populates round trip time, and it is off by default. Without it you cannot distinguish "the host is slow" from "the user's home internet is slow", which is the single most common diagnostic fork in AVD support.

Repeat the same command against the workspace and each application group. They emit different categories and the workbook stitches them together.

Gotcha: Diagnostic settings on the host pool do not cover the session hosts. Those are virtual machines, and their performance counters come from the Azure Monitor Agent with a data collection rule. Two separate pipelines, and Insights needs both.


Step 2: Collect the Right Performance Counters

The session host half runs through a data collection rule. The temptation is to collect everything, and everything is expensive and mostly noise.

This is the set I actually use:

az monitor data-collection rule create \
  --resource-group $RG \
  --name "dcr-avd-hosts" \
  --location canadacentral \
  --rule-file dcr-avd.json

With dcr-avd.json carrying the counters that map to the four signals:

{
  "dataSources": {
    "performanceCounters": [
      {
        "name": "avdCounters",
        "streams": ["Microsoft-Perf"],
        "samplingFrequencyInSeconds": 60,
        "counterSpecifiers": [
          "\\Memory\\Available Mbytes",
          "\\Memory\\Page Faults/sec",
          "\\Processor Information(_Total)\\% Processor Time",
          "\\LogicalDisk(C:)\\Avg. Disk sec/Transfer",
          "\\User Input Delay per Session(*)\\Max Input Delay",
          "\\Terminal Services\\Active Sessions",
          "\\RemoteFX Network(*)\\Current TCP RTT"
        ]
      }
    ]
  }
}

User Input Delay per Session is the counter almost nobody collects and the one that most directly measures what a user feels. It reports how long input sat in the queue before the session processed it. Under about 100 milliseconds the session feels fine. Past 500 the user will describe the machine as broken, regardless of what CPU says.

Pro tip: Sample at 60 seconds, not 15. AVD counters at 15 second intervals across fifty hosts generate a Log Analytics bill that will get you a meeting with finance, and profile and memory problems do not resolve on a 15 second scale anyway.


Step 3: The Queries I Actually Run

The Insights workbook is fine for browsing. When something is wrong I go straight to KQL.

Sign-in duration, broken down by phase. This tells you whether the delay is the broker, the host, or the profile.

WVDConnections
| where TimeGenerated > ago(24h)
| where State == "Connected"
| extend SessionId = tostring(CorrelationId)
| join kind=inner (
    WVDCheckpoints
    | where Name == "LoadBalancedNewConnection"
    | project CorrelationId, BrokerTime = TimeGenerated
) on $left.SessionId == $right.CorrelationId
| extend SignInSeconds = datetime_diff('second', TimeGenerated, BrokerTime)
| summarize p50 = percentile(SignInSeconds, 50),
            p95 = percentile(SignInSeconds, 95),
            Sessions = count()
    by SessionHostName
| order by p95 desc

Look at p95, not the average. The average hides the login storm, and the login storm is when people complain.

Round trip time by user. This is the query that ends the "is it us or them" argument.

WVDConnectionNetworkData
| where TimeGenerated > ago(7d)
| summarize AvgRTT = avg(EstRoundTripTimeInMs),
            P95RTT = percentile(EstRoundTripTimeInMs, 95),
            Sessions = dcount(CorrelationId)
    by UserName = tostring(split(UserName, "@")[0])
| where Sessions > 5
| order by P95RTT desc
| take 25

Under 50 milliseconds is good. Between 50 and 100 is usable. Past 150 the user is going to describe typing lag no matter how much you scale the host pool, and the fix is a network conversation rather than an Azure one.

Memory headroom per host. Oversubscription shows up here long before it shows up in complaints.

Perf
| where TimeGenerated > ago(24h)
| where CounterName == "Available MBytes"
| summarize MinAvailableMB = min(CounterValue),
            AvgAvailableMB = avg(CounterValue)
    by Computer
| where MinAvailableMB < 2048
| order by MinAvailableMB asc

Any host dipping under 2 GB available during business hours is over its limit. That is where you either reduce max session limit or move to a larger SKU.


Step 4: Alert on Symptoms, Not Causes

Most AVD alerting I inherit fires on CPU and gets muted within a fortnight because it is noisy and does not correlate with anything. Alert on the thing the user would report.

az monitor scheduled-query create \
  --name "avd-signin-degraded" \
  --resource-group $RG \
  --scopes $LAW_ID \
  --condition "count 'signins' > 0" \
  --condition-query signins='
    WVDConnections
    | where TimeGenerated > ago(15m)
    | where State == "Connected"
    | summarize p95 = percentile(SessionDurationSeconds, 95)
    | where p95 > 90' \
  --evaluation-frequency 15m \
  --window-size 15m \
  --severity 2 \
  --description "Sign-in p95 above 90 seconds over the last 15 minutes"

Three alerts is usually the right number for a pool: sign-in degraded, agent unhealthy, and profile share latency. Everything else is a dashboard you look at when one of those three fires.

Warning: Do not alert on session host CPU. On a pooled host, high CPU is the system doing its job. You will train your team to ignore AVD alerts within a month, and then the agent health alert that actually matters gets ignored too.


Watch the Storage, Not Just the Hosts

The most common cause of slow sign-ins is not the session host at all. It is the profile share, and it sits outside every AVD-specific dashboard.

AzureMetrics
| where ResourceProvider == "MICROSOFT.STORAGE"
| where MetricName in ("SuccessE2ELatency", "Transactions")
| where TimeGenerated > ago(24h)
| summarize AvgLatencyMs = avgif(Average, MetricName == "SuccessE2ELatency"),
            TotalTransactions = sumif(Total, MetricName == "Transactions")
    by bin(TimeGenerated, 15m)
| order by TimeGenerated asc

If SuccessE2ELatency climbs during your morning peak, the share is throttling and every user is paying for it at sign-in. On a premium file share the fix is to increase the quota, because provisioned IOPS scale with size.


What I Ignore

Being explicit about this, because dashboards accumulate.

CPU percentage as a health signal. Useful for capacity planning over weeks. Useless as an alert.

Disk queue length. A holdover from spinning disks. On premium SSD it is almost never the constraint, and when it is, latency shows it more clearly.

Session count per host in isolation. Twenty light users and eight heavy ones are different loads. Watch memory headroom instead, which reflects the actual demand.

The default Insights "availability" tile. It tells you the agent is reporting. An agent can report happily while the session it hosts is unusable.


Conclusions

Monitoring AVD well is mostly about resisting the urge to collect everything. Four signals cover the vast majority of real incidents: how long sign-in takes, what the network round trip looks like, whether the host has memory left, and whether the profile share is keeping up.

Turn on NetworkData, collect User Input Delay per Session, watch the storage account alongside the hosts, and alert on symptoms rather than causes. That gets you to a place where a green dashboard actually means something.

What's Next

  • Scaling plans to reduce the host count outside business hours, which changes both the bill and the memory headroom picture
  • FSLogix on Azure Files if profile attach time is where your sign-in delay is concentrated
  • App attach to keep the image thin, which shortens the host build and rebuild cycle

Recommended Readings