To find unused licences in Microsoft 365, subtract the licences assigned from the licences bought for each subscription. A licence held by a disabled or idle account still counts as assigned, and free and trial products can carry counts large enough to swamp the rest.
| Method | Best fit |
|---|---|
| PowerShell | A CSV of every subscription with its bought, assigned and unassigned counts. |
| Admin centre | A quick look at 1 product's total and who it is assigned to. |
| Altivane | Recommended for regular reviews, with the subscriptions that need attention and the accounts holding licences they may not need. |
Before you start
Use an account that can read subscription details: for Microsoft Graph, a supported Microsoft Entra role such as Global Reader or Directory Readers. The PowerShell example requests LicenseAssignment., the least privileged permission Microsoft lists for the call; your organisation may require administrator consent. The admin centre needs an administrator who can open the Billing pages.
Here "unused" means bought and not assigned, as Microsoft reports it when you run the script or open the page. The examples read and export; they change nothing.
Option 1
Find unused licences with PowerShell
Install the modules once, then connect and export every subscription that can be assigned to users. The list of subscriptions does not support $filter, so the script reads all of it and sorts the most unassigned first.
# Install once, if the modules are not already installed.
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser$ErrorActionPreference = 'Stop'
Connect-MgGraph -Scopes 'LicenseAssignment.Read.All' -NoWelcome
try {
$skus = @(Get-MgSubscribedSku -All | Where-Object { $_.AppliesTo -eq 'User' })
if ($skus.Count -eq 0) {
Write-Output 'No subscriptions that can be assigned to users were found.'
} else {
$report = @($skus | ForEach-Object {
[pscustomobject]@{
SkuPartNumber = $_.SkuPartNumber
Status = $_.CapabilityStatus
Bought = $_.PrepaidUnits.Enabled
Assigned = $_.ConsumedUnits
Unassigned = $_.PrepaidUnits.Enabled - $_.ConsumedUnits
InGrace = $_.PrepaidUnits.Warning
Suspended = $_.PrepaidUnits.Suspended
}
} | Sort-Object Unassigned -Descending)
$report | Export-Csv -Path './unused-licences.csv' -NoTypeInformation -Encoding utf8
$spare = @($report | Where-Object { $_.Unassigned -gt 0 })
Write-Output "$($spare.Count) of $($skus.Count) subscriptions have unassigned licences."
}
} finally {
Disconnect-MgGraph | Out-Null
}Bought counts enabled licences only, so a subscription that has expired or been cancelled can show a negative Unassigned; read Status, InGrace and Suspended beside it. Part numbers such as ENTERPRISEPACK are not product names: Microsoft publishes a page that maps one to the other. Filter on SkuPartNumber to leave out free or trial products.
Read a single subscription
Run this while connected. Enabled is the number bought and ConsumedUnits the number assigned.
# Replace the example part number.
Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq 'ENTERPRISEPACK' } |
Select-Object SkuPartNumber,ConsumedUnits -ExpandProperty PrepaidUnitsOption 2
Read licence totals in the admin centre
- Sign in to the Microsoft 365 admin centre for the correct tenant.
- Go to Billing > Licenses.
- Read the total licences for each product. The page adds up every subscription with the same product name.
- Select a product to see the users and groups it is assigned to. A licence assigned through a group shows the group's name, not each member.
- Compare those assigned with the total. Leave without choosing Assign licenses or Unassign licenses.
This suits 1 product, such as before you buy more. It does not suit a full review: subscriptions are combined and group assignments make counting slow.
Option 3 ยท Recommended for regular licence reviews
Review licences with Altivane
- Open Altivane and choose the tenant.
- Build the tenant's documents with the Licensing scorecard report in full, so its tables are built as well as counted.
- Open the tenant's Excel workbook and read Subscriptions: Total is licences bought, Assigned is licences in use and Available is the difference.
- Read Licence SKUs Needing Attention for subscriptions with more assignments than seats, seats in the grace period, suspended seats or a status other than Enabled. Disabled Accounts Holding Licences and Licences Held by Stale Users list accounts that may not need their licences.
Subscriptions shows product names rather than part numbers. Free products granted in bulk are left out of the totals by default and listed in Free Products Left Out. The scorecard rates the subscriptions needing attention and accounts holding licences; spare licences are in Subscriptions. Where sign-in activity cannot be read, which needs Microsoft Entra ID P1 or P2, Licences Held by Stale Users says so rather than showing 0. The build also writes the tenant's Word report.
Contact usCommon questions
Why do some products show thousands of unassigned licences?
Free and trial products can carry very large counts. Filter them out of the CSV on SkuPartNumber. Altivane leaves free products granted in bulk out of its totals.
Does an assigned licence mean it is in use?
No. A licence assigned to an account nobody uses counts as assigned, so the subscription figures hide it. Altivane's Disabled Accounts Holding Licences and Licences Held by Stale Users tables find some. To see who holds each licence, see how to export licensed users.
Why does Get-MgSubscribedSku fail or return nothing?
Check the permission and role: the call needs LicenseAssignment. with consent, and the signed-in account needs a supported role such as Global Reader.
Microsoft references
Technical references checked on 5 October 2026. The example scripts have been reviewed and syntax checked; they have not been run against a live tenant.