Entra / Microsoft 365 · Users & guests
Process expiring guest accounts
Remove expired guest accounts, identify guests expiring in the next 30 days, and email administrators with accounts to review.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-AzAccount -Identity
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([int] $LookbackDays = 30)Connect-AzAccount -Identity$AccessToken = Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com"Connect-MgGraph -AccessToken $AccessToken.Token -Scopes User.ReadWrite.AllSelect-MgProfile Beta# Connect to Exchange Online# Connect-ExchangeOnline -ManagedIdentity -Organization redmondassociates.onmicrosoft.com# Get display name for the tenant$TenantName = (Get-MgOrganization).displayName# Define variables[datetime]$CheckDate = (Get-Date).AddDays(14)[datetime]$Now = Get-Date[datetime]$NewExpirationDate = (Get-Date).AddDays(120)[datetime]$Check30 = (Get-Date).AddDays(-$LookbackDays)[int]$i = 0$Report = [System.Collections.Generic.List[Object]]::new() # Create output file for report[array]$Guests = Get-MgUser -Filter "userType eq 'Guest'" -All -Property SigninActivityForEach ($Guest in $Guests) {$i++#Write-Output ("Processing {0} {1}/{2}" -f $Guest.displayName, $i, $Guests.count)$UserLastSignInDate = $Null[datetime]$GuestExpirationDate = $Guest.onPremisesExtensionAttributes.extensionAttribute15$DaysExpired = $Null# Is account already marked as expired?If ($Guest.onPremisesExtensionAttributes.extensionAttribute14 -eq "Expired") {# Guest account is expired and can be deleted 7 days after expiration$DaysExpired = ($GuestExpirationDate | New-TimeSpan).DaysIf ($DaysExpired -ge 7) {Write-Output ("Removing guest account {0}" -f $Guest.displayname)Try {Remove-MgUser -UserId $Guest.Id$ReportLine = [PSCustomObject]@{Timestamp = Get-Date -format sId = $Guest.IdAction = "Account deleted"Name = $Guest.DisplayNameMail = $Guest.MailExpiration = $Guest.onPremisesExtensionAttributes.extensionAttribute15LastSignIn = "N/A" }$Report.Add($ReportLine)}Catch {Write-Output ("Error removing expired account{0} with expiration date of {1}" -f $Guest.displayName, $GuestExpirationDate)}} # End days expired check} # End processing section to remove expired accounts# Now check for accounts past their expiration dateIf (($Now -ge $GuestExpirationDate) -and ($Guest.onPremisesExtensionAttributes.extensionAttribute14 -ne "Expired")) {Write-Output ("Detected expired guest account {0} with an expiration date of {1}" -f $Guest.displayName, $Guest.onPremisesExtensionAttributes.extensionAttribute15)# Check last sign in date. If less than 30 days ago, extend the account by 120 daysIf ($Guest.SignInactivity.LastNonInteractiveSignInDateTime) {[datetime]$UserSignInDate = ($Guest.SignInactivity.LastNonInteractiveSignInDateTime)} Else {[datetime]$UserSignInDate = ($Guest.createdDateTime) }If ($UserSignInDate -ge $Check30) {# Extend the expiration dateUpdate-MgUser -UserId $Guest.Id -OnPremisesExtensionAttributes @{'extensionAttribute15' = (Get-Date $NewExpirationDate -format s)}$ReportLine = [PSCustomObject]@{Timestamp = Get-Date -format sId = $Guest.IdAction = "Account extended"Name = $Guest.DisplayNameMail = $Guest.MailExpiration = Get-Date($NewExpirationDate) -format gLastSignIn = $UserSignInDate }$Report.Add($ReportLine)} Else { # Mark the account as expired so that it will be removed the next time this job runsUpdate-MgUser -UserId $Guest.Id -OnPremisesExtensionAttributes @{'extensionAttribute14' = 'Expired'}$ReportLine = [PSCustomObject]@{Timestamp = Get-Date -format sId = $Guest.IdAction = "Account due to expire"Name = $Guest.DisplayNameMail = $Guest.MailExpiration = $Guest.onPremisesExtensionAttributes.extensionAttribute15LastSignIn = "N.A" }$Report.Add($ReportLine)} # End if sign-in data} # End check to mark expired accounts} # End ForEach$Report | Format-Table Id, Name, Action, Expiration, LastSignIn# Define variables for the mailbox used to send the message, the recipient, and the message subject$MsgFrom = Get-AzKeyVaultSecret -VaultName "Office365ITPros" -Name "ExoAccountName" -AsPlainText$ToAddress = "James.Smith@office365itpros.com"$MsgSubject = "Guest Account Expiration Information for $($TenantName)"# Define HTML header with styles$htmlhead="<style>.UserTable {border:1px solid #C0C0C0;border-collapse:collapse;padding:5px;}.UserTable th {border:1px solid #C0C0C0;padding:5px;background:#F0F0F0;}.UserTable td {border:1px solid #C0C0C0;padding:5px;}</style>"# Build the message including the audit details in a table$HtmlBody = "<body><p><font size='2' face='Segoe UI'><p><strong>Generated:</strong> $(Get-Date -Format g)</p><h2><u>Please check these guest accounts</u></h2><p><b>If guest accounts have been deleted in error, please recover them using the Microsoft Entra admin center (or PowerShell) and assign the accounts a new expiration date.</b></p><p>Accounts marked as expired will be removed the next time the job runs. Accounts marked as extended have sign-in activity in the last 30 days.</p><p></p><table class='UserTable'><caption><h2><font face='Segoe UI'>Guest Account Expiration Report</h2></font></caption><thead><tr><th>Id</th><th>Account Name</th><th>Email</th><th>Expiration date</th><th>Action</th><th>LastSignIn</th></tr></thead><tbody>"$Report = $Report | Sort-Object ActionForEach ($A in $Report) {$HtmlBody += "<tr><td><font face='Segoe UI'>$($A.Id)</td><td><font face='Segoe UI'>$($A.Name)</td></font><td><font face='Segoe UI'>$($A.Mail)</td></font><td><font face='Segoe UI'>$($A.Expiration)</td><td><font face='Segoe UI'>$($A.Action)</td></font><td><font face='Segoe UI'>$($A.LastSignIn)</td></tr></font>"}$HtmlBody += "</tbody></table><p>"$HtmlBody += '</body></html>'$EmailAddress = @{address = $ToAddress}$EmailRecipient = @{EmailAddress = $EmailAddress}$HtmlHeaderUser = "<h2>Guest Account Information</h2>"$HtmlMsg = "</html>" + $HtmlHead + $htmlbody + "<p>"# Construct the message body$MessageBody = @{content = "$($HtmlBody)"ContentType = 'html' }# Create a draft message in the mailbox used to send the message$NewMessage = New-MgUserMessage -UserId $MsgFrom -Body $MessageBody -ToRecipients $EmailRecipient -Subject $MsgSubject# Send the messageSend-MgUserMessage -UserId $MsgFrom -MessageId $NewMessage.Id
Parameters
ParameterDefaultNotes
-LookbackDays30Number of days ahead to flag guest accounts approaching expiration.Attribution
Author
Office365itpros