List all users' last login date

Iron Contributor

Is it possible, using PowerShell, to list all AAD users' last login date (no matter how they logged in)? I have found a couple of scripts that check the last mailbox login, but that is not what we need, because we also want to list unlicensed users.

70 Replies
You could use a for each loop to check all the users in the tenant.

If you are new to Microsoft Graph, I recommend reading this article to learn more about it:
https://developer.microsoft.com/en-us/graph/blogs/announcing-30-days-of-microsoft-graph-blog-series/

@Robert Bollinger How to get this out in a CSV?

@Thijs Lecomte 

Trying to sing in to Graph Explorer and get an error:

 

Sign in

Sorry, but we’re having trouble signing you in.

 
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application: 'de8bc8b5-d9f9-48b1-a8ad-b748da725064'. More details: Reply address did not match because of case sensitivity.
Is this the first time you are trying to use the graph explorer?
What kind of permissions do you have?

I'm having this issue ever since they GAd the "preview" version, even opened a GitHub issue on it. Removing/reconsenting to the explorer app did nothing in my case, and moreover why the hell should case sensitivity matter... Programmers! :)

@Thijs Lecomte 

 

Yes, first time trying to login. i'm one and only Global Admin at my tenant. Can this issue be connected to tenant type? I mean does it matter Corporate licence or Personal licence tenant belong to?

@aero2466 

It shouldn't matter.

 

But the fact that @Vasil Michev has the same issue, suggests that it's a global issue.

Vasil, could you link the Github issue you created please?

Maybe aero can share his experience as well

Nobody bothered to even look at it for 20 days, but sure: https://github.com/microsoftgraph/microsoft-graph-explorer/issues/422

I personally prefer Joshua's solution. Just make a connection to AzureAD with Connect-AzureAD using the preview version of the module, then run a query using the objectid or upn as follows. The "-Top 1" gives you the most recent login.
# upn
Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$userPrincipalName'" -Top 1 | Select -ExpandProperty CreatedDateTime
# objectid
Get-AzureADAuditSignInLogs -Filter "UserId eq '$objectId'" -Top 1 | Select -ExpandProperty CreatedDateTime

@Jakob Rohde this is a classic Microsoft 365 housekeeping task and question. A pity Microsoft is not offering something out of the box.

 

I would suggest following Powershell script, which returns not only your users last login date as CSV file, but also assigned licenses. This way you may check whether there is someone not logging in and consuming licenses.

 

 

 

Install-Module AzureADPreview -AllowClobber -Force

Connect-AzureAD

$usersexport = [system.collections.arraylist]@()

Get-AzureADUser | % {
    $User = $_
    $UPN = $User.UserPrincipalName
    
    Write-Output "Start analysing $UPN"

    $LoginTime = Get-AzureAdAuditSigninLogs -top 1 -filter "userprincipalname eq '$UPN'" | select CreatedDateTime
    $Licenses = Get-AzureADUserLicenseDetail -ObjectId $User.ObjectId | % { $_.ServicePlans | Where {$_.AppliesTo -eq "User"}} | select -ExpandProperty ServicePlanName
    
    $Export = [pscustomobject]@{
        'DisplayName' = $User.DisplayName; 
        'UserPrincipalName' = $UPN;
        'LastSignInDate' = $LoginTime.CreatedDateTime;
        'Licenses' = $Licenses -join ",";
    }
    
    $usersexport.Add($Export)
    
    Write-Output $Export
}
$usersexport | Export-CSV $Home\LastLogonDate.csv

 

 

 

@Staniko 

Thank you for the above, looks exactly what I need but am getting the following error when running an elevated ISE PowerShell window:

 

Get-AzureAdAuditSigninLogs : The term 'Get-AzureAdAuditSigninLogs' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At line:9 char:18
+ $LoginTime = Get-AzureAdAuditSigninLogs -top 1 -filter "userprinc ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-AzureAdAuditSigninLogs:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

Any ideas? :)

@jjdmsgc Did the module install correctly? Did you run Powershell as admin? What does the command  

Install-Module AzureADPreview -AllowClobber -Force

 return? 

Ran and installed fine, then signed in with a global admin account for the tenancy I want to run it on and script fails on that line.

When I remove that line it runs and exports the information minus the login info etc. so problem is local to that line! :( Thank you for the swift reply.

@jjdmsgc seems to be an ISE issue, try it with the normal powershell window.

 

The command reference, just in case:

https://docs.microsoft.com/en-us/powershell/module/azuread/get-azureadauditsigninlogs?view=azureadps...

Found on a Github post:
So solution was to restart Powershell and login to Azure AD using Connect-AzureAD from AzureADPreview module:

AzureADPreview\Connect-AzureAD


After that cmdlet became available.

Get-AzureADAuditSignInLogs returns the events from the last month only, doesn't it? 

@Deleted Well upon checking I noticed that even accounts which for sure where logged in the last month where noted without an last sign-in date in the output.

So after more searching I'm now using the solution found on : GitHub - mzmaili/Get-AzureADUsersLastSignIn: Get-AADUserLastSignIn.ps1 is a PowerShell script retrieves Azure AD users with their last sign in date.

It's also faster and gives me the correct data i'm looking for.

Thanks :) The only catch here is that *I believe* it will provide you the last login for 90 days unlike the graph api which is a static value. I'm also hearing that some tenants are having throttling issues with with this cmdlet... I'm going to start moving my scripts to graph as the azure module will be deprecated soon anyway. (June 2022)
YES, This is possible!
But you need to do a little trick because it is only accessible via the Graph API. If you want to achieve that by PowerShell, you need to create an application, with a secret, that has access with the permission AuditLog.Read.All and User.Read.All and call this application with Graph command to do your query.
You can see my PowerShell script bellow:

#Replace [tenant_id], [Application_ID], [client_secret] with your own values

#Provide your Office 365 Tenant Id or Tenant Domain Name
$TenantId = "[tenant_id]"

#Provide Azure AD Application (client) Id of your app.
#You should have granted Admin consent for this app to use the application permissions "AuditLog.Read.All and User.Read.All" in your tenant.
$AppClientId="[Application_ID]"

#Provide Application client secret key
$ClientSecret = "[client_secret]"

$RequestBody = @{client_id=$AppClientId;client_secret=$ClientSecret;grant_type="client_credentials";scope="https://graph.microsoft.com/.default";}
$OAuthResponse = Invoke-RestMethod -Method 'Post' -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" -Body $RequestBody
$AccessToken = $OAuthResponse.access_token

#Form request headers with the acquired $AccessToken
$headers = @{'Content-Type'="application\json";'Authorization'="Bearer $AccessToken"}

#This request get users list with signInActivity.
$ApiUrl = "https://graph.microsoft.com/beta/users?`$select=displayName,userPrincipalName,signInActivity,userType,assignedLicenses,mail,createdDateTime&`$top=999"
$Result = @()

While ($ApiUrl -ne $Null){ #Perform pagination if next page link (odata.nextlink) returned.
$Response = Invoke-WebRequest -Method 'GET' -Uri $ApiUrl -ContentType "application\json" -Headers $headers | ConvertFrom-Json
if($Response.value){
$Users = $Response.value
ForEach($User in $Users){
#Filter only Guests
if ($User.userType -eq 'Guest'){
$Result += New-Object PSObject -property $([ordered]@{
UserID = $User.id
DisplayName = $User.displayName
ExternalDomain = if ($User.mail) {$User.mail.Split("@")[1]} else {$null}
Email = $user.mail
UserPrincipalName = $User.userPrincipalName
CreationDateTime = if($User.createdDateTime) {[DateTime]$User.createdDateTime} else {$null}
LastSignInDateTime = if($User.signInActivity.lastSignInDateTime) { [DateTime]$User.signInActivity.lastSignInDateTime } else {$null}
IsLicensed = if ($User.assignedLicenses.Count -ne 0) { $true } else { $false }
IsGuestUser = if ($User.userType -eq 'Guest') { $true } else { $false }
URL = "https://portal.azure.com/#blade/Microsoft_AAD_IAM/UserDetailsMenuBlade/Profile/userId/"+$user.id
})
}
}
}
$ApiUrl=$Response.'@odata.nextlink'
}

#Export to local computer
$Result | Export-CSV "C:\Temp\LastLoginDateReport.CSV" -NoTypeInformation -Encoding UTF8

Enjoy! :)
There is a way, please see my post on this topic and unmark your post as answer.