Microsoft 365 license audit: a step-by-step checklist (2026)
A Microsoft 365 license audit doesn't need a platform or a consultant — it needs a repeatable, read-only process. This is the checklist we'd run on any tenant: inventory what you own, find what's wasted, spot what can be downgraded, and put a dollar figure on it. Everything here is read-only and runs inside your own tenant.
Install-Module Microsoft.Graph -Scope CurrentUser. Sign-in activity additionally needs Microsoft Entra ID P1.The checklist
- Inventory your SKUs. List every license you've purchased vs. assigned. The gap is unassigned waste.
Get-MgSubscribedSkugivesprepaidUnits.enabled(bought) vsconsumedUnits(assigned). - Map SKUs to real prices. Use your contracted/regional price, not just list. Watch the legacy names — Microsoft 365 Business Standard's part number is literally
O365_BUSINESS_PREMIUM. - Find unassigned seats. Any SKU where purchased > assigned is money spent on nothing. The cleanest win — reduce quantity at (or before) renewal.
- Find disabled-but-licensed accounts. Blocked sign-in doesn't stop billing. Anyone with
accountEnabled = falsewho still holds a license is pure waste. - Find never-signed-in users. Licensed accounts with no sign-in ever (and old enough to be meaningful) — provisioned and forgotten.
- Find inactive users. No sign-in in your chosen window (30/60/90 days). Reclaim or reassign after checking with their manager.
- Spot overlapping plans. Users carrying two base plans (e.g. E3 + E5) — drop the redundant one.
- Spot downgrade candidates. E5 seats not using premium security/compliance/Power BI/voice can move to E3 — $21/user/month. (full E5-vs-E3 math →)
- Quantify it. Multiply each reclaimable seat by its price × 12. "47 inactive users" is a shrug; "$X,000/year recoverable" gets a decision.
- Remediate, then schedule. Reclaim at renewal, remove licenses from disabled accounts, and re-run monthly — waste creeps back the moment offboarding slips.
The starter queries
Connect-MgGraph -Scopes "Organization.Read.All","User.Read.All","Directory.Read.All","AuditLog.Read.All"
# 1) Purchased vs assigned per SKU
Get-MgSubscribedSku | Select SkuPartNumber,
@{n='Purchased';e={$_.PrepaidUnits.Enabled}}, @{n='Assigned';e={$_.ConsumedUnits}}
# 2) Licensed users + status + last sign-in
Get-MgUser -All -Property displayName,userPrincipalName,accountEnabled,assignedLicenses,signInActivity |
Where-Object { $_.AssignedLicenses.Count -gt 0 }
signInActivity on a tenant without Entra ID P1 returns a 403 for the whole query (retry without it); and free/self-service SKUs report a giant "unlimited" prepaid count you must exclude before summing unassigned seats.Do it automatically
That's the whole audit — but assembling the scripts, handling the gotchas, pricing each seat, and formatting a report takes an afternoon each time.
Run the whole checklist in one command
SeatScout does every step above read-only and produces a dollar-quantified, CFO-ready report (plus CSVs and a remediation checklist). Free tier available.
Get SeatScout →Related: How to find wasted M365 licenses · Offset the 2026 price increase · Best M365 license tools compared
SeatScout is independent and not affiliated with Microsoft. Prices referenced are public list prices; your agreement may differ.