TL;DR: Two one-liners that turn the verbose Get-StorageQoSFlow output into a clean per-VM table of live IOPS, latency, and bandwidth on an Azure Local / Storage Spaces Direct cluster — either for the local node only, or for every VM across every node.
Recommended action:
Open a PowerShell prompt on any cluster node (or remote in via Enter-PSSession).
For the VMs currently running on the local node:
Get-StorageQoSFlow | Select-Object `
@{name='VM Name';expression={$_.InitiatorName}}, `
@{name='IOPS';expression={$_.InitiatorIOPS}}, `
@{name='Latency (ms)';expression={$_.InitiatorLatency}}, `
@{name='Bandwidth (MB/s)';expression={$_.InitiatorBandwidth}}
For every VM across every node in the cluster (run from any node):
Get-StorageQoSFlow -CimSession (Get-ClusterNode).Name | Select-Object `
@{name='Node';expression={$_.PSComputerName}}, `
@{name='VM Name';expression={$_.InitiatorName}}, `
@{name='IOPS';expression={$_.InitiatorIOPS}}, `
@{name='Latency (ms)';expression={$_.InitiatorLatency}}, `
@{name='Bandwidth (MB/s)';expression={$_.InitiatorBandwidth}} |
Sort-Object Node, 'VM Name'
Each row in the output is one active I/O flow from a VM to a storage file, with current performance counters as seen from the initiator (VM) side. The clusterwide form fans out via CimSession to every cluster node and adds a Node column so you can see which host owns each VM.
Why:
Get-StorageQoSFlow returns the active storage I/O flows the Storage QoS resource is tracking. The default output is wide and includes GUIDs, policy IDs, and target-side counters that are noisy for quick triage. Renaming the four most useful initiator-side properties to friendly column headers gives a clean snapshot of which VMs are doing storage work and how that work is performing — useful for spotting “noisy neighbor” VMs, validating that a Storage QoS policy is taking effect, or sanity-checking a “my VM is slow” complaint against the actual numbers.
Going forward:
Why two forms. Each Hyper-V host tracks its own initiator-side flows, so a local
Get-StorageQoSFlowcall only returns flows for the VMs currently running on that node. The clusterwide form uses CimSession fan-out to query every node in one pipeline. This relies on WinRM / WS-Man, which is already required for normal cluster operation, so no extra setup is needed.Real-time snapshot, not history. Values change second to second. For a transient spike, run the command repeatedly (or in a loop with
while ($true) { ...; Start-Sleep 2; cls }). For sustained baselining and trending, use cluster Performance History (Get-ClusterPerf) instead. Idle VMs will not appear because there is no active flow to report.
Optional details:
InitiatorBandwidthis reported in bytes per second, not megabytes per second. The “MB/s” column label is for readability only — divide the value by1MBif you need true MB/s.To filter to a single VM, append
| Where-Object {$_.InitiatorName -eq 'MyVM'}.To sort by hottest VM, sort on
IOPSafter the Select, e.g.... | Sort-Object IOPS -Descending.To see every available property (including target-side IOPS / latency / bandwidth, which can diverge from initiator-side under contention) run
Get-StorageQoSFlow | Format-List *.Works on any Storage QoS-enabled cluster: Azure Local, Azure Stack HCI, and Storage Spaces Direct on Windows Server 2016 and later.