During a recent engagement with a client, I was tasked with reviewing their Microsoft Entra ID tenant as part of a broader identity security assessment. One of my first observations was the presence of a significant number of inactive guest accounts.

For context, guest accounts are typically assigned to external users invited to collaborate with an organization. In this case, many of these accounts were months—or even years—old and had either never signed in or had long since stopped accessing the tenant.

This is a surprisingly common issue. Guest accounts are simple to create but often forgotten, and when left unmanaged, they present an unnecessary attack surface. Dormant accounts could potentially be compromised and exploited to gain access to internal resources.

The client needed an efficient way to identify, review, and clean up these accounts—without manually checking each one. In this write-up, I will outline how I approached this project and offer actionable strategies for managing inactive guest accounts in your own environment.

The Approach

Instead of performing a one-time manual cleanup, I created a reusable PowerShell script that connects to the tenant using the Microsoft Graph API and automates the entire process. Scripts like this not only save time but also ensure standardized implementation of such processes, allowing new team members to easily reuse the script in the future.

The script:

Using the dry run flag was crucial in this process. It allowed the client to first review a comprehensive report and verify that the list of guest users was correct. Only after confirming the accuracy of this list did we proceed to live mode, which executed the deletion of stale accounts.

Prerequisites

To replicate this in your own environment you will need:

The Script

The full script is available on GitHub: github.com/tmugema1/stale-guest-user-cleanup

Here is the core logic:

# Get all guest users

$guestUsers = Get-MgUser -Filter “userType eq ‘Guest'” -All -Property “displayName,mail,userPrincipalName,createdDateTime,id”

# Check sign-in activity and flag stale users

$today = Get-Date

$staleUsers = foreach ($guest in $guestUsers) {

    $signIns = Get-MgAuditLogSignIn -Filter “userId eq ‘$($guest.Id)'” -Top 1 | Select-Object -First 1

    if ($signIns) {

        $lastSignIn      = $signIns.CreatedDateTime

        $daysSinceSignIn = ($today – $lastSignIn).Days

    } else {

        $lastSignIn      = “Never”

        $daysSinceSignIn = 999

    }

    if ($daysSinceSignIn -ge $thresholdDays) {

        [PSCustomObject]@{

            Id           = $guest.Id

            DisplayName  = $guest.DisplayName

            Email        = $guest.Mail

            LastSignIn   = $lastSignIn

            DaysInactive = $daysSinceSignIn

        }

    }

}

The Output

The script exports a CSV report listing every stale guest account, their last sign-in date, and how many days they have been inactive. This gave the client a clear, auditable record of exactly who was removed and why. You could further customize the script to export the CSV file to a shared location such as SharePoint or OneDrive, automatically email it to an administrator. 

What We Did Next

After the initial cleanup, I recommended the client put in place ongoing governance practices to prevent the problem from recurring. These include: 

Conclusion

Stale guest accounts pose a common yet often overlooked security risk for many organizations. Fortunately, with the right tools, identifying and cleaning up these accounts can be straightforward. Automating this process ensures that it does not get neglected over time.

A critical point to remember is that when automating with your script, you should always include a step to review the results before deleting any accounts. This reduces the already slim risk of accidentally removing active guest accounts that users may still rely on.

If your organization has ungoverned guest access or has recently flagged inactive external users in a security audit, I can help you assess the problem and implement a long-term solution. Connect with me on tom@helpdesq.tech to discuss how I can help.

Leave a Reply

Your email address will not be published. Required fields are marked *