The Dormant Guest Problem
Run this query against any mature M365 tenant and the results will alarm you:
Get-MgUser -Filter "userType eq 'Guest'" -All |
Where-Object { $_.SignInActivity.LastSignInDateTime -lt (Get-Date).AddDays(-180) } |
Measure-Object
In a typical 500-seat tenant that has been running for three or more years, it is common to find 200-400 guest accounts that have not signed in for over six months. These are former contractors, agency staff, project collaborators from completed engagements, and vendor representatives who changed roles.
Each of these dormant accounts retains every SharePoint site membership, every Teams channel access, and every shared mailbox permission that was granted during the active engagement. If that external user's home tenant is compromised - or if their personal email account (for consumer guests) is phished, the attacker inherits all of those permissions in your tenant.
This is not theoretical. The 2024 Midnight Blizzard attack against Microsoft itself exploited a legacy OAuth application. Guest account hygiene is an equal or greater risk vector.
Invitation Controls: The First Gate
Before addressing lifecycle, restrict who can invite guests and under what conditions.
Entra ID External Collaboration Settings
Navigate to Entra ID > External Identities > External collaboration settings:
- Guest invite restrictions: Set to "Only users assigned to specific admin roles can invite guest users." This prevents any standard user from creating B2B invitations. The roles that retain invitation rights are Guest Inviter, User Administrator, and Global Administrator.
- Collaboration restrictions: Configure an allowlist of permitted external domains. If your firm only collaborates with known partners, explicitly list their tenant domains. Deny all others.
- Guest user access restrictions: Set to "Guest users have limited access to properties and memberships of directory objects." This prevents guests from enumerating your user directory.
Cross-Tenant Access Settings
For each trusted partner organisation, configure explicit cross-tenant access policies under Entra ID > External Identities > Cross-tenant access settings:
- Inbound access: Specify which of their users can access your tenant and which applications they can access
- Trust settings: Decide whether to trust MFA and device compliance claims from the partner tenant. Only trust MFA from partners whose security posture you have verified
- Automatic redemption: Enable for verified partners to eliminate the consent prompt
Conditional Access for Guests
Create a dedicated Conditional Access policy targeting guest and external users:
Policy name: CA-200: Guest Access Controls
- Users: Include "Guest or external users" > "B2B collaboration guest users"
- Cloud apps: All cloud apps
- Conditions: N/A (applies universally)
- Grant: Require MFA + Require device to be marked as compliant OR require app protection policy (for unmanaged devices)
- Session: Sign-in frequency of 4 hours (forces re-authentication more aggressively than internal users), Conditional Access App Control for monitoring
This ensures that even valid guest accounts face continuous verification. Many organisations overlook this, their guest users inherit the less restrictive "all users" policies and avoid device compliance checks entirely.
Access Reviews: The Core Lifecycle Mechanism
Entra ID Access Reviews automate the periodic recertification of guest access. Without this, you are relying on site owners to remember to remove people - which never happens.
Creating a Guest Access Review
Navigate to Entra ID > Identity Governance > Access reviews > New access review:
Step 1 - Review type:
- Review scope: "Guest users only"
- Scope: "All Microsoft 365 groups with guest users" (this captures Teams, SharePoint sites, and shared mailboxes)
Step 2 - Reviews:
- Frequency: Quarterly
- Duration: 14 days (reviewers have two weeks to respond)
- Reviewers: "Group owners" (the person who owns the Team or site must justify continued guest access)
Step 3 - Settings:
- Auto-apply results: Yes
- If reviewer doesn't respond: Remove access
- At end of review, apply results to: Remove denied users' membership from the resource
- Send notifications to reviewers: Yes
Step 4 - Advanced:
- Show recommendations: Yes (Entra will recommend "Deny" for guests who have not signed in during the review period)
- Require reason on approval: Yes
This creates a quarterly cycle where every Team owner must actively confirm that each guest in their Team still needs access. If they ignore the review, the guest is automatically removed. This is essential for ISO 27001 A.5.19 (Information security in supplier relationships) and A.9.2.5 (Review of user access rights).
Expiration Policies
Guest Account Expiration
Configure automatic cleanup of inactive guest accounts using Entra ID Lifecycle Workflows (requires Entra ID Governance licence):
- Navigate to Entra ID > Identity Governance > Lifecycle Workflows
- Create a new workflow triggered by "User attribute changes"
- Set the condition:
userType eq 'Guest' AND signInActivity.lastSignInDateTime lt [180 days ago] - Actions: Send notification to sponsor > Wait 14 days > Block sign-in > Wait 30 days > Delete user
Alternatively, for organisations without the Governance licence, script this with PowerShell on a scheduled Azure Automation runbook:
$threshold = (Get-Date).AddDays(-180)
$staleGuests = Get-MgUser -Filter "userType eq 'Guest'" -All -Property SignInActivity |
Where-Object { $_.SignInActivity.LastSignInDateTime -lt $threshold }
foreach ($guest in $staleGuests) {
# Block sign-in first, don't delete immediately
Update-MgUser -UserId $guest.Id -AccountEnabled:$false
# Log for audit
Write-Output "Disabled stale guest: $($guest.DisplayName) - $($guest.UserPrincipalName)"
}
SharePoint Sharing Link Expiration
Even after a guest is removed, sharing links they were granted may persist. Configure tenant-wide link expiration:
In the SharePoint Admin Centre > Policies > Sharing:
- "These links must expire within this many days": Set to 30 days for "Specific people" links and 7 days for "Anyone" links (if Anyone links are permitted at all - in regulated environments, disable them entirely)
- "These links can give these permissions": View only (restrict edit permissions to explicitly managed site memberships, not ad-hoc links)
Monitoring Guest Activity
Deploy Sentinel analytics rules to monitor for anomalous guest behaviour:
SigninLogs
| where UserType == "Guest"
| where ResultType == 0 // Successful sign-in
| summarize SignInCount = count(), DistinctApps = dcount(AppDisplayName),
DistinctLocations = dcount(Location) by UserPrincipalName, bin(TimeGenerated, 1d)
| where DistinctLocations > 3 or DistinctApps > 5
This detects guests accessing an unusual number of applications or signing in from multiple locations - potential indicators of compromised guest credentials.
The Complete Lifecycle
To summarise the end-to-end guest lifecycle:
- Invitation: Restricted to authorised inviters, scoped to approved domains
- Onboarding: Guest passes Conditional Access (MFA + device compliance/app protection)
- Active use: Monitored via Sentinel, sign-in frequency enforced at 4 hours
- Periodic review: Quarterly access review by resource owner, with auto-removal on non-response
- Inactivity detection: 180-day inactivity threshold triggers account disablement
- Offboarding: Account deleted after 30-day grace period, sharing links expired
Every stage is automated. No stage relies on human memory. That is the only way guest lifecycle management works at scale.