Is there a way in AzureAD to get last logins and what program they logged in with?

Hello Powershellers
I have a question which I believe I am 95% complete but I cannot seem to find the issue.

I am trying to get the last login of the users in my tenant as well as what service (Outlook / Sharepoint / etc) they used to log in.
I’ve ran a few scripts that give me results but nothing that I really need.

I am looking for a script (or a set of cmdlets) that would output me a CSV that would look something like this

Column A = UPN
Column B = Last Login
Column C = What they used to log in (Outlook / Sharepoint / Teams)

This is my script

     $file = Import-Csv -Path "My_file_with_usernames.csv" -delimiter ","
         
     foreach ($User in $file) {
         Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$User'" -Top 1 | `
         select CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress, TokenIssuerType, @{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
        
             #write data to file
             $userData += $OutputData;
             $userData | Export-csv -path C:\Path-to-my-CSV -Append -Encoding UTF8
        
            }

The purpose of this script is to go take everyone from Column A in my CSV (UserPrincipalName) and get the following info

Lastlogindate
Interactive login or not
What IP they are logging in from
What application they logged in from
OS

My current problem
I ran the line
Import-Module AzureADPreview
But I still get the same issue

Get-AzureADAuditSignInLogs : The term ‘Get-AzureADAuditSignInLogs’ is not recognized as the name of a cmdlet

Get-Module AzureADPreview
It does not return any version
But when I run
Install-Module AzureADPreview
It just skips to the next line

I’m running these commands on a powershell that has administrative rights

When I run the installer with the verbose command I get the following (Install-module AzureADPreview -Verbose)

 VERBOSE: Using the provider 'PowerShellGet' for searching packages.
 VERBOSE: The -Repository parameter was not specified.  PowerShellGet will use all of the registered repositories.
 VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'.
 VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2' and PackageManagementProvider is 'NuGet'.
 VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='AzureADPreview'' for ''.
 VERBOSE: Total package yield:'1' for the specified package 'AzureADPreview'.
 VERBOSE: Skipping installed module AzureADPreview 2.0.2.149.

This is my PS Version

 Name                           Value
 ----                           -----
 PSVersion                      5.1.22000.832
 PSEdition                      Desktop
 PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
 BuildVersion                   10.0.22000.832
 CLRVersion                     4.0.30319.42000
 WSManStackVersion              3.0
 PSRemotingProtocolVersion      2.3
 SerializationVersion           1.1.0.1

I also have PS V7 installed on my machine (I don’t think it works on PS7) and when I run the command from PS7 I get the following output

Untrusted repository
 You are installing the modules from an untrusted repository. If you trust this repository, change its
 InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
 'PSGallery'?
 [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): a
 VERBOSE: The installation scope is specified to be 'CurrentUser'.
 VERBOSE: The specified module will be installed in 'C:\Users\Me\Documents\PowerShell\Modules'.
 VERBOSE: Version '2.0.2.149' of module 'AzureADPreview' is already installed at 'C:\Program Files\WindowsPowerShell\Modules\AzureADPreview\2.0.2.149'.
 Name                           Value
 ----                           -----
 PSVersion                      7.2.7
 PSEdition                      Core
 GitCommitId                    7.2.7
 OS                             Microsoft Windows 10.0.22000
 Platform                       Win32NT
 PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
 PSRemotingProtocolVersion      2.3
 SerializationVersion           1.1.0.1
 WSManStackVersion              3.0

Any help will be greatly appreciated!

Thank you very much

First I would check the output of

Import-Module AzureAdPreview -Force -Verbose

Assuming you don’t get any errors and see all the different cmdlets that were imported, my next suggested step is

Connect-AzureAD

Without connecting, you won’t be able to get any audit logs.

Success so far.
But I am getting this error now

Get-AzureADAuditSignInLogs : Error occurred while executing GetAuditSignInLogs
Code: UnknownError
Message: Too Many Requests
InnerError:
  RequestId: 224647ce-a92e-46c4-b20a-525835e42c82
  DateTimeStamp: Tue, 01 Nov 2022 19:41:14 GMT
HttpStatusCode: 429
HttpStatusDescription:

My script looks like this (am I doing something wrong)?

$file = Import-Csv -Path "TEST-UPN-LIST.csv" -delimiter ","

foreach ($user in $file) {
    Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$User'" -Top 1 | `Export-csv -path .\Test.csv -Append -Encoding UTF8
    select CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress, TokenIssuerType, @{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
         
}	


Does this look right? Why would I be getting too many requests?

Anyone have any ideas?
I’m at a bit of a loss.
If I’m going about this all wrong I’m open to any other suggestion / way of doing this

Basically, going back to my original post all I really want is to be able to have a script that will look at my CSV file that has my users in column A under the header UserPrincipalName and output it to a CSV file that has

Column A = Username
Column B = Last sign in date
Column C = What application they used

I’m really in need of some help here

Again, I apologize for asking all these questions or not fully understanding where my error is.

Your code isn’t right, but that would not cause the error so you have two separate problems.

Does your CSV have a header?

Your Select-Object should appear before Export-Csv, otherwise what are you selecting from?

There are some posts on other forums about the Too Many Requests message. The proposed solution is to pause between requests. I tried it on my dev tenant and I got the error with anything less than 2 seconds; that’ll be slow if you have a lot of users (the dev tenant has only 25).

This works for me:

Import-Module AzureADPreview

Connect-AzureAD -TenantId <your GUID here>

$userList = Import-Csv .\TEST-UPN-LIST.csv

foreach ($user in $userList) {
    
    Start-Sleep -Seconds 2

    $auditInfo = Get-AzureADAuditSignInLogs -Filter "startsWith(userPrincipalName,'$($user.UserPrincipalName)')" -Top 1

    [PSCustomObject] @{
        Username    = $auditInfo.UserPrincipalName
        LastSignIn  = $auditInfo.CreatedDateTime
        Application = $auditInfo.AppDisplayName
    } | Export-Csv .\AuditResults.csv -Append -NoTypeInformation

}

While trying to get this working, I noted that:

The AzureADPreview module didn’t work in 7.2.7.
The AzureADPreview module didn’t work in the ISE or VSCode.
The AzureADPreview module didn’t work with #Requires -modules

Hello Matt,
Thanks for taking the time to answer the questions.
Since the last time someone answered me in the thread I actually advanced quite a bit in getting results. With the help of a few other major geeks (way more geek than me) I was able to get a script that produces an output I want.

Here is an example of what I am using

 $Users = Import-CSV -Path ".\test3.csv"
>>
>> $SignIns = foreach ($User in $Users) {
>>     Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$($User.Users)'" -Top 1 |
>>     select CreatedDateTime, UserPrincipalName, IsInteractive, AppDisplayName, IpAddress, TokenIssuerType, @{Name = 'DeviceOS'; Expression = {$_.DeviceDetail.OperatingSystem}}
>>     Start-Sleep -MilliSeconds 400
>> }
>>
>> $SignIns | Export-Csv -Path "C:\Export.csv"

However, the only real challenge I’m facing now is to get a little more information that would be relevant to what I want to do.

– If the last logon was passed 30 days to be able to get the time stamp that exists in the Azure Active Directory when we go to Users - Sign-In Logs. When the user has not logged in for more than 30 days the current script that I have will not give out any data. So, for example, if I have 25 users in my script but only 20 have signed in during the last 30 days my output will be missing the other 5 users. But, if those 5 users signed in 40 days ago then in the AAD Users - Sign In (In Overview) there will be a Last Sign In

According to the current script all the switches are correct, they actually work well, I’m just trying to find a way to get all the other users who have not logged in for more than 30 days, I would like some kind of information. If I cannot get what software they logged in to passed 30 days at the very least get that last timestamp that is in Azure AD

PS I’m not too concerned about the delay, I currently have a delay of 400MS which works well up to 50 users at a time. So that is OK for me

Hello just for some clarity
CreatedDateTime ← Does this attribute only give me the sign in attempts (whether it is failure or success) or only success?
I’m looking at a user who says that there was an Azure Sign-In but when I extract the sign in logs I only see failures.
Thanks!

It’s the time of the log entry, success or failure. If you only want a specific type, you’ll need to modify your filter. This will give the last successful entry:

Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq '$($User.Users) and status/errorCode eq 0'" -Top 1

By default you can’t get the info. after 30 days. As far as I can tell, it’s not stored with the user object like it is with on-premises AD so you have to search the logs. If you want to keep the logs for longer than 30 days you’ll have to send the logs to a Log Analytics workspace.

Thanks Matt!
Very helpful! I was getting weird log entries and I didn’t understand how that particular user was successfully logging on.
I just wanted to double check if it was showing as an attempt rather than success / failure!