Quick And Easy PowerShell Report For Azure AD Guest User Last Sign In

Microsoft has some cool tools for Guest user management. Implementing Access Reviews for example is great for ensuring expiration of Guest access when needed. We can also control who can invite Guests and which domains we allow Guests from. When this is all set up we have some really great governance over our B2B strategy in Microsoft 365. Unfortunately, there are a ton of organizations who didn’t have a full governance plan from day 1 and are now in remediation of Guest sprawl.

To help out with this remediation process, I’ve put together a straightforward script (available on GitHub) which will pull all guest users in a tenant, search the logs for the last sign in date/time and also list any apps they’ve logged into. This report is limited by the retention of AAD logs which is 30 days so keep that in mind when running. You’ll need to have an output folder present at c:\temp to export the results.

The only module required for this script is the AzureADPreview PowerShell Module which can be installed using the command “Install-Module AzureADPreview”

    Author: Sean McAvinue
    Contact: Sean@seanmcavinue., Twitter: @Sean_McAvinue
    Gets guest users last sign in action from AAD logs and exports user and signin list to CSV in C:\temp

##Get all guest users
$guests = Get-AzureADUser -Filter "userType eq 'Guest'" -All $true 

##Loop Guest Users
foreach ($guest in $guests) {

    ##Get logs filtered by current guest
    $logs = Get-AzureADAuditSignInLogs -Filter "userprincipalname eq `'$($guest.mail)'" -ALL:$true 

    ##Check if multiple entries and tidy results
    if ($logs -is [array]) {
        $timestamp = $logs[0].createddatetime
    else {
        $timestamp = $logs.createddatetime

    ##Build Output Object
    $object = [PSCustomObject]@{

        Userprincipalname = $guest.userprincipalname
        Mail              = $guest.mail
        LastSignin        = $timestamp
        AppsUsed          = (($logs.resourcedisplayname | select -Unique) -join (';'))

    ##Export Results
    $object | export-csv C:\temp\GuestUserSignins.csv -NoTypeInformation -Append

    Remove-Variable object

8 thoughts on “Quick And Easy PowerShell Report For Azure AD Guest User Last Sign In

  1. Thank you for providing this! I had to make some tweaks to this script because:

    if ($logs -is [array]) {
    $timestamp = $logs[0].createddatetime
    else {
    $timestamp = $logs.createddatetime

    A lot of times would not return anything, after doing numerous spot checks against the user in Azure Active Directory. I did change this to the following with much better success:

    $timestamp = $logs.createddatetime
    If ($timestamp -eq $NULL){
    $timestamp = “BLANK”}
    {$timestamp = $logs[0].createddatetime}

    Additionally, I needed to do some spot checks against the creation date to show a customer that accounts that have existed for a while needed cleanup by pulling the creation date. I did this with some modifications:

    $exuser = $guest.extensionproperty
    $object = [PSCustomObject]@{

    Userprincipalname = $guest.userprincipalname
    UserState = $guest.UserState
    Enabled = $guest.AccountEnabled
    Name = $guest.DisplayName
    Mail = $guest.mail
    CreatedDate = $exuser.createdDateTime
    LastSignin = $timestamp
    AppsUsed = (($logs.resourcedisplayname | select -Unique) -join (‘;’))

    Finally to ensure that attributes were being cleared for the next loop, I updated the end to change Remove-Variable to Clear-Variable:

    Clear-Variable object
    Clear-Variable logs
    Clear-Variable timestamp
    Clear-Variable exuser

    Your script has been an invaluable part of my Azure Active Directory security audits for clients so I had to share some ways I’ve been able to improve 😁


  2. Robin Koele

    If got an error from azureadauditsigninlogs stating: too many requests.
    Solved it by adding a 2 second sleep at the foreach loop.

    Start-Sleep -s 2


  3. Nadim

    How on earth did you get this to work?

    $logs = Get-AzureADAuditSignInLogs -Filter “userprincipalname eq `’$($guest.mail)'” -ALL:$true

    It’s well documented out there that the filter switch can’t handle any variables. Replace it with a hard coded string and it’s fine. Can you confirm this DEFINITELY works? I copied your script and found it didn’t work. I’ve already been trying using my own script for months.


    1. It has always worked for me as long as it’s formatted correctly, for example I just ran this. note the formatting of the variable inside the string.
      Get-AzureADAuditSignInLogs -Filter “userprincipalname eq ‘$($user.userprincipalname)'” -ALL:$true


  4. Pingback: Explaining Azure AD External Identities

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s