Reconnaissance & OSINT
Map the target's Microsoft 365 footprint before touching a single authentication endpoint. Discover domains, tenant configuration, user accounts, and exposed services using only passive and semi-passive techniques.
Phase 1 Checklist
0 / 8 completed🔍 Tenant Discovery
The first objective is to discover every domain associated with the target's Microsoft 365 tenant. Organisations often register multiple domains (primary, regional, vanity, subsidiary) under a single Entra ID tenant. Each domain is a potential attack surface.
AADInternals — Enumerate Tenant Domains
This queries the Microsoft autodiscover and federation endpoints to return all domains registered under the tenant associated with the given domain. No authentication is required.
# Install AADInternals if not already present
Install-Module AADInternals -Scope CurrentUser
Import-Module AADInternals
# Enumerate all domains registered in the target tenant
Get-AADIntTenantDomains -Domain "targetcompany.com"
# Example output:
# targetcompany.com
# targetcompany.onmicrosoft.com
# targetcompany.mail.onmicrosoft.com
# subsidiary-brand.com
# targetcompany.co.uk
DNS-Based Domain Validation
Verify Microsoft 365 usage by checking for well-known DNS records that Microsoft requires for domain verification and service routing.
# Check for Microsoft domain verification TXT record
dig TXT targetcompany.com +short
# Look for: "MS=ms12345678" or "v=spf1 include:spf.protection.outlook.com"
# Check for the msoid CNAME (Microsoft Online ID)
dig CNAME msoid.targetcompany.com +short
# Resolves to: clientconfig.microsoftonline-p.net
# Check Autodiscover CNAME (Exchange Online)
dig CNAME autodiscover.targetcompany.com +short
# Resolves to: autodiscover.outlook.com
# Check for DMARC policy
dig TXT _dmarc.targetcompany.com +short
# Reveals mail handling policies and reporting addresses
Certificate Transparency Logs
Query certificate transparency (CT) logs to discover subdomains and related domains that may share the same tenant. Certificates issued for Microsoft-hosted services can reveal additional domain names.
# Query crt.sh for certificate transparency logs
curl -s "https://crt.sh/?q=%25.targetcompany.com&output=json" | \
jq -r '.[].name_value' | sort -u
# Use Amass for comprehensive subdomain enumeration
amass enum -passive -d targetcompany.com -o subdomains.txt
# Search for related domains via WHOIS registrant data
whois targetcompany.com | grep -i "registrant"
.onmicrosoft.com domain is always present and cannot be removed. It is often the initial tenant domain and may follow a predictable naming convention such as companyname.onmicrosoft.com. Discovering this domain confirms Microsoft 365 usage and provides a reliable identifier for the tenant.
Get-AADIntTenantDomains uses public Microsoft endpoints and does not generate sign-in logs. Consider whether your organisation's subsidiary and vanity domains need to be associated with the primary tenant, or whether they should be isolated. Unused domains that remain verified in your tenant increase your attack surface.
Every Entra ID (Azure AD) tenant has a globally unique identifier (GUID). Knowing the tenant ID allows you to query specific Microsoft APIs, target the correct authorization endpoints, and distinguish between tenants when an organisation has multiple.
OpenID Configuration Endpoint
Microsoft publishes an OpenID Connect discovery document for every tenant. This document is publicly accessible and contains the tenant ID embedded in the issuer and authorization endpoint URLs.
# Retrieve the OpenID configuration for the target domain
curl -s "https://login.microsoftonline.com/targetcompany.com/.well-known/openid-configuration" | \
jq '.authorization_endpoint'
# Example output:
# "https://login.microsoftonline.com/a1b2c3d4-e5f6-7890-abcd-ef1234567890/oauth2/v2.0/authorize"
# The GUID in the URL is the tenant ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
# Extract just the tenant ID
curl -s "https://login.microsoftonline.com/targetcompany.com/.well-known/openid-configuration" | \
jq -r '.token_endpoint' | grep -oP '[a-f0-9-]{36}'
AADInternals — Get Tenant ID
# Resolve tenant ID from any verified domain
Get-AADIntTenantID -Domain "targetcompany.com"
# Example output:
# a1b2c3d4-e5f6-7890-abcd-ef1234567890
# You can also resolve from the onmicrosoft.com domain
Get-AADIntTenantID -Domain "targetcompany.onmicrosoft.com"
Alternative: Login Error Endpoint
Another approach uses the Azure AD login endpoint, which will redirect and expose the tenant ID in the response even for invalid requests.
# Use the OAuth2 authorize endpoint with a dummy client_id
curl -s -I "https://login.microsoftonline.com/targetcompany.com/oauth2/v2.0/authorize?client_id=test&response_type=code&redirect_uri=https://localhost" | \
grep -i "location"
# The redirect URL in the Location header contains the tenant ID
# You can also query the login metadata endpoint
curl -s "https://login.microsoftonline.com/getuserrealm.srf?login=user@targetcompany.com" | jq '.'
# Returns: NameSpaceType (Managed/Federated), DomainName, FederationBrandName, CloudInstanceName
getuserrealm.srf endpoint also reveals whether the domain uses Managed authentication (cloud-only passwords in Entra ID) or Federated authentication (ADFS, PingFederate, Okta, etc.). This distinction is critical for planning credential attacks in later phases.
Determine which Microsoft 365 services are active for the target tenant. Each service exposes distinct attack surfaces. DNS records are the primary indicator because Microsoft requires specific records for each service to function.
M365 Service DNS Fingerprints
Each M365 service requires specific DNS records. Checking for these records reveals which services are provisioned and actively configured.
| Service | DNS Record | Record Type | Expected Value |
|---|---|---|---|
| Exchange Online | autodiscover.domain.com | CNAME | autodiscover.outlook.com |
| Exchange Online | domain.com | MX | *.mail.protection.outlook.com |
| SharePoint Online | domain.com | CNAME | *.sharepoint.com |
| Skype / Teams | lyncdiscover.domain.com | CNAME | webdir.online.lync.com |
| Skype / Teams | _sipfederationtls._tcp.domain.com | SRV | sipfed.online.lync.com |
| Skype / Teams | sip.domain.com | CNAME | sipdir.online.lync.com |
| Intune / MDM | enterpriseregistration.domain.com | CNAME | enterpriseregistration.windows.net |
| Intune / MDM | enterpriseenrollment.domain.com | CNAME | enterpriseenrollment.manage.microsoft.com |
| DKIM (Exchange) | selector1._domainkey.domain.com | CNAME | selector1-domain-com._domainkey.*.onmicrosoft.com |
Automated DNS Service Check
#!/bin/bash
# Enumerate M365 services for a target domain
DOMAIN="targetcompany.com"
echo "[*] Checking Exchange Online..."
dig CNAME autodiscover.${DOMAIN} +short
dig MX ${DOMAIN} +short
echo "[*] Checking Teams / Skype for Business..."
dig CNAME lyncdiscover.${DOMAIN} +short
dig SRV _sipfederationtls._tcp.${DOMAIN} +short
dig CNAME sip.${DOMAIN} +short
echo "[*] Checking Intune / MDM Enrollment..."
dig CNAME enterpriseregistration.${DOMAIN} +short
dig CNAME enterpriseenrollment.${DOMAIN} +short
echo "[*] Checking DKIM..."
dig CNAME selector1._domainkey.${DOMAIN} +short
dig CNAME selector2._domainkey.${DOMAIN} +short
echo "[*] Checking SPF Record..."
dig TXT ${DOMAIN} +short | grep "spf"
echo "[*] Checking Microsoft Online ID..."
dig CNAME msoid.${DOMAIN} +short
SharePoint & OneDrive URL Discovery
SharePoint Online tenant URLs follow a predictable format. You can confirm their existence by making unauthenticated HTTP requests.
# Attempt to resolve the SharePoint Online tenant URL
# The tenant name is usually the onmicrosoft.com prefix
curl -s -o /dev/null -w "%{http_code}" "https://targetcompany.sharepoint.com"
# 200/302 = exists, 404 = does not exist
# Check for OneDrive (uses the -my suffix)
curl -s -o /dev/null -w "%{http_code}" "https://targetcompany-my.sharepoint.com"
# Check for common SharePoint site naming patterns
for site in intranet portal hr wiki team; do
curl -s -o /dev/null -w "$site: %{http_code}\n" \
"https://targetcompany.sharepoint.com/sites/${site}"
done
enterpriseregistration and enterpriseenrollment CNAMEs indicates that Intune MDM is configured, which means device compliance policies are likely in place. This is relevant for later phases when you may need to bypass device-based Conditional Access policies.
dnsrecon against your own domains as part of your security hygiene checks.
👤 User Enumeration
Before attempting to validate accounts, you need a list of candidate usernames. Microsoft 365 accounts typically follow an email format such as first.last@domain.com or flast@domain.com. OSINT sources provide the raw names; you then generate candidate UPNs (User Principal Names) matching the organisation's email format.
LinkedIn Employee Scraping
LinkedIn is the most reliable source of employee names. Tools like linkedin2username automate the process of scraping employee names and generating email addresses in common formats.
# linkedin2username - generate usernames from LinkedIn company page
# Requires valid LinkedIn session cookie
python3 linkedin2username.py -u "your-linkedin-email" -c "Target Company" -d "targetcompany.com"
# Output generates multiple format files:
# first.last@targetcompany.com
# flast@targetcompany.com
# firstl@targetcompany.com
# first_last@targetcompany.com
# CrossLinked - alternative LinkedIn scraper (no API key needed)
python3 crosslinked.py -f '{first}.{last}@targetcompany.com' "Target Company"
python3 crosslinked.py -f '{f}{last}@targetcompany.com' "Target Company"
hunter.io & Email Format Discovery
hunter.io reveals the email format used by an organisation and provides verified email addresses found in public sources.
# Query hunter.io API for email pattern and known addresses
curl -s "https://api.hunter.io/v2/domain-search?domain=targetcompany.com&api_key=YOUR_API_KEY" | \
jq '.data.pattern, .data.emails[].value'
# Example pattern result: "{first}.{last}"
# This tells you the email format is first.last@targetcompany.com
theHarvester — Comprehensive OSINT
# theHarvester - search multiple data sources for emails and subdomains
theHarvester -d targetcompany.com -b all -l 500 -f harvester_results
# Sources include: Google, Bing, Baidu, DNSDumpster,
# CertSpotter, Crtsh, LinkedIn, Twitter, and more
# Also check for emails in public breach databases (legal/ethical consideration)
# dehashed.com, haveibeenpwned.com (domain search)
Company Website & Documents
Author metadata in publicly available documents (PDFs, DOCX, XLSX) often contains internal usernames or full names that can be converted to email addresses.
# Use exiftool to extract metadata from downloaded documents
exiftool -Author -Creator -Producer -LastModifiedBy *.pdf *.docx *.xlsx
# Use Google dorks to find documents from the target domain
# site:targetcompany.com filetype:pdf
# site:targetcompany.com filetype:docx
# site:targetcompany.com filetype:xlsx
# Metagoofil - automates document metadata extraction
metagoofil -d targetcompany.com -t pdf,docx,xlsx -l 100 -o metadata_output
mat2 or built-in Office metadata removal. Consider monitoring for bulk scraping activity against your corporate LinkedIn page. Be aware that your email format is essentially public knowledge.
After harvesting candidate usernames, you need to validate which ones actually exist in the target Entra ID tenant. Microsoft exposes several endpoints that allow username enumeration without requiring a password attempt, making this a stealthy reconnaissance technique.
GetCredentialType API
The GetCredentialType endpoint is the primary method for unauthenticated user enumeration. It is the same endpoint that the Microsoft login page calls to determine how to authenticate a user. It returns different response codes depending on whether a username exists.
# GetCredentialType API - validate a single username
curl -s -X POST \
"https://login.microsoftonline.com/common/GetCredentialType" \
-H "Content-Type: application/json" \
-d '{"Username":"user@targetcompany.com"}' | jq '.'
# Key response fields:
# IfExistsResult: 0 = user EXISTS, 1 = user does NOT exist
# IfExistsResult: 5 = exists but in different tenant, 6 = exists (catch-all)
# ThrottleStatus: 1 = you are being throttled (slow down)
Bulk Enumeration with o365creeper
# o365creeper - bulk email validation against Office 365
python3 o365creeper.py -f userlist.txt -o valid_users.txt
# userlist.txt contains one email per line:
# john.smith@targetcompany.com
# jane.doe@targetcompany.com
# admin@targetcompany.com
# it.helpdesk@targetcompany.com
# valid_users.txt will contain only confirmed accounts
AADInternals — User Enumeration
AADInternals provides a dedicated function that uses multiple Microsoft endpoints (GetCredentialType, autodiscover, and others) to validate users.
# Enumerate a single user
Invoke-AADIntUserEnumerationAsOutsider -UserName "user@targetcompany.com"
# Bulk enumerate from a file
Get-Content ".\userlist.txt" | Invoke-AADIntUserEnumerationAsOutsider | Format-Table
# Example output:
# UserName Exists
# -------- ------
# john.smith@targetcompany.com True
# fake.user@targetcompany.com False
# admin@targetcompany.com True
# jane.doe@targetcompany.com True
# The function checks multiple methods:
# - GetCredentialType (login.microsoftonline.com)
# - Autodiscover (outlook.office365.com)
# - Azure AD Graph (desktopsso endpoint)
TeamFiltration — Teams Enumeration
TeamFiltration can validate users via the Microsoft Teams user search API. This method can sometimes bypass protections applied to the GetCredentialType endpoint.
# TeamFiltration - enumerate users via Teams API
# Requires a valid Teams token (from any tenant)
TeamFiltration --enum --userlist userlist.txt --token teams_token.txt
# This uses the Teams people search API to check if users exist
# It can also pull profile pictures and presence information
ThrottleStatus returns 1 in the response, your requests are being throttled and all results will appear as "user does not exist" regardless of reality. Slow down your enumeration rate to approximately 1 request per second, use rotating source IPs, or switch to an alternative enumeration method.
The GetCredentialType response contains far more than just user existence. It reveals the authentication methods configured for each user, including whether MFA is required, what type of MFA is configured, and whether the account uses federation. This intelligence is invaluable for planning credential attacks.
Parsing GetCredentialType Responses
# Full GetCredentialType request with detailed fields
curl -s -X POST \
"https://login.microsoftonline.com/common/GetCredentialType" \
-H "Content-Type: application/json" \
-d '{
"Username": "user@targetcompany.com",
"isOtherIdpSupported": true,
"checkPhones": true,
"isRemoteNGCSupported": true,
"isCookieBannerShown": false,
"isFidoSupported": true,
"originalRequest": "",
"flowToken": ""
}' | jq '.'
Response Field Analysis
Key fields in the GetCredentialType response and what they reveal about the target account:
| Field | Value | Meaning |
|---|---|---|
IfExistsResult |
0 | Account exists in tenant |
IfExistsResult |
1 | Account does not exist |
IfExistsResult |
5 | Account exists in a different tenant |
IfExistsResult |
6 | Account exists (domain-level match) |
ThrottleStatus |
1 | Requests are being throttled |
Credentials.PrefCredential |
1 | Password-based authentication |
Credentials.PrefCredential |
4 | FIDO2 / Windows Hello preferred |
Credentials.HasPassword |
true/false | Whether account has a password set |
EstsProperties.DesktopSsoEnabled |
true | Seamless SSO is configured (indicates on-prem AD) |
FederationRedirectUrl |
URL present | Account uses federated authentication (ADFS, etc.) |
Automated Auth Method Enumeration
# Simple Python script to check auth methods for a list of users
import requests, json, sys, time
url = "https://login.microsoftonline.com/common/GetCredentialType"
with open(sys.argv[1]) as f:
users = [line.strip() for line in f if line.strip()]
for user in users:
body = {
"Username": user,
"isOtherIdpSupported": True,
"checkPhones": True,
"isRemoteNGCSupported": True,
"isFidoSupported": True
}
resp = requests.post(url, json=body).json()
exists = resp.get("IfExistsResult", -1)
has_pw = resp.get("Credentials", {}).get("HasPassword", "N/A")
fed_url = resp.get("Credentials", {}).get("FederationRedirectUrl")
pref = resp.get("Credentials", {}).get("PrefCredential", -1)
sso = resp.get("EstsProperties", {}).get("DesktopSsoEnabled")
throttle = resp.get("ThrottleStatus", 0)
status = "EXISTS" if exists == 0 else "NOT_FOUND"
fed = "FEDERATED" if fed_url else "MANAGED"
print(f"{user} | {status} | {fed} | HasPW:{has_pw} | PrefCred:{pref} | SSO:{sso}")
if throttle == 1:
print("[!] THROTTLED - sleeping 30s")
time.sleep(30)
else:
time.sleep(1) # Rate limit to avoid throttling
MFASweep — MFA Configuration Check
Once you have valid credentials (in later phases), MFASweep can test which MFA methods are configured and whether any authentication endpoints lack MFA enforcement.
# MFASweep - test MFA enforcement across various Microsoft endpoints
# Run this once you have valid credentials (post Phase 3)
Import-Module MFASweep.ps1
Invoke-MFASweep -Username "user@targetcompany.com" -Password "Password123"
# MFASweep tests these endpoints for MFA enforcement:
# - Microsoft Graph API
# - Azure Service Management API
# - Microsoft 365 Exchange Web Services
# - Microsoft 365 Web Portal
# - Microsoft 365 Active Sync
# - ADFS (if federated)
HasPassword: false are likely passwordless accounts using FIDO2 keys or Windows Hello. These are harder to attack via traditional credential spraying. Prioritise accounts with HasPassword: true and PrefCredential: 1 (password-based) for Phase 3 attacks. Accounts showing a FederationRedirectUrl authenticate through an external IdP, so credential attacks should target that federation endpoint instead.
🖧 Infrastructure Mapping
Identifying the IP ranges and infrastructure associated with the target's Azure and Microsoft 365 deployment helps map their external footprint. SPF records reveal mail infrastructure, MX records show mail routing, and Azure IP ranges can indicate deployed resources.
SPF Record Analysis
SPF (Sender Policy Framework) records list the IP addresses and services authorised to send email for a domain. This reveals the target's mail infrastructure and any third-party email services they use.
# Retrieve and analyze the SPF record
dig TXT targetcompany.com +short | grep "spf"
# Example output:
# "v=spf1 include:spf.protection.outlook.com include:_spf.google.com ip4:203.0.113.0/24 -all"
# This tells us:
# - Exchange Online is used (spf.protection.outlook.com)
# - Google Workspace may also be in use (_spf.google.com)
# - 203.0.113.0/24 is an on-prem or third-party mail server
# Recursively resolve all SPF includes to get full IP list
dig TXT spf.protection.outlook.com +short
dig TXT spfa.protection.outlook.com +short
dig TXT spfb.protection.outlook.com +short
# Check MX records for mail routing
dig MX targetcompany.com +short
# Example: 10 targetcompany-com.mail.protection.outlook.com.
# Points to Exchange Online Protection (EOP)
Azure IP Range Identification
If the target has Azure resources, you can identify their IP ranges by querying the published Azure IP ranges and cross-referencing with information gathered from DNS, headers, and other sources.
# Download Microsoft's published IP ranges for Azure/M365
# The URL changes weekly; get the latest from the download page
curl -s "https://www.microsoft.com/en-us/download/details.aspx?id=56519"
# Alternatively, use the service tag discovery API
curl -s "https://download.microsoft.com/download/7/1/D/71D86715-5596-4529-9B13-DA13A5DE5B63/ServiceTags_Public_20250106.json" | \
jq '.values[] | select(.name == "AzureCloud") | .properties.addressPrefixes[:10]'
# Search Shodan for the target's Azure-hosted assets
shodan search "ssl.cert.subject.cn:targetcompany.com"
shodan search "org:\"Target Company\" cloud:azure"
# Check if the target has Azure Front Door, App Gateway, etc.
dig A targetcompany.com +short
# If it resolves to Azure IP ranges, they have Azure-hosted web resources
# Check for Azure Blob Storage
curl -s -o /dev/null -w "%{http_code}" "https://targetcompany.blob.core.windows.net"
Exchange Online IP & Header Analysis
# If you can send an email to the target and receive a bounce or auto-reply,
# examine the email headers for routing information
# Headers to look for:
# X-MS-Exchange-Organization-AuthSource: reveals internal Exchange server names
# X-OriginatorOrg: targetcompany.com (confirms the tenant domain)
# X-MS-Exchange-CrossTenant-Id: a1b2c3d4-... (reveals tenant GUID)
# Received: from ... by ... with Microsoft SMTP Server
# X-Microsoft-Antispam: reveals EOP/Defender configuration
# Resolve the Exchange Online Protection (EOP) endpoint
dig A targetcompany-com.mail.protection.outlook.com +short
mailchimp.com, sendgrid.net, or salesforce.com reveal which SaaS platforms the target uses, providing additional attack surface.
-all (hard fail) rather than ~all (soft fail) to prevent email spoofing. Implement DMARC with a p=reject policy and DKIM signing. Audit your SPF record regularly to remove deprecated IP ranges and third-party includes that are no longer in use. Consider using Azure Private Link and Private Endpoints to reduce the number of publicly exposed Azure resources.
Azure AD B2B collaboration allows organisations to invite external users as guests. If the target tenant has permissive federation or guest access settings, attackers can leverage this to gain an initial foothold, enumerate internal resources, or pivot from a compromised partner tenant.
Checking Federation Configuration
Determine whether the target tenant uses federated authentication (ADFS, Okta, PingFederate, etc.) and identify the federation endpoint. This is important because federated environments may have different security controls than cloud-managed environments.
# Check the user realm to determine federation status
curl -s "https://login.microsoftonline.com/getuserrealm.srf?login=user@targetcompany.com" | jq '.'
# Example output for a FEDERATED domain:
# {
# "NameSpaceType": "Federated",
# "DomainName": "targetcompany.com",
# "FederationBrandName": "Target Company",
# "AuthURL": "https://adfs.targetcompany.com/adfs/ls/?..."
# "CloudInstanceName": "microsoftonline.com"
# }
# Example output for a MANAGED (cloud-only) domain:
# {
# "NameSpaceType": "Managed",
# "DomainName": "targetcompany.com",
# "CloudInstanceName": "microsoftonline.com",
# "FederationBrandName": "Target Company"
# }
ADFS Endpoint Discovery
If the domain is federated, the ADFS server is an additional attack surface. Common ADFS endpoints can be probed to confirm availability and gather configuration details.
# If AuthURL reveals an ADFS server, probe its endpoints
ADFS="adfs.targetcompany.com"
# ADFS metadata endpoint (public by default)
curl -s "https://${ADFS}/adfs/.well-known/openid-configuration" | jq '.'
# ADFS federation metadata (reveals signing certificates)
curl -s "https://${ADFS}/federationmetadata/2007-06/federationmetadata.xml"
# Common ADFS authentication endpoints
curl -s -o /dev/null -w "%{http_code}" "https://${ADFS}/adfs/ls/idpinitiatedsignon.aspx"
# 200 = ADFS sign-in page is accessible (can be used for password spraying)
# Check for ADFS Extranet Lockout settings
# High lockout thresholds or disabled lockout = easier password spraying
Guest Access & B2B Collaboration Testing
Test whether the target tenant allows external users to be invited as guests. This can be checked by attempting to look up external user properties through the Teams API or by probing the invitation endpoint.
# AADInternals - Check tenant's external collaboration settings
# Get the OpenID configuration which reveals tenant info
Get-AADIntTenantDomains -Domain "targetcompany.com"
# Check if Teams external access is open
# If you have a valid token from ANY M365 tenant, you can test:
Invoke-RestMethod -Uri "https://teams.microsoft.com/api/mt/emea/beta/users/user@targetcompany.com/externalsearchv3" `
-Headers @{Authorization = "Bearer $token"}
# If external access is allowed, this returns user details
# including display name, presence, and profile picture
Teams External Access Enumeration
# TeamFiltration can enumerate users via Teams external access
# First, acquire a Teams token from your own tenant
TeamFiltration --enum --userlist userlist.txt
# If external access is enabled on the target tenant, this returns:
# - User display names
# - User presence status (Available, Busy, etc.)
# - User profile photos
# - Whether the user has Teams enabled
# This works because Teams external access allows
# cross-tenant user lookup by default in many tenants
Cross-Tenant Trust Assessment
# Check if the target has Azure AD cross-tenant access policies
# by testing the cross-tenant synchronization endpoint
curl -s "https://login.microsoftonline.com/targetcompany.com/v2.0/.well-known/openid-configuration" | \
jq '.tenant_region_scope, .cloud_instance_name'
# Check Azure AD federation metadata for trust relationships
curl -s "https://login.microsoftonline.com/targetcompany.com/federationmetadata/2007-06/federationmetadata.xml"
# The metadata reveals:
# - Token signing certificates
# - Supported claim types
# - Federation endpoints
# - Tenant region and cloud instance
Get-MgUser -Filter "userType eq 'Guest'" and remove stale guests. (6) If you run ADFS, restrict extranet access and enable Extranet Smart Lockout.