Microsoft Entra ID security

How to find inactive users in Microsoft Entra ID

List accounts with no recent sign-in with Microsoft Graph PowerShell, filter the Users list in the Entra admin centre, or use Altivane's Identity report for stale and never signed in accounts.

By the Published Updated

To find inactive users in Microsoft Entra ID, read each enabled account's last successful sign-in and keep those with none for 90 days or more. The date needs Microsoft Entra ID P1 or P2 and can take up to 24 hours to update. A blank date does not always mean the person never signed in.

Choose the method that fits the job
MethodBest fit
PowerShellA CSV of enabled member accounts with no successful sign-in for the days you choose.
Admin centreA quick look: add a last sign-in column to the Users list and filter it.
AltivaneRecommended for recurring reviews, with stale and never signed in accounts rated against a threshold.

Before you start

Use an account with a role that can read sign-in activity: Reports Reader is the least privileged role Microsoft lists. The tenant needs Microsoft Entra ID P1 or P2. The PowerShell example requests User.Read.All and AuditLog.Read.All; your organisation may require administrator consent.

Microsoft calls 90 to 180 days reasonable for many organisations. The script uses 90 and Altivane starts at 180. The examples read and export enabled member accounts, leave guests out and change nothing.

Option 1

Find inactive users with PowerShell

Install the modules once, then connect and export the enabled member accounts with no successful sign-in for 90 days or more. Change $days to widen or narrow the window.

PowerShell
# Install once, if the modules are not already installed.
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Users -Scope CurrentUser
PowerShell
$ErrorActionPreference = 'Stop'
$days = 90
$cutoff = (Get-Date).ToUniversalTime().AddDays(-$days)
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All' -NoWelcome
try {
    $properties = 'displayName,userPrincipalName,userType,createdDateTime,signInActivity'
    $users = Get-MgUser -All -Filter 'accountEnabled eq true' -Property $properties
    $members = $users | Where-Object { $_.UserType -eq 'Member' }
    $report = @(foreach ($user in $members) {
        $last = $user.SignInActivity.LastSuccessfulSignInDateTime
        # With no sign-in on record, count from the day the account was created.
        $since = if ($last) { $last } else { $user.CreatedDateTime }
        if ($since -and $since.ToUniversalTime() -gt $cutoff) { continue }
        [pscustomobject]@{
            DisplayName          = $user.DisplayName
            UserPrincipalName    = $user.UserPrincipalName
            LastSuccessfulSignIn = $last
            LastSignInAttempt    = $user.SignInActivity.LastSignInDateTime
            Created              = $user.CreatedDateTime
        }
    })
    if ($report.Count -eq 0) {
        Write-Output "No enabled member account has been inactive for $days days or more."
    } else {
        $report | Sort-Object LastSuccessfulSignIn |
            Export-Csv -Path './inactive-users.csv' -NoTypeInformation -Encoding utf8
        Write-Output "$($report.Count) enabled member accounts inactive for $days days or more."
    }
} finally {
    Disconnect-MgGraph | Out-Null
}

An account with no sign-in on record counts from its creation date, so a new account is not listed until it is $days old. The script reads the sign-in property for every enabled account because Microsoft says a filter on signInActivity cannot be combined with another filterable property. LastSignInAttempt includes failed attempts. Remove the Where-Object line to include guests.

Check a single account

Reconnect with Connect-MgGraph, then read 1 person's 3 sign-in dates.

PowerShell
# Run while connected to Microsoft Graph. Replace the example account.
$filter = "userPrincipalName eq 'person@example.com'"
$user = Get-MgUser -Filter $filter -Property 'displayName,signInActivity'
$user.SignInActivity | Select-Object LastSuccessfulSignInDateTime, LastSignInDateTime,
    LastNonInteractiveSignInDateTime

These are the last successful sign-in, the last interactive attempt and the last attempt by a client acting for the person.

Option 2

Find inactive users in the admin centre

  1. Sign in to the Microsoft Entra admin centre as at least a Reports Reader.
  2. Go to Entra ID > Users.
  3. Select Manage view, then Edit columns.
  4. Select + Add column, choose Last interactive sign-in time and select Save.
  5. Select Add filter, choose the new column, set the operator to <= and pick a date 90 days back to list people whose last sign-in is before it.
  6. To check 1 person, open them and read the Sign-ins tile under My Feed.

This suits a quick look at 1 tenant. The column is named for interactive sign-ins, so an account used only by background clients can look idle.

Common questions

Why is the sign-in date blank for an account that is used?

Microsoft says the property is not returned for an account that never signed in or last signed in before April 2020. The last successful sign-in is also recorded only from 1 December 2023, and a new sign-in can take up to 24 hours to show.

Do failed sign-ins count as activity?

No. lastSignInDateTime counts failed attempts, so an account someone only tried to use can look active. The script and Altivane use lastSuccessfulSignInDateTime.

What should I do with the accounts on the list?

Ask whether the person still works there and the account is still needed. Then see which hold licences in how to export licensed users, and which lack an MFA method in how to find users without MFA.

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.

Talk to us about Altivane

Questions, or pricing? Get in touch.

Contact us