To find disabled users with licences in Microsoft 365, list the accounts where accountEnabled is false and keep those with at least 1 assigned licence. Disabling an account does not remove its licences. Removing one starts a 30 day countdown on the mailbox, so check it first.
| Method | Best fit |
|---|---|
| PowerShell | A CSV of every disabled account that holds a licence, with how each was assigned. |
| Admin centre | A quick look at blocked accounts for 1 product, or at 1 person's licences. |
| Altivane | Recommended for regular clean-ups, rated against a threshold. |
Before you start
Use an account that can read users and subscriptions, such as Global Reader. The PowerShell example requests User. and LicenseAssignment.; your organisation may require administrator consent. Saving a custom filter in the admin centre needs a role that manages user views, such as User Administrator.
Disabled here means sign-in is blocked: the admin centre says blocked, and Microsoft Graph stores accountEnabled as false. Guests are included, and the user list can lag for accounts changed a moment ago. The examples read and export and change nothing, apart from the admin centre filter, a saved view every admin sees until it is deleted.
Option 1
Find disabled users with licences with PowerShell
Install the modules once, then connect and export every disabled account that holds a licence. Microsoft Graph applies the filter, so only those accounts come back.
# Install once, if the modules are not already installed.
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Users -Scope CurrentUser
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser$ErrorActionPreference = 'Stop'
Connect-MgGraph -Scopes 'User.Read.All', 'LicenseAssignment.Read.All' -NoWelcome
try {
$names = @{}
foreach ($sku in Get-MgSubscribedSku -All) {
$names[$sku.SkuId.ToString()] = $sku.SkuPartNumber
}
$query = @{
Filter = 'accountEnabled eq false and assignedLicenses/$count ne 0'
Property = 'displayName,userPrincipalName,userType,licenseAssignmentStates'
ConsistencyLevel = 'eventual'
CountVariable = 'disabledCount'
}
$users = Get-MgUser -All @query
$report = @(foreach ($user in $users) {
$held = @($user.LicenseAssignmentStates | ForEach-Object {
$id = $_.SkuId.ToString()
$product = if ($names.ContainsKey($id)) { $names[$id] } else { $id }
$via = if ($_.AssignedByGroup) { "group $($_.AssignedByGroup)" } else { 'direct' }
"$product ($via)"
})
[pscustomobject]@{
DisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
UserType = $user.UserType
Licences = $held -join '; '
}
})
if ($report.Count -eq 0) {
Write-Output 'No disabled account holds a licence.'
} else {
$report | Sort-Object DisplayName |
Export-Csv -Path './disabled-with-licences.csv' -NoTypeInformation -Encoding utf8
Write-Output "$($report.Count) disabled accounts hold licences."
}
} finally {
Disconnect-MgGraph | Out-Null
}Keep ConsistencyLevel and CountVariable: the licence count needs them, or Microsoft Graph returns an error. Licences gives part numbers, each followed by direct or the ID of the group that assigned it.
Check a single account
Reconnect with Connect-MgGraph, then read 1 person's licences. The main export above disconnects when it finishes.
# Run while connected to Microsoft Graph. Replace the example account.
$properties = 'displayName,accountEnabled,licenseAssignmentStates'
$user = Get-MgUser -UserId 'person@example.com' -Property $properties
$user | Select-Object DisplayName, AccountEnabled
$user.LicenseAssignmentStates | Select-Object SkuId, AssignedByGroup, State, ErrorAssignedByGroup is empty for a licence assigned directly and holds the group's ID otherwise.
Option 2
Review blocked accounts in the admin centre
- Sign in to the Microsoft 365 admin centre for the correct tenant.
- Go to Users > Active users, select Filter set, then New filter.
- In the Custom filter pane, name the filter, choose blocked users under Sign-in status, optionally choose 1 product under Assigned product license, and select Add.
- Select a person, open the Licenses and Apps tab and read the Licenses section.
- Close the pane without changes. To delete the filter, switch to it, then choose Filter set, Edit filter, the ellipsis (...) and Delete.
This suits a quick look at 1 product or 1 person. For every blocked account's licences in 1 list, use PowerShell or a report.
Option 3 ยท Recommended for regular licence clean-ups
Find disabled users with licences in Altivane
- Open Altivane and choose the tenant.
- Build the tenant's documents with the Licensing scorecard report in full, so the accounts are listed as well as counted; its starting mode, Summary, counts them without listing them.
- Open the tenant's Excel workbook. Its Index gives the account count under Records and the worksheet name, shortened to Excel's 31 characters.
- Read Disabled Accounts Holding Licences for product names in Licences and whether each is assigned directly or through a named group in Assigned Via. Read the Scorecard for the count's rating against your thresholds.
The sheet has 1 row per disabled account with a licence, guests included. By default the Word report carries the same table without the Enabled, Type and Usage Location columns. The scorecard marks the count Warning from 5 accounts and Critical from 20 until you change the thresholds. Last Sign-In needs sign-in activity, which Microsoft returns only with Microsoft Entra ID P1 or P2. The build also writes the tenant's Word report.
Contact usCommon questions
Can I remove the licence from a disabled account straight away?
Not before checking the mailbox. Removing a licence holds its data for 30 days, then deletes it, and any holds must go first. To keep the mailbox, convert it to a shared mailbox while the licence is still assigned. Under 50 GB it can then lose the licence, but the account must stay.
Why is an enabled account that nobody uses not on the list?
The list holds disabled accounts only. To find enabled accounts with no recent sign-in, see how to find inactive users.
Why does the PowerShell call return an error?
The licence count is an advanced query, so the request needs the consistency level eventual and a count. The script sets both; keep them if you adapt the call.
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.
- Get-MgUser reference
- Get-MgSubscribedSku reference
- Advanced query capabilities on directory objects
- user properties
- licenseAssignmentState properties
- Block user accounts with PowerShell
- Add users and custom user views
- Assign or unassign licenses for users
- Remove a license from a former employee
- Convert a user mailbox to a shared mailbox