A cloud-based identity and access management service for securing user authentication and resource access
Use Microsoft Graph PowerShell to pull sign-in activity and account properties, then filter and export. Entra PowerShell also exposes sign-in activity per user, but Graph is better suited for bulk/reporting.
Below is an outline that matches the requirements, using the patterns from the documented examples.
1. Prerequisites
- Install and import Microsoft Graph PowerShell:
Install-Module Microsoft.Graph -Scope CurrentUser Import-Module Microsoft.Graph - Connect with the right permissions (audit + users):
Connect-MgGraph -Scopes "AuditLog.Read.All","User.Read.All"
2. Input: users from CSV
Assume the CSV from on-prem AD group has a column UserPrincipalName (adjust if different):
$users = Import-Csv -Path "C:\temp\groupUsers.csv"
3. Get last sign-in activity per user
The documented Entra PowerShell example shows how to retrieve sign-in activity for a single user and expand the SignInActivity property:
Connect-Entra -Scopes 'User.Read.All','AuditLog.Read.All'
Get-EntraUser -UserId '******@contoso.com' -Property 'SignInActivity' |
Select-Object -Property Id, DisplayName, UserPrincipalName -ExpandProperty 'SignInActivity'
Using the same pattern, loop through the CSV list and collect:
- Last interactive sign-in
- Last non-interactive sign-in
- Account creation date
Example using Entra PowerShell cmdlets:
Connect-Entra -Scopes 'User.Read.All','AuditLog.Read.All'
$results = foreach ($u in $users) {
$upn = $u.UserPrincipalName
# Get user with sign-in activity and creation date
$user = Get-EntraUser -UserId $upn -Property 'SignInActivity','CreatedDateTime'
if (-not $user) { continue }
$signIn = $user.SignInActivity
[PSCustomObject]@{
Id = $user.Id
DisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
CreatedDateTime = $user.CreatedDateTime
LastInteractiveSignInDate = $signIn.LastSignInDateTime
LastNonInteractiveSignInDate= $signIn.LastNonInteractiveSignInDateTime
}
}
This follows the documented pattern of expanding SignInActivity and selecting user properties.
4. Filter users inactive for 30 days
Once the data is in $results, calculate inactivity based on the most recent sign-in (interactive or non-interactive):
$cutoff = (Get-Date).AddDays(-30)
$resultsWithCalc = $results | ForEach-Object {
$lastInteractive = $_.LastInteractiveSignInDate
$lastNonInteractive= $_.LastNonInteractiveSignInDate
# Determine the most recent sign-in of any type
$lastAny = $lastInteractive
if ($lastNonInteractive -and (!$lastAny -or $lastNonInteractive -gt $lastAny)) {
$lastAny = $lastNonInteractive
}
$_ | Add-Member -NotePropertyName LastAnySignInDate -NotePropertyValue $lastAny -Force
$_
}
$inactive30Days = $resultsWithCalc | Where-Object {
-not $_.LastAnySignInDate -or $_.LastAnySignInDate -lt $cutoff
}
$inactive30Days now contains users who have not signed in (interactive or non-interactive) in the last 30 days and are candidates for license reclamation.
5. Export to CSV
Export both the full dataset and the inactive subset:
$resultsWithCalc | Export-Csv -Path "C:\temp\AllUsersSignInReport.csv" -NoTypeInformation -Encoding UTF8
$inactive30Days | Export-Csv -Path "C:\temp\InactiveOver30Days.csv" -NoTypeInformation -Encoding UTF8
6. Optional: use audit logs for deeper analysis
If more detailed sign-in history is needed (for example, to validate specific events), audit and sign-in logs can be queried with Microsoft Graph PowerShell. The monitoring and health documentation shows how to query audit logs using Get-MgAuditLogDirectoryAudit with filters such as category and activity date:
Get-MgAuditLogDirectoryAudit -Filter "activityDateTime gt 2019-04-18"
Similar approaches can be used for sign-in logs when more granular analysis is required.
This approach satisfies:
- Import users from CSV.
- Retrieve last interactive and non-interactive sign-in dates via
SignInActivity. - Include account creation date (
CreatedDateTime). - Export to CSV.
- Identify users inactive for 30 days for license reclamation.
References: