In local Active Directory we have the possibility to export last logon for each user but in Azure AD we don´t had that attribute before. From now it´s available in Microsoft Graph beta. There are still limitation in graph queries to filter the data so the best recommendations is to export the data with PowerShell and user filter from there. The requirement to export the data is to create an App registrations in Azure AD with correct permissions.

To get this to work we need to create an App registration in Azure AD.
1, Push New registration
2, Name the application: LastLogon (or whatever you want)
3, Redirect URL to https://localhost

4, Copy both Application ID and Directory ID to the script. We need this numbers to connect to the application with correct permissions.

5, Assign Microsoft Graph permissions to the application. We need three permissions and add the Application permission. Push API permissions to the left menu.
6, Remove the current User.Read and add the following permissions.
7, Add AuditLog.Read.All, Directory.Read.All, User.Read.All
8, Grant admin consent for Company

9, Push Certificates & secrets, Add new client secret and name the description to Last logon secret. Copy the value to the script. Now the application is ready to connect.

10, Modify the script with correct parameters the just run the script to export all data into an variable. When the data stored in a variable you can filter the result.

What happend if the SignInActivity value is blank?
To generate a lastSignInDateTime timestamp, you need a successful sign-in. Because the lastSignInDateTime property is a new feature, the value of the lastSignInDateTime property can be blank if:

  • The last successful sign-in of a user took place before this feature was released (December 1st, 2019).
  • The affected user account was never used for a successful sign-in.
Export Azure AD SignInActivity
Connect to App registrations and Export Azure AD SignInActivity
Create by Daniel Aldén

# Application (client) ID, Directory (tenant) ID, and secret
$clientID = "b7409785-cba3-43d5-b566-8d4287e629aa"
$tenantName = ""
$ClientSecret = "*secret*"
$resource = ""

$ReqTokenBody = @{
    Grant_Type    = "client_credentials"
    Scope         = ""
    client_Id     = $clientID
    Client_Secret = $clientSecret

$TokenResponse = Invoke-RestMethod -Uri "$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody

# Get all users in source tenant
$uri = '$select=displayName,userPrincipalName,signInActivity'

# If the result is more than 999, we need to read the @odata.nextLink to show more than one side of users
$Data = while (-not [string]::IsNullOrEmpty($uri)) {
    # API Call
    $apiCall = try {
        Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $uri -Method Get
    catch {
        $errorMessage = $_.ErrorDetails.Message | ConvertFrom-Json
    $uri = $null
    if ($apiCall) {
        # Check if any data is left
        $uri = $apiCall.'@odata.nextLink'

# Set the result into an variable
$result = ($Data | select-object Value).Value
$Export = $result | select DisplayName,UserPrincipalName,@{n="LastLoginDate";e={$_.signInActivity.lastSignInDateTime}}


# Export data and pipe to Out-GridView for copy to Excel
$Export | select DisplayName,UserPrincipalName,@{Name='LastLoginDate';Expression={[datetime]::Parse($_.LastLoginDate)}} | Out-GridView

# Export and filter result based on domain name (Update the domainname)
$Export | Where-Object {$_.userPrincipalName -match ""} | select DisplayName,UserPrincipalName,@{Name='LastLoginDate';Expression={[datetime]::Parse($_.LastLoginDate)}}

Hope you can enjoy this script to create basis to clean up cloud users in Azure AD.