๐ฅ Recipe ยท User & Group Management
Audit 90-Day Inactive M365 Users and Build a Safe Deprovisioning Plan
Identify stale accounts with real activity data from Exchange, SharePoint, OneDrive, and Teams โ then phase deprovisioning with manager notification
Complexity
Intermediate
Impact
cost-savings + security + governance + compliance + license-optimization
Context
Why This Matters
Inactive user accounts are one of the highest-leverage cleanup targets in a Microsoft 365 tenant. Every stale account typically carries a paid license (often E3 or E5), a live mailbox, OneDrive storage, and potentially SharePoint site ownership. Beyond cost, they're a security liability: dormant accounts are prime targets for password spray and phishing-based takeover.
Running a 90-day inactivity audit is a standard governance control recommended by CIS, NIST, and Microsoft Secure Score. This recipe walks through the right way to build that audit โ not just by looking at Entra sign-in logs (which miss service-specific activity), but by cross-referencing the getOffice365ActiveUserDetail usage report that includes Exchange, SharePoint, OneDrive, and Teams last-activity dates.
The output is not just a list โ it's a phased deprovisioning plan with manager notifications, license removal recommendations, and safeguards against accidentally disabling executive or service accounts.
Expected Outcomes
After completing this recipe you will have:
- A CSV report of all users inactive for 90+ days with per-service last-activity dates (Exchange, SharePoint, OneDrive, Teams)
- License assignment details for each inactive user (critical for cost recovery)
- Manager name and email for each inactive user (for the notification workflow)
- A recommended action per user (review for license removal vs. account disable)
- A phased 4-week deprovisioning plan (notify โ review โ disable โ delicense)
- A flag for high-risk accounts (executives, service accounts, shared mailboxes) that need HR/manual review
Risks & Considerations
Watch out for these pitfalls:
- Anonymized reports: By default M365 usage reports show concealed names like
5C0DBAโฆ. You must temporarily setdisplayConcealedNames: falseviaPATCH /admin/reportSettingsto get real UPNs. Always re-enable anonymization after the audit to stay aligned with your privacy posture. - Don't rely on
signInActivityalone: It only tracks Entra interactive/non-interactive sign-ins. A user may be inactive in Entra logs but still have Exchange mail flow or OneDrive sync activity. Use the usage report for a true picture. - Service accounts and shared mailboxes will appear as inactive โ they're designed to never sign in interactively. Tag and exclude them before any disable action.
- Executive accounts (C-suite, board members) often show low activity but should never be auto-deprovisioned. Always escalate these to HR.
- Don't delete accounts directly. The safe sequence is: notify manager โ remove licenses โ disable sign-in โ convert mailbox to shared โ wait 30 days โ delete. Skipping the shared-mailbox step loses mail data permanently.
- Licensed but never signed in: These are often onboarding mistakes (license assigned before user started) or offboarding failures. Investigate before revoking.
- B2B guest users have different activity patterns and may not appear correctly in the usage report. Handle them in a separate audit.
Required Permissions
| Permission | Why It's Needed |
|---|---|
| Reports.Read.All | Required to call getOffice365ActiveUserDetail and related usage reports that provide per-service activity data |
| ReportSettings.ReadWrite.All | Required to temporarily disable report anonymization (displayConcealedNames) so actual UPNs and display names appear in reports |
| User.Read.All | Required to fetch per-user license assignments, accountEnabled status, department, and job title |
| Directory.Read.All | Required to read each user's manager relationship for the notification workflow |
| Organization.Read.All | Optional โ needed if you want to map license SKU GUIDs to friendly product names for the report |
The fastest way to get this done โ just ask Dex. Copy the prompt below and paste it into your Dex conversation.
For IT Admins
Paste into Dex CoAdmin