Microsoft Entra ID security

How to find users who have never signed in to Entra ID

List enabled accounts that have never signed in with Microsoft Graph PowerShell, add a sign-in column to the Users list in the Entra admin centre, or use Altivane's Identity report for a rated list.

By the Published

To find users who have never signed in to Microsoft Entra ID, read each enabled account's sign-in activity and keep those with no sign-in date of any kind, leaving out accounts created in the last few weeks. The dates need Microsoft Entra ID P1 or P2. An account whose last sign-in was before April 2020 also shows no date, so check its age before you act.

Choose the method that fits the job
MethodBest fit
PowerShellA tenant-wide CSV of enabled member accounts with no sign-in on record, oldest first.
Admin centreA quick look at the Users list with a last sign-in column added.
AltivaneRecommended for recurring reviews, with the share of unused 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 keeps 3 dates for each account: the last interactive attempt, the last non-interactive attempt and, from 1 December 2023, the last successful sign-in. An account counts as never signed in only when all 3 are blank, and a new sign-in can take up to 24 hours to show. The examples leave out guests and accounts younger than 30 days. They read and export; they change no account.

Option 1

Find users who have never signed in with PowerShell

Install the modules once, then connect and export the enabled member accounts with no sign-in on record. Change $graceDays to give new accounts more or less time.

PowerShell
# Install once, if the modules are not already installed.
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Users -Scope CurrentUser
PowerShell
$ErrorActionPreference = 'Stop'
$graceDays = 30
$now = (Get-Date).ToUniversalTime()
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
    $report = @(foreach ($user in $users) {
        if ($user.UserType -ne 'Member' -or -not $user.CreatedDateTime) { continue }
        $activity = $user.SignInActivity
        if ($activity.LastSuccessfulSignInDateTime -or $activity.LastSignInDateTime -or
            $activity.LastNonInteractiveSignInDateTime) { continue }
        $created = $user.CreatedDateTime.ToUniversalTime()
        $age = [int][math]::Floor(($now - $created).TotalDays)
        # A new account has had no chance to sign in yet.
        if ($age -lt $graceDays) { continue }
        [pscustomobject]@{
            DisplayName       = $user.DisplayName
            UserPrincipalName = $user.UserPrincipalName
            CreatedUtc        = $created
            DaysSinceCreated  = $age
        }
    })
    if ($report.Count -eq 0) {
        Write-Output "No enabled member account older than $graceDays days is unused."
    } else {
        $report | Sort-Object CreatedUtc |
            Export-Csv -Path './users-never-signed-in.csv' -NoTypeInformation -Encoding utf8
        Write-Output "$($report.Count) enabled member accounts have never signed in."
    }
} finally {
    Disconnect-MgGraph | Out-Null
}

The script keeps enabled member accounts with none of the 3 dates, oldest first. It reads every enabled account's sign-in activity because Microsoft does not let a filter on signInActivity be combined with another filterable property. Remove the UserType test to include guests.

Check a single account

Reconnect with Connect-MgGraph, then read 1 person's dates. The main export above disconnects when it finishes.

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

Option 2

Find unused accounts 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. Read the accounts where the new column is blank. To check 1 of them, open it and read the Sign-ins tile under My Feed.
  6. Leave the list without changing any account.

This suits a quick look at a small tenant. The column shows interactive attempts only, so an account used only by background clients looks unused. For all 3 dates with new accounts left out, use PowerShell or a report.

Common questions

Why does an account I know is used show no sign-in?

Microsoft leaves the interactive date blank when the last attempt was before April 2020, keeps non-interactive dates back to May 2020 and records successful sign-ins only from 1 December 2023. Read all 3 dates before you treat the account as unused.

Why does the script stop with an error about a premium licence?

Sign-in activity needs Microsoft Entra ID P1 or P2, and Microsoft Graph refuses the request without it. If the tenant has the licence, Microsoft says the error can come and go when the app cannot read the tenant's licensing; adding Directory.Read.All to the scopes avoids it.

What should I do with the accounts on the list?

Ask whether each person started and still needs the account. Accounts that once signed in but stopped are covered in how to find inactive users, and to stop several accounts signing in at once, see how to block sign-in for multiple users.

Microsoft references

Technical references checked on 6 November 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