Back to script library
Entra / Microsoft 365 · Users & guests

Report last account sign in mg

Fetch user account sign in data using the Microsoft Graph SDK for PowerShell.

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-MgGraph -TenantId xxxxx-axxad-acadd-ddaadda -Scope Auditlog.Read.All

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

Connect-MgGraph -TenantId xxxxx-axxad-acadd-ddaadda -Scope Auditlog.Read.All
Select-MgProfile beta # Beta needed to get more informtion from the Graph
$Details = Get-MgContext
$Scopes = $Details | Select -ExpandProperty Scopes
$Scopes = $Scopes -Join ", "
$ProfileName = (Get-MgProfile).Name
$OrgName = (Get-MgOrganization).DisplayName
CLS
Write-Host "Microsoft Graph Connection Information"
Write-Host "--------------------------------------"
Write-Host " "
Write-Host ("Connected to Tenant {0} ({1}) as account {2}" -f $Details.TenantId, $OrgName, $Details.Account)
Write-Host "+-------------------------------------------------------------------------------------------------------------------+"
Write-Host ("Profile set as {0}. The following permission scope is defined: {1}" -f $ProfileName, $Scopes)
Write-Host ""
Write-Host "Finding Azure AD Users"
[array]$Users = Get-MgUser -All
If ($Users.Count -eq 0) { Write-Host "Sorry, no Azure AD user info fetched"; break}
CLS
$ProgressDelta = 100/($Users.count); $PercentComplete = 0; $UserNumber = 0
Write-Host ("{0} accounts to process..." -f $Users.Count)
CLS;$Report = [System.Collections.Generic.List[Object]]::new();$CSVOutput = "C:\temp\LastAccountSignIn.CSV"
ForEach ($User in $Users) { # See if we can find the last sign in record for an account
$UserNumber++
$PercentComplete += $ProgressDelta
$UserStatus = $User.DisplayName + " ["+ $UserNumber +"/" + $Users.Count + "]"
Write-Progress -Activity "Processing user" -Status $UserStatus -PercentComplete $PercentComplete
[array]$LastSignIn = Get-MgAuditLogSignIn -Filter "UserId eq '$($User.Id)'" -Top 1
If ($LastSignIn) { # We found a sign in record, so report it
$ReportLine = [PSCustomObject][Ordered]@{
Name = $LastSignIn.UserDisplayName
UPN = $LastSignIn.UserPrincipalName
Id = $LastSignIn.UserId
UserType = $User.UserType
AccountCreated = $User.CreatedDateTime
SignInDate = $LastSignIn.CreatedDateTime
DaysSince = ($LastSignIn.CreatedDateTime | New-TimeSpan).Days
Location = $LastSignIn.Location.City
App = $LastSignIn.AppDisplayName
Client = $LastSignIn.ClientAppUsed
IPAddress = $LastSignIn.IPAddress
IsInteractive = $LastSignIn.IsInterActive
CAAccess = $LastSignIn.ConditionalAccessStatus
RiskState = $LastSignIn.RiskState }
$Report.Add($ReportLine) } #End if
Else { # No sign in record found, so we report that"
$ReportLine = [PSCustomObject][Ordered]@{
Name = $User.UserDisplayName
UPN = $User.UserPrincipalName
Id = $User.Id
UserType = $User.UserType
AccountCreated = $User.CreatedDateTime
SignInDate = "No sign in data found"
DaysSince = "N/A"
App = "N/A"
Client = "N/A"
IPAddress = "N/A"
IsInteractive = "N/A"
CAAccess = "N/A"
RiskState = "N/A" }
$Report.Add($ReportLine) } #End Else
} #End ForEach
$Report | Sort {$_.SignInDate -as [datetime]} -descending | Out-GridView
$Report | Export-CSV -NoTypeInformation $CSVOutput
Write-Host "All done. Report is available in" $CSVOutput
Attribution