Table of Contents
A Proliferation of Guest User Accounts Within Tenants
Updated July 19. 2023
Guest User Accounts are terrifically useful in terms of allowing people outside your Microsoft 365 tenant to access resources inside the tenant. Applications like Teams, SharePoint Online, Planner, and Outlook Groups use the Entra B2B Collaboration framework to create new guest accounts to share information, whether it’s the membership of a team or access to a shared document or folder.
Old guest accounts can accumulate over time. For instance, guest accounts might be needed to share just one document. A regular spring cleaning is needed to ensure that you detect old guest accounts that are possibly no longer needed.
The Problem of Deciding When to Leave
As always, the problem is to decide when a guest account should be removed. Left by themselves, guest accounts will remain in the tenant directory because neither Microsoft 365 nor Entra ID include an automated method to clean up guests past their best-by date. One approach is to review guest accounts that are older than a certain age and look for evidence to indicate if they should be removed.
For example, you might decide that membership of multiple Microsoft 365 groups (aka Office 365 groups) is sufficient reason to keep guest accounts. The logic here is that these memberships give people access to Teams (conversations), Outlook Groups (conversations delivered via email), and Planner (group tasks). Therefore, if we write a script to scan for guest accounts older than x days and then check if these accounts are members of groups, we should have some evidence upon which to base a decision to remove or keep.
PowerShell Script to Highlight Old Guest Accounts and their Group Membership
The script below does the following:
- Connects to the Graph with the Connect-MgGraph cmdlet.
- Finds all guest accounts in the tenant using the Get-MgUser cmdlet.
- Checks each guest to discover its age using the account creation date.
- If the guest account is older than 365 days, we look for its group membership by running the Get-MgUserMemberOf cmdlet and report the display names of any groups found.
- Checks the last sign-in activity for the account. This could be an important indicator that the account is active.
- Writes the discovered information out to an array.
- After all guest accounts are processed, the script writes the contents of the array containing information about old guest accounts to a CSV file.
Some example code is shown below. The latest version of the script is available on GitHub and is the version which you should download and use. The latest version runs with the Microsoft Graph PowerShell V2 cmdlets.
Remember that you might want to update the code to add error handling and do whatever testing is necessary before running the script against your production tenant.
# Script to find Old Guest Accounts in a Microsoft 365 Tenant that are older than 365 days and the groups they belong to
# Find guest accounts
Write-Host "Finding Guest Accounts..."
[Array]$GuestUsers = Get-MgUser -Filter "userType eq 'Guest'" -All -Property Id, displayName, userPrincipalName, createdDateTime, signInActivity `
| Sort-Object displayName
$i = 0; $Report = [System.Collections.Generic.List[Object]]::new()
# Loop through the guest accounts looking for old accounts
CLS
ForEach ($Guest in $GuestUsers) {
# Check the age of the guest account, and if it's over the threshold for days, report it
$AccountAge = ($Guest.CreatedDateTime | New-TimeSpan).Days
$i++
If ($AccountAge -gt $AgeThreshold) {
$ProgressBar = "Processing Guest " + $Guest.DisplayName + " " + $AccountAge + " days old " + " (" + $i + " of " + $GuestUsers.Count + ")"
Write-Progress -Activity "Checking Guest Account Information" -Status $ProgressBar -PercentComplete ($i/$GuestUsers.Count*100)
$StaleGuests++
$GroupNames = $Null
# Find what Microsoft 365 Groups the guest belongs to... if any
[array]$GuestGroups = (Get-MgUserMemberOf -UserId $Guest.Id).additionalProperties.displayName
If ($GuestGroups) {
$GroupNames = $GuestGroups -Join ", "
} Else {
$GroupNames = "None"
}
# Find the last sign-in date for the guest account, which might indicate how active the account is
$UserLastLogonDate = $Null
$UserLastLogonDate = $Guest.SignInActivity.LastSignInDateTime
If ($Null -ne $UserLastLogonDate) {
$UserLastLogonDate = Get-Date ($UserLastLogonDate) -format g
} Else {
$UserLastLogonDate = "No recent sign in records found"
}
$ReportLine = [PSCustomObject][Ordered]@{
UPN = $Guest.UserPrincipalName
Name = $Guest.DisplayName
Age = $AccountAge
"Account created" = $Guest.createdDateTime
"Last sign in" = $UserLastLogonDate
Groups = $GroupNames }
$Report.Add($ReportLine) }
} # End Foreach Guest
Sample Report File
The output CSV file is shown (somewhat obscured to protect the names of the guilty) in Figure 1. Any guest that isn’t a member of at least one Microsoft 365 group is a potential delete target. As you can see from the created column, it’s easy for old and stale guest accounts to linger on unless you clean them up from time to time.
Details of a more comprehensive report of membership of Microsoft 365 Groups including both tenant and guest members and summary details are explained in this article.
We have lots of other PowerShell examples of how to manage inactive Entra ID guest accounts and other Microsoft 365 objects in the Office 365 for IT Pros eBook.
