Azure VM Comparator
A GitHub Copilot Chat participant that explains why one Azure VM is faster than another.

Same VNet, same proximity placement group, same SKU on paper — yet one VM runs 3× slower than its peers. @vmcompare collects the full configuration and live metrics for every VM you name, diffs them property by property, ranks each delta by impact, and asks Copilot to write a TL;DR with copy-paste az fix commands.
Read-only. No agents on the VM. No data leaves your machine except the structured diff sent to your in-process Copilot model.
Contents
Quick start
- Install Azure VM Comparator from the VS Code Marketplace (or sideload the
.vsix from Releases).
- Sign in to Azure CLI:
az login (Reader on every target subscription is enough).
- Open the Copilot Chat view in VS Code and type:
@vmcompare vm-fast vm-slow --rg my-resource-group
The report streams into chat and is also saved to ./vm-compare-report.md in your workspace, with raw per-VM JSON in ./vm-compare-raw/.
Usage
Basic compare
@vmcompare vm-a vm-b --rg my-resource-group
Compare more than two VMs and a longer metric window:
@vmcompare vm-a vm-b vm-c --rg my-resource-group --metrics 7d
Cross-resource-group / cross-subscription
Use the vm@rg shorthand or full Azure resource IDs — no --rg needed:
# Different resource groups, same subscription
@vmcompare vm-fast@rg-A vm-slow@rg-B
# Different subscriptions
@vmcompare vm-fast@rg-A/<subId-1> vm-slow@rg-B/<subId-2>
# Or paste full resource IDs straight from the portal
@vmcompare /subscriptions/<id>/resourceGroups/.../virtualMachines/vm-fast \
/subscriptions/<id2>/resourceGroups/.../virtualMachines/vm-slow
You only need Reader on each subscription; the extension picks the right --subscription per call automatically.
Anchor on a baseline VM
When you already know which VM is healthy, name it as the baseline. The narrative reframes every other VM as deviations from the baseline, and the baseline column is marked with ⭐ in every diff table:
@vmcompare vm-fast vm-slow1 vm-slow2 --rg appservers --baseline vm-fast
Skip the typing and let @vmcompare resolve the target list:
# Every VM in --rg with role=web
@vmcompare --rg appservers --tag role=web
# Every instance of a Virtual Machine Scale Set, anchored on instance 0
@vmcompare --rg appservers --vmss web-vmss --baseline web-vmss_0
Flag reference
| Flag |
Aliases |
Description |
<vm-spec> (positional) |
— |
Bare name (needs --rg), vm@rg, vm@rg/sub, or full /subscriptions/.../virtualMachines/<name> resource ID. Repeatable. |
--rg <name> |
-g |
Default resource group for bare VM names and for --tag / --vmss selectors. |
--sub <id> |
-s |
Default subscription for bare names and shorthand without /sub. |
--metrics 1h\|24h\|7d |
— |
Time window for Azure Monitor metric collection. Default 24h. |
--baseline <vm> |
-b |
Treat this VM as the known-good reference; report frames others as deviations from it. |
--tag k=v |
— |
Auto-include every VM (in --rg or whole subscription) carrying the given tag. Repeatable. |
--vmss <name> |
— |
Auto-include every instance of a VM Scale Set. Requires --rg. |
--output <path> |
-o |
Override report save path (workspace-relative). |
What it compares
Compute — VM size, OS family/image SKU/version, region, availability zone, proximity placement group, dedicated host, host group, priority (Spot vs Regular), security type (Trusted/Confidential Launch).
Network (per NIC) — accelerated networking, IP forwarding, subnet, NSG, effective NSG rule count, effective route table (non-default routes), custom DNS.
Storage (per disk) — SKU, performance tier, size, provisioned IOPS, provisioned throughput, caching mode, write accelerator, on-demand bursting. OS disk and every data disk by LUN.
Identity & extensions — managed identity type, installed VM extensions (sorted, deduped).
Live metrics (over --metrics window) — CPU avg/max, available memory, OS/data disk queue depth, VM Uncached/Cached IOPS Consumed %, VM Uncached/Cached Bandwidth Consumed % (the throttle counters), network in/out. Anything over 80% on a Consumed Percentage counter is flagged 🚨.
Resource Health — current availability state and reason.
Each diff carries a severity (CRITICAL / HIGH / MEDIUM / LOW) and a one-line "why it matters" explanation. Common CRITICAL flags include accelerated networking off, Standard SSD where peers run Premium SSD v2, missing write accelerator, wrong caching mode, different PPG, different zone, Spot vs Regular.
Sample report
# VM Comparison Report
Subscription: prod-eastus · Baseline: vm-fast ⭐
Properties matched: 41 · Differences: 5 (1 🔴, 2 🟠, 2 🟡)
## TL;DR
vm-slow runs ~3× slower because Accelerated Networking is OFF (vm-fast: ON)
and its data disk LUN 0 is Premium_LRS P30 (5,000 IOPS) vs vm-fast's
Premium_SSD_v2 with 16,000 provisioned IOPS. VM Uncached IOPS Consumed
peaked at 97% on vm-slow during the last 24h vs 41% on vm-fast.
## 🧭 Likely root cause
1. **Accelerated Networking disabled on vm-slow** (confidence: high)
Fix: az network nic update -g rg-A -n vm-slow-nic --accelerated-networking true
# then restart the VM
2. **Underprovisioned data disk on vm-slow** (confidence: high)
Fix: az disk update -g rg-A -n vm-slow-data0 --sku PremiumV2_LRS \
--disk-iops-read-write 16000 --disk-mbps-read-write 800
## 🔴 Critical differences
| Property | vm-fast ⭐ | vm-slow | Why it matters |
|----------------------------|-------------------|-----------------|-------------------------------|
| NIC0: Accelerated Networking | ✅ true | ❌ false | SR-IOV bypass; ~10× lower lat |
| Data disk LUN 0: SKU | PremiumV2_LRS | Premium_LRS | Order-of-magnitude IOPS gap |
(The real report also includes 🟠 high, collapsible 🟡⚪ medium/low, a live-metrics table with 🚨 throttle flags, Resource Health, and an appendix.)
Settings
| Setting |
Default |
Description |
vmcompare.metricsWindow |
24h |
Default metric time window: 1h, 24h, or 7d |
vmcompare.outputPath |
./vm-compare-report.md |
Workspace-relative path for the saved report |
vmcompare.azCliPath |
az |
Path to the Azure CLI executable |
Prerequisites
- Visual Studio Code 1.95 or later with GitHub Copilot Chat enabled
- Azure CLI (
az) installed and signed in (az login)
- Reader role on every subscription containing a target VM
- Two or more VMs to compare (any combination of resource groups or subscriptions)
Privacy and safety
- Read-only. Never invokes a mutating
az command. The fix commands in the report are emitted as text for you to review and run yourself.
- Local only. Talks to Azure exclusively through your existing
az CLI session. No third-party network calls.
- Your own model. Narration uses VS Code's in-process
LanguageModelChatMessage API — same Copilot model already authorized in your editor.
- No telemetry. The extension does not collect or transmit usage data.
- Generated reports (
vm-compare-report.md, vm-compare-raw/*.json) may contain VM names, IPs, NSG rule contents, and subscription IDs. Treat them like any other infrastructure dump — they are git-ignored by default in this repo and excluded from the published .vsix.
Troubleshooting
Azure CLI not signed in or not installed — run az login in a terminal, then re-run the chat command. If you have multiple tenants, also run az account set --subscription <id>.
VM not found — double-check the VM name and --rg. For cross-subscription targets use vm@rg/<subId> or a full resource ID.
Metrics show as — — the VM may not have generated metrics in the requested window (recently started, deallocated, or first-time deployment). Try a wider --metrics 7d.
--vmss <name> fails — VMSS expansion requires --rg.
The chat participant doesn't appear — ensure GitHub Copilot Chat is installed and enabled, then reload the window (Ctrl+Shift+P → Developer: Reload Window).
Contributing
Issues and PRs welcome at https://github.com/vaddify/azure-vm-comparator. Especially useful contributions: more diff rules in src/diff.ts, additional Azure Monitor counters in src/azure.ts, and screenshots of real reports for the README.
Local development:
npm install
npm run compile
# Press F5 in VS Code to launch an Extension Development Host
Package a .vsix:
npx @vscode/vsce package
License
MIT