Back to blog

How to Find Unused Microsoft 365 Licenses in Your Org

Benny Rosner
How to Find Unused Microsoft 365 Licenses in Your Org

A 500-seat Microsoft 365 E3 tenant paying $39 per seat per month is burning roughly $23,400 a year on licenses where nobody has logged in for months. The waste isn’t hypothetical: most organizations with 200 or more seats are over-licensed by 8% to 15% based on typical enterprise audits, and the evidence sits quietly inside sign-in logs that never get reviewed between renewal cycles. If you’ve been asking yourself, “How do I find unused Microsoft 365 licenses in my organization?”, this guide answers that question with every method available, from the Admin Center to PowerShell to Microsoft Graph.

Finding those unassigned and inactive seats isn’t a single button click. It requires cross-referencing sign-in timestamps, license assignment types, account purposes, and service dependencies before anyone touches a removal script. Skip any of those steps and you’ll either miss real waste or accidentally cut access to an active employee.

Platforms like Chronom AI surface this waste automatically across your entire Microsoft stack, but if you want to run the audit yourself first, this guide walks through every method: how to define “unused,” pull data from the Admin Center and PowerShell, handle edge cases like guest accounts and service accounts, safely reclaim licenses in bulk, and schedule recurring reviews so the waste doesn’t quietly rebuild.

What Actually Counts as an “Unused” Microsoft 365 License

The sign-in activity threshold question

Microsoft records two key timestamps in the signInActivity resource: lastSignInDateTime (the last interactive sign-in attempt, successful or not) and lastSuccessfulSignInDateTime (successful auth only, available from December 2023 via the Graph beta endpoint). Most IT teams flag accounts with no activity in 30, 60, or 90 days, but the right threshold depends on workforce type. A field rep or seasonal worker can go 45 days between system logins without being genuinely inactive.

Microsoft’s own Enterprise Agreement renewal methodology standardizes on 90 days as the official inactivity threshold for identifying license recovery candidates. Many teams layer a tiered approach on top of that: 30 days for high-cost E5 or Copilot add-ons, 60 days for standard E3 users, and 90 days as the baseline for everything else. One important data caveat: Graph and PowerShell telemetry can lag up to 6 hours, and sign-in logs only retain 30 days on Entra ID Premium tiers, so any export is a point-in-time snapshot, not a real-time feed.

Account types that need separate treatment before you touch anything

Service accounts, shared mailboxes, and conference room resources rarely generate interactive sign-ins, but many of them still require specific license plans to function. Mark these as exempt before running any bulk removal. Guest accounts are a separate category: they frequently accumulate licenses through group membership after contractor engagements or vendor trials end without cleanup, and they deserve their own audit pass.

The highest-yield category in most tenants is offboarding gaps, recently departed employees whose accounts were never deprovisioned. These are easy wins because there’s no ambiguity about whether the license is genuinely wasted.

How Do I Find Unused Microsoft 365 Licenses in My Organization: Admin Center Methods

Running the license utilization report under Billing

Navigate to Billing > Licenses in the Microsoft 365 Admin Center. The main view shows total purchased seats versus assigned seats per subscription, giving you an immediate gap count for unassigned licenses across your tenant. The tabbed layout makes assignment type visible without any scripting: the Users tab shows direct assignments, and the Groups tab shows licenses flowing in through group-based licensing. Reviewing both tabs together gives you a complete picture before you run anything in PowerShell.

Checking sign-in activity from the Users section

Go to Users > Active Users, add the “Last sign-in” column from the column picker, and filter for dates older than your chosen threshold. This is the fastest no-code method for a first-pass review on smaller tenants. The limitation is real though: this view only captures interactive logins and doesn’t distinguish between account types, so the raw export still needs human triage before anything gets removed.

Auditing guest accounts in Entra ID

In the Microsoft Entra admin center, navigate to Identity > Users > All Users, apply a filter for User Type = Guest, and review the assigned licenses column. Guest accounts pulling full E3 seats at $39 per month are a common waste vector, especially after contractor engagements end without a formal offboarding step. The portal will list the guests, but you’ll need PowerShell to filter programmatically for those with active license assignments across a large tenant.

Finding Unused Microsoft 365 Licenses with PowerShell and Microsoft Graph

The Microsoft Graph script: licenses and last sign-in in one call

The Microsoft.Graph module is the right tool here. It retrieves signInActivity and assignedLicenses in a single Get-MgUser call, while the older AzureAD module requires a separate per-user API call for sign-in logs, turning a 10-minute query into hours on a large tenant. Connect with the required scopes first:

Connect-MgGraph -Scopes “User.Read.All”, “AuditLog.Read.All”Then pull all users with the properties you need and export the results:

$users = Get-MgUser -All -Select UserPrincipalName, DisplayName, assignedLicenses, signInActivity

$users | ForEach-Object { [PSCustomObject]@{ UserPrincipalName = $.UserPrincipalName DisplayName = $.DisplayName LastSignInDateTime = $.signInActivity.LastSignInDateTime LicenseSKUs = ($.assignedLicenses.SkuId) -join ”;” } } | Export-Csv -Path “C:\temp\UsersLicensesSignIn.csv” -NoTypeInformationFilter the export immediately to build your candidate list: pipe through Where-Object { $.LastSignInDateTime -lt (Get-Date).AddDays(-90) -and $.LicenseSKUs -ne "" }. If you need human-readable plan names instead of raw SKU IDs, add a second call to Get-MgSubscribedSku to map IDs to part numbers like ENTERPRISEPACK or SPE_E5.

Detecting group-based versus direct license assignments

Before reclaiming anything, check how each license was assigned by inspecting the licenseAssignmentStates property. If AssignedByGroup is not null and State is “Active,” the license flows from a group: remove the user from that group and the license releases cleanly. If AssignedByGroup is null, it’s a direct assignment and requires a direct removal command. Mixing these two paths in a bulk script produces silent failures, so confirm the assignment type for every user in your candidate list before executing anything at scale.

Filtering your candidate list to avoid false positives

Cross-reference your export against your HR system or ITSM to flag service accounts and shared mailboxes before treating the list as final. Then sort by license SKU to prioritize the highest-dollar rows: each recovered E3 seat is worth $39 per month, an E1 seat returns $10, and a Business Premium seat returns $22. On a 500-seat tenant where 10% of E3 licenses are genuinely idle, that’s over $23,000 in annual spend sitting in a spreadsheet waiting to be reclaimed.

Safely Reclaiming Licenses Without Disrupting Your Team

The pre-reclamation checklist

Before running any removal script, export a full snapshot of current license assignments as a CSV using Get-MgUser . Save it with today’s date in the file name. This is your rollback blueprint: if a user reports access loss after removal, you can identify their original SKUs from this file and reassign within minutes. Validate the SKU ID you plan to remove using Get-MgSubscribedSku to confirm the exact SkuId value, because a mismatch in the script produces silent failures with no error output. Run a pilot pass on 5 to 10 low-risk accounts (confirmed departed employees) before executing at scale.

Bulk removal with PowerShell and a rollback plan

Use Set-MgUserLicense for removal, pairing -RemoveLicenses @($skuIdToRemove) with -AddLicenses @{}. The empty array on AddLicenses is required syntax: omit it and the command errors out. Log every success and failure to a timestamped text file during execution so you know exactly where the script stopped if it fails midway. Immediate rollback is straightforward: use the pre-export CSV to identify the user’s original SKUs and reassign via Set-MgUserLicense -AddLicenses.

Notifying users to prevent support ticket floods

Send affected users a clear notice 24 to 48 hours before removal: name the specific services they’ll lose (Outlook, Teams, SharePoint) and provide a contact path if the removal is in error. For accounts you’re confident are inactive, departed employees or accounts with 120-plus days of no activity, skip the notice and act immediately. The goal is speed on confirmed waste and caution on ambiguous cases.

Setting Up Recurring License Reviews to Prevent Future Waste

Scheduling an automated monthly license report

Use Windows Task Scheduler to trigger your PowerShell script on the first of each month, or Azure Automation if you prefer a cloud-native approach that doesn’t depend on a local server being online. Point the output to a dated CSV in a shared SharePoint folder. Over time, this builds a longitudinal record where headcount changes become visible as license trends, which gives your team data-driven leverage going into Enterprise Agreement renewal conversations.

Governance guardrails to prevent waste from rebuilding

Set a lifecycle policy in Entra ID to flag accounts with no sign-in activity after 90 days for automated review, not automatic deletion. A human-approved review step keeps the process controlled while eliminating the quarterly manual sprint. Require all new license assignments to flow through groups rather than direct user assignment. When someone is removed from a group, the license releases automatically, eliminating the manual cleanup step that creates the waste in the first place.

How Chronom AI Turns This Entire Process into a 10-Minute Audit

Everything covered in this guide, from sign-in activity cross-referencing to group-based assignment detection to guest account audits, requires query time, export management, and human triage before any action is safe to take. For a 500-seat tenant, that’s easily 8 to 10 hours of skilled admin time per quarter. And that’s before you factor in Azure compute rightsizing, SharePoint storage overages, or reserved instance reconciliation, which are separate problems requiring separate manual investigations.

Chronom AI connects to your Microsoft tenant with read-only access (no write permissions, SOC 2 compliant) and runs all of these checks automatically across M365 licensing, Azure compute, SharePoint storage, and Marketplace commitments simultaneously. Rather than handing IT admins a raw spreadsheet of flags to interpret, the platform surfaces findings with specific dollar amounts attached: “14 E3 seats with no sign-in activity in 120-plus days = $X in recoverable annual spend.” The free audit report requires no credit card and delivers board-ready recommendations from day one.

The Full Workflow, Summarized

Define your threshold (90 days is Microsoft’s official standard, with tiered variations for high-cost SKUs), run Admin Center and Graph checks, handle edge cases for guests, service accounts, and group-based assignments. Reclaim safely with a pre-export rollback plan, then schedule monthly recurring reviews to keep the savings from eroding.

If you’re still asking how to find unused Microsoft 365 licenses in your organization, the manual steps above give you the full answer, but running them every quarter is a significant time commitment for an organization spending $50,000 or more annually on Microsoft. Chronom AI’s free audit** surfaces exactly what this guide takes hours to find, in minutes.** Start with the manual steps to understand the data landscape, then let automation handle the recurring cadence.

Frequently Asked Questions

How do I find unused Microsoft 365 licenses in my organization without PowerShell?

The Microsoft 365 Admin Center covers the basics without any scripting. Navigate to Billing > Licenses for unassigned seat counts, then go to Users > Active Users and add the “Last sign-in” column to identify inactive accounts. For a complete picture across large tenants, PowerShell and Microsoft Graph are still the most reliable path.

Microsoft’s Enterprise Agreement renewal methodology uses 90 days as the standard threshold. Many organizations layer a tiered approach on top: 30 days for high-cost SKUs like E5 or Copilot add-ons, 60 days for E3 users, and 90 days as the baseline for everything else.

Can I automate the process of finding and reclaiming unused Microsoft 365 licenses?

Yes. You can schedule the PowerShell script described in this guide using Windows Task Scheduler or Azure Automation to run monthly. Tools like Chronom AI automate the entire process, detection, triage, and reporting, across your full Microsoft stack without requiring manual query runs or export management.

Will removing unused licenses affect any active services or integrations?

Potentially, yes. Service accounts and shared mailboxes often require specific license plans to function even without interactive sign-ins. Always cross-reference your candidate list against your HR system and ITSM, run a pilot pass on confirmed inactive accounts first, and keep a pre-export rollback CSV before executing any bulk removal.

One Audit. Real Savings.
Zero Risk.

Get a comprehensive audit of your environment and see exactly how much you can save in under 15 minutes.

Read-only Access No Credit Card SOC 2 Compliant