Forum Discussion

JakobRohde's avatar
JakobRohde
Iron Contributor
Sep 22, 2017

List all users' last login date

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.

  • Staniko's avatar
    Staniko
    Copper Contributor

    JakobRohde 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

     

     

     

    • jjdmsgc's avatar
      jjdmsgc
      Copper Contributor

      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? 🙂

      • Staniko's avatar
        Staniko
        Copper Contributor

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

        Install-Module AzureADPreview -AllowClobber -Force

         return? 

    • Dia_2's avatar
      Dia_2
      Copper Contributor
      This is great! how would you set the script to run aganst a specific dynamic cloud security group?
    • Joe_Ghaleb's avatar
      Joe_Ghaleb
      Copper Contributor
      i ran the script but it's giving me empty results on the LastSignInDate, do you know why this is happening?
  • No. Use the report in the O365 admin center -> Reports -> Usage -> Active users.

  • NicolasHon's avatar
    NicolasHon
    Brass Contributor
    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! 🙂
      • NicolasHon's avatar
        NicolasHon
        Brass Contributor
        Thanks for this information. I never had this issue.
    • dcorso's avatar
      dcorso
      Copper Contributor

      NicolasHonThanks for this it worked as is.  I was close but was having trouble with the actual lastsignindate being output.  I think the OP originally wanted all users not just Guest users but should be easy to figure that out. 

    • Deleted's avatar
      Deleted
      Hi NicolasHon, does on have to create an app still? I see in the docs that one can authenticate directly in powershell with an admin account by using graph SDK (https://docs.microsoft.com/en-us/graph/powershell/get-started) Or have I misunderstood?

      Could something like this work with only direct commands in powershell?

      Install-Module Microsoft.Graph

      Connect-MgGraph -Scope "User.Read.All"

      Select-MgProfile -Name "beta"

      Get-MgUser -Filter "signInActivity/lastSignInDateTime le 2021-09-30T00:00:00Z$select=id,displayName,userType"

      $Result | Export-CSV "C:\Temp\LastLoginDateReport.CSV" -NoTypeInformation -Encoding UTF8
      • NicolasHon's avatar
        NicolasHon
        Brass Contributor
        Hi Niklask, yes you could also achieve that using this way. The thing is that you need to have an admin role to approve Microsoft Graph module when running it from PowerShell. It is less practical as I don't have a permanent admin so I need to go in PIM, activate my admin role, and then run the script. With the app this is not the case as I don't use Graph commands in PowerShell and just do Graph API's calls. The app allow you also to schedule this script in a scheduled task or to access it from a Power Automate Flow to do periodic report.
    • Robert Luck's avatar
      Robert Luck
      Iron Contributor

      The provided script gives you the last login information of users who have Exchange Online license whereas the requirement is to display "last logon time" of unlicensed users as well.

      • Deleted's avatar
        Deleted

        Hi Robert,

        I tested the script it also provides the logon times for "Unlicensed Users" as well and exports them to a .CSV.

  • Once you've logged in and authenticated against your Office 365 tenant, you can then use the below commands.

     

    # Connects you to Windows Azure Active Directory

    Connect-MsolService

     

    # Gets Unlicenced users and lists the "DisplayName, LastLogonTime and LastLogoffTime"

    Get-MsolUser -UnlicensedUsersOnly | Foreach {Get-MailboxStatistics $_.UserPrincipalName | Select DisplayName, LastLogonTime, LastLogoffTime}

    • Jay Carper's avatar
      Jay Carper
      Brass Contributor

      What about users who don't have mailboxes? I have a number of users for whom we have disabled the Exchange Online license. How can I get their logon statistics?

  • Kevin_Morgan's avatar
    Kevin_Morgan
    Iron Contributor

    JakobRohde VasilMichev 

     

    The Microsoft Graph API now supports the resource property signInActivity in users end-point, this resource exposes the lastSignInDateTime property which shows the last time a user made a successful sign-in. Fetching signInActivity property requires an Azure AD Premium P1/P2 license and the AuditLog.Read.All permission. The following request retrieves user details along with signInActivity property.

     

    #GET Request
    https://graph.microsoft.com/beta/users?$select=displayName,signInActivity

     

    Before Microsoft Graph supports this property, we need to either get the mailbox last logon time using the Get-MailboxStatistics cmdlet or we need to crawl the Azure AD sign-in logs or the Unified audit logs in the Security and Compliance Center. 

     

    You can refer the below post to know more details about how to find and export Last login date for all Azure AD Users using PowerShell.

     

    • Joshua Bines's avatar
      Joshua Bines
      Iron Contributor

      Kevin_Morgan license? 😉 the lastsignin property is a static value that is populated for all user accounts including guest accounts (see thread) back to apr 2020.  

       

      Edit: I'm guessing you mean the tenant has 'any' aad p1/p2. Haven't tested this maybe you are right? 

      • Kevin_Morgan's avatar
        Kevin_Morgan
        Iron Contributor

        Joshua Bines 

         

        Yes the value may get populated for all users. But we need the license to retrieve the lastlogin value through Microsoft Graph API. The Microsoft's document itself still indicates the same and so my testing. Hope still there is no other API or PowerShell cmdlet to get the details without Azure AD Premium P1/P2 license. Can you please point me if you know any resources?

  • Malith_Jayashan's avatar
    Malith_Jayashan
    Copper Contributor

    JakobRohde 

    This can be done using AzureADPreview

    Import-Module AzureADPreview
    $UsersUPN = (Get-AzureADUser -Top 20000).UserPrincipalName
    
    foreach($user in $UsersUPN)
        {
            Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$user'" -Top 1| `
            select CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress, TokenIssuerType, @{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
            Start-Sleep -Seconds 5
        }

     A Start-Sleep -Seconds 5 delay should be added in order to bypass the "Too many requests" error code

  • While this is good information for AAD, it appears to be limited to the past 90 days. We are looking for something static and retrievable like LastLogonTimestamp in AD or LastLogonTime in Exchange Online MailboxStatistics.
  • maureris's avatar
    maureris
    Copper Contributor
    Hi Jakob,

    Sorry, is possible to use one command in PowerShell for extraction the last logon in software Microsoft Project?

    Thank you

Resources