Exporting a list of inactive users is mostly a housekeeping task — unused accounts are a security and licensing cost, and this is how you actually find them instead of guessing.
In this post: Via PowerShell · Via the admin center (no scripting) · Third-party tools · Best practices · When would you actually use this? · References
Via PowerShell
Older versions of this script (and a lot of guides still floating around) use Connect-MsolService and Get-MsolUser — the MSOnline module those come from is retired, and it never exposed a real last-sign-in timestamp anyway (LastPasswordChangeTimestamp is not the same thing as “last logged in”). The current, correct approach uses the Microsoft Graph PowerShell SDK and the actual signInActivity property, which requires AuditLog.Read.All and User.Read.All permissions:
Connect-MgGraph -Scopes "AuditLog.Read.All", "User.Read.All"
$daysInactive = 90
$cutoffDate = (Get-Date).AddDays(-$daysInactive)
$inactiveUsers = Get-MgUser -All -Property DisplayName, UserPrincipalName, SignInActivity |
Select-Object DisplayName, UserPrincipalName, @{N='LastSignIn'; E={$_.SignInActivity.LastSignInDateTime}} |
Where-Object { $_.LastSignIn -eq $null -or $_.LastSignIn -lt $cutoffDate }
$inactiveUsers | Export-Csv -Path "InactiveUsers.csv" -NoTypeInformation
Write-Host "Export completed! Check InactiveUsers.csv"
One real gotcha worth knowing: filtering purely on “LastSignIn older than the cutoff” silently misses accounts that have never signed in at all — for those, SignInActivity comes back $null rather than a date, which is why the condition above explicitly checks for $null too. Skip that check and a stale account that was created but never used won’t show up in your export, even though it’s arguably the highest-priority one to find.
Via the admin center (no scripting)
Microsoft 365 Admin Center > Reports > Usage > Active Users Report, then filter by login activity. Good enough for a one-off check; the PowerShell route is better if you’re doing this regularly.
Third-party tools
Quest, ManageEngine, and AvePoint add automated tracking, real-time alerts, and compliance auditing on top of what the built-in options give you — worth it if you’re doing this at real scale.
Best practices
- Define “inactive” before you export anything — 90, 180, or 365 days of no login is a real business/security decision, not a default to accept blindly. Pick the threshold that matches your actual risk tolerance.
- Cross-verify before acting — a user might look inactive in one report but still be accessing SharePoint via mobile or an API integration. Check Microsoft 365 usage reports or audit logs before disabling anything.
- Handle exported data carefully — it’s personal information; restrict access to the report and anonymize where you can.
- Decide the actual next step in advance — disable, remove access, downgrade the license, or notify the user first. “Export the list” isn’t the finish line.
Define \”inactive\” before you export anything — 90, 180, or 365 days is a real business decision, not a default to accept blindly.
When would you actually use this?
- Licensing costs need trimming and nobody’s sure which accounts are actually still in use — the PowerShell script above gives you a real answer, not a guess.
- A security review is asking about dormant accounts as an attack surface — inactive accounts with valid credentials are exactly the kind of thing that gets flagged.
- You need this as an ongoing, repeatable process rather than a one-time check — that’s when the third-party tooling starts to earn its cost.
References
Hope that’s useful for your setup. Let me know in the comments.
App Catalog Authentication Automation Backup Compliance Content Type CSS Flows Google Javascript Limitations List Metadata MFA Microsoft Node NodeJs O365 OneDrive Permissions PnP PnPJS Policy PowerApps Power Automate PowerAutomate PowerPlatform PowerShell React ReactJs Rest API Rest Endpoint Security Send an HTTP Request to SharePoint SharePoint SharePoint List SharePoint Modern SharePoint Online SPFX SPO Sync Tags Teams Termstore Versioning


