Every December I find the same picture in almost every tenant. Old user and guest accounts have piled up quietly in Entra ID, many of them untouched since last year's audit (if there was one). Inactive accounts in Entra ID are more than clutter. They are a real risk, and clearing them out at the end of the year frees up licences and tightens security. For me it has become a fixed routine, mainly because guests and departed users tend to hang around far too long.

The signInActivity property shows which Entra ID accounts are inactive

The signInActivity property is the modern way to find inactive accounts efficiently. It records the last successful sign-in for each user (lastSuccessfulSignInDateTime) and the last sign-in attempt (lastSignInDateTime). In many of the environments I work in, admins are still surprised that this information exists at all. You need Entra ID P1 (or higher) and the AuditLog.Read.All permission. In the Entra admin center you'll find it under "Users" → "All users" → the "Inactive users" view, and Microsoft Graph exposes it as well. The admin center view is fine for a quick look. Once you want to filter, slice or export the data, you'll end up scripting fairly soon.

The diagram below shows how sign-in data reaches these tools:

flowchart TD
  SignInEvent["Sign-in (user)"] --> SignInLog["Sign-in log (audit log)"]
  SignInLog --> GraphAPI["Graph API signInActivity"]
  GraphAPI --> AdminCenter["Entra admin center: Inactive users"]

Figure: Sign-in data feeds signInActivity, which admins then see in Graph and the admin center.

Keep in mind that signInActivity is not a permanent audit log. For most clean-up scenarios it is still enough. Microsoft's article How to manage inactive user accounts covers the details and the retention period.

Microsoft Graph PowerShell exports the sign-in data for every user

For proper reporting and filtering I use a PowerShell export. With Microsoft Graph PowerShell (module 1.24 or later), an Entra ID P1 licence and a role that has AuditLog.Read.All, a single command pulls every user along with their signInActivity. Connect with Connect-MgGraph first, then run this in PowerShell 7:

Get-MgUser -All -Property signInActivity | Select-Object DisplayName,UserPrincipalName,AccountEnabled,@{Name='LastSignIn';Expression={$_.SignInActivity.LastSuccessfulSignInDateTime}}

Note that this returns cloud users, including guests who have signed in at least once. Accounts that were never used (invitations nobody accepted, or accounts that were provisioned but never activated) show no sign-in activity at all. Append | Export-Csv .\InactiveUsers.csv -NoTypeInformation if you want to save the list for further review.

Practical tip: Decide what "inactive" means for your organisation before you start. 90 or 180 days are common values, but think it through, especially for service accounts. Don't disable anything blindly.

Access reviews catch inactive guest accounts

Guests usually cause the most trouble. In most tenants the guest list keeps growing because nobody reviews access on a regular basis. Access reviews with the "Users who have not signed in for X days" option make this much easier now. You'll find them in the Entra admin center under "Identity Governance" → "Access reviews", and the wizard lets you target inactive guests directly. Microsoft's guide walks through the setup, and I've described the cleanup in more detail in Guest access reviews in Entra ID: cleaning up the mess.

If you use Entitlement Management, you can automate guest clean-up even further, because access is revoked as soon as the package membership ends (more on that here).

Service accounts and shared mailboxes often only look inactive

Don't delete every inactive account you come across. Service accounts, automation accounts and scanner identities rarely sign in interactively, so they often appear inactive. Shared mailboxes behave the same way. People open them, but the mailbox account itself hardly ever signs in. Before I touch an account, I check whether it is still assigned to apps, groups or workflows. Deleting without that check can break important integrations, particularly where ApplicationImpersonation or hybrid identities are involved.

My approach is to disable the account first and watch it for a couple of weeks. If nothing breaks, you can delete it later without worry. The licences are freed up automatically, which helps when planning next year's budget.

A simple year-end routine works better for me than a perfect one

My process is straightforward and holds up well in practice:

  • Review signInActivity data in the Entra admin center and with PowerShell
  • Set up access reviews for guest accounts
  • Check service accounts and shared mailboxes before taking action
  • Disable unused accounts first, delete them later
  • Audit group memberships and licences

No process is bulletproof, but these steps leave my tenants cleaner and safer every year. For more on guest cleanup, see Guest access reviews in Entra ID: cleaning up the mess.