Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Use Microsoft Graph PowerShell to identify accounts whose recorded sign-in activity falls before an organization-approved cutoff, then review those accounts before changing access. For actual account access, use lastSuccessfulSignInDateTime where available; the more familiar lastSignInDateTime records an interactive attempt even when it failed. Neither a stale timestamp nor a blank value proves that an account is safe to disable.

Which Microsoft Graph sign-in timestamp should you use?

The signInActivity property contains different kinds of activity. Choose the field according to what the report is intended to establish.

Field What it represents How to use it
lastSignInDateTime The most recent interactive sign-in attempt, whether successful or unsuccessful. Useful for seeing when a user last tried an interactive sign-in, but not sufficient by itself to establish successful access.
lastNonInteractiveSignInDateTime The most recent non-interactive sign-in attempt. Some clients access tenant resources without a user directly signing in. Use as supporting context, especially when an account or client may generate non-interactive activity.
lastSuccessfulSignInDateTime The latest successful interactive or non-interactive sign-in. The stronger signal when the question is whether the account actually accessed Microsoft services.

Microsoft documents interactive sign-in history from April 2020 and non-interactive history from May 2020. The successful-sign-in property became available on December 1, 2023, and earlier values were not backfilled. A null value can mean there is no recorded attempt or that the relevant event falls outside available history; it does not establish that the user never used the account. See Microsoft’s signInActivity resource documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What inactivity cutoff should you choose?

Set the cutoff as an organizational policy, not as a universal Microsoft 365 default. Microsoft Learn gives 90–180 days as an example reasonable window and advises considering legitimate absences, such as vacation. The appropriate period depends on business requirements, leave practices, account purpose, and the consequences of missing a rarely used but necessary account. See Microsoft’s guidance on managing inactive user accounts.

Decide and document which timestamp the report uses. If the purpose is to find accounts without confirmed access, compare lastSuccessfulSignInDateTime with the cutoff. If that field is blank, classify the account as needing investigation rather than automatically treating it as inactive. You may use the two attempt fields as additional context, but do not treat a failed interactive attempt as successful access.

What access and licensing are required?

Requirements depend on the Graph endpoint and whether the script uses delegated or application access. For the users-list operation, Microsoft documents that reading signInActivity requires Microsoft Entra ID P1 or P2 and the AuditLog.Read.All permission. The property must also be explicitly selected. Check the current user list API requirements and limitations before running the query.

For sign-in log access, the v1.0 sign-ins endpoint lists AuditLog.Read.All as its least-privileged permission and supports delegated access with roles including Reports Reader and Security Reader. Microsoft guidance also describes P1 or P2 licensing and examples involving AuditLog.Read.All and Directory.Read.All for activity-log access. Verify the exact permission and role requirements for the endpoint and access model you will use; avoid granting broader tenant access by default. See the sign-ins API documentation and Microsoft’s sign-in activity report guidance.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do you find potentially inactive users with PowerShell?

The following example uses the Microsoft Graph PowerShell SDK. It requests the identifiers needed for review plus signInActivity, retrieves every result page, and classifies users against a cutoff. Set the cutoff and null-value handling to match an approved policy before using the report.

  1. Connect with an approved access model. Install the Microsoft Graph PowerShell SDK if needed, then connect with permissions approved for your tenant and endpoint. For delegated access, sign in with an account that has the required permissions and role.
  2. Query users and select the activity property. signInActivity is not returned by default, so include it explicitly. This example uses Get-MgUser with an all-pages request:
    Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All"
    $users = Get-MgUser -All -Property "displayName,userPrincipalName,accountEnabled,userType,signInActivity"

    The scopes shown are an example delegated connection, not a substitute for checking current endpoint, role, and licensing requirements. Use the least-privileged model approved for your environment.

  3. Set the policy cutoff and classify the result. The example below uses 90 days only as an illustrative policy choice; replace it with the period your organization has approved. It treats missing successful-sign-in values as a separate review category rather than labeling them inactive:
    $cutoff = (Get-Date).ToUniversalTime().AddDays(-90)
    $report = foreach ($user in $users) {
        $successful = $user.SignInActivity.LastSuccessfulSignInDateTime
        $interactiveAttempt = $user.SignInActivity.LastSignInDateTime
        $nonInteractiveAttempt = $user.SignInActivity.LastNonInteractiveSignInDateTime
    
        $status = if ($null -eq $successful) {
            "Needs review - no successful sign-in value"
        } elseif ([datetime]$successful -lt $cutoff) {
            "Candidate - last successful sign-in before cutoff"
        } else {
            "No match - successful sign-in within cutoff"
        }
    
        [pscustomobject]@{
            DisplayName                    = $user.DisplayName
            UserPrincipalName              = $user.UserPrincipalName
            AccountEnabled                 = $user.AccountEnabled
            UserType                       = $user.UserType
            LastSuccessfulSignInDateTime   = $successful
            LastSignInDateTime             = $interactiveAttempt
            LastNonInteractiveSignInDateTime = $nonInteractiveAttempt
            ReviewStatus                   = $status
        }
    }
    
    $report | Export-Csv -Path ".sign-in-review.csv" -NoTypeInformation

    Review the exported results before taking action. The report includes account type and enabled status to help reviewers distinguish cases that may need different handling.

When selecting or filtering signInActivity in the users list operation, Microsoft documents a maximum page size of 500. Use pagination rather than assuming the first response represents every account. The SDK’s -All option requests all pages; if using direct Graph requests instead, follow each returned next-page link until no next page remains. The limit and selection constraints are documented in the users list API reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How should you review report matches?

Treat the output as a candidate list for human review, not as an automatic disablement decision. Microsoft advises considering legitimate reasons for a period without sign-in. Review each account with its owner or the responsible team, and check:

  • Whether the person is still employed or otherwise entitled to the account.
  • Whether the person is on leave or has another documented reason for not signing in.
  • Whether the account is a guest, shared, service-related, or otherwise not a standard employee account.
  • Whether the account supports a business process or workload that may not require regular interactive sign-ins.
  • Whether available sign-in and directory audit events clarify a recent or disputed case.

Microsoft notes that interactive attempts typically appear in the related sign-in report within six hours, while the user object’s timestamp can take up to 24 hours to update. Account for this lag when checking recent activity. To investigate particular events, use sign-in logs rather than treating per-user summary fields as a complete event history. Microsoft Graph PowerShell provides Get-MgAuditLogSignIn for sign-in logs and Get-MgAuditLogDirectoryAudit for directory changes; see the sign-in cmdlet and directory audit cmdlet.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.