EXT-08: Auditing Mailbox Delegation Permissions
Overview
Mailbox delegation allows users to access, send mail on behalf of, or send mail as another user. While legitimate for administrative assistants and shared mailboxes, improper delegation can lead to unauthorized access and data exfiltration. This guide helps you audit existing delegations, identify risky configurations, and establish governance controls.
Prerequisites
Required Roles
- Global Administrator - Full access to all settings
- Exchange Administrator - Can manage Exchange Online settings
- Security Administrator - Can view security configurations
- Compliance Administrator - Access to audit logs
Required Licenses
- Microsoft 365 E3/E5 or Business Premium
- Exchange Online (included in above)
Time Estimate
- Complete Audit: 45-60 minutes
- Per-User Review: 5-10 minutes
- Policy Configuration: 30 minutes
Step-by-Step Instructions
Step 1: Understand Delegation Types
Exchange Online has several delegation mechanisms:
| Delegation Type | Description | Risk Level |
|---|---|---|
| Full Access | Complete access to mailbox contents | High |
| Send As | Send email appearing as the mailbox owner | High |
| Send on Behalf | Send email showing both sender and on behalf of | Medium |
| Folder Permissions | Access to specific folders only | Low-Medium |
| Calendar Delegates | Manage calendar on behalf of user | Low |
| Mailbox Automapping | Automatic Outlook access | Low |
Step 2: Audit Full Access Permissions
Full Access allows complete mailbox access:
# Connect to Exchange Online
Connect-ExchangeOnline
# Get all mailboxes with full access delegations
$mailboxes = Get-Mailbox -ResultSize Unlimited
$fullAccessReport = @()
foreach ($mbx in $mailboxes) {
$permissions = Get-MailboxPermission -Identity $mbx.UserPrincipalName |
Where-Object {
$_.User -notlike "NT AUTHORITY\*" -and
$_.User -notlike "S-1-5-*" -and
$_.User -ne "SELF" -and
$_.IsInherited -eq $false
}
foreach ($perm in $permissions) {
$fullAccessReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
MailboxDisplayName = $mbx.DisplayName
Delegate = $perm.User
AccessRights = ($perm.AccessRights -join ", ")
IsExternal = $perm.User -like "*#EXT#*"
MailboxType = $mbx.RecipientTypeDetails
}
}
}
Write-Host "Found $($fullAccessReport.Count) full access delegations"
$fullAccessReport | Format-Table Mailbox, Delegate, AccessRights -AutoSize
$fullAccessReport | Export-Csv -Path "FullAccessDelegations.csv" -NoTypeInformation
Step 3: Audit Send As Permissions
Send As allows impersonating the mailbox owner:
Connect-ExchangeOnline
$mailboxes = Get-Mailbox -ResultSize Unlimited
$sendAsReport = @()
foreach ($mbx in $mailboxes) {
$sendAsPerms = Get-RecipientPermission -Identity $mbx.UserPrincipalName |
Where-Object {
$_.Trustee -ne "NT AUTHORITY\SELF" -and
$_.AccessRights -contains "SendAs"
}
foreach ($perm in $sendAsPerms) {
$sendAsReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
MailboxDisplayName = $mbx.DisplayName
Delegate = $perm.Trustee
IsExternal = $perm.Trustee -like "*#EXT#*"
MailboxType = $mbx.RecipientTypeDetails
}
}
}
Write-Host "Found $($sendAsReport.Count) Send As delegations"
$sendAsReport | Format-Table Mailbox, Delegate -AutoSize
$sendAsReport | Export-Csv -Path "SendAsDelegations.csv" -NoTypeInformation
Step 4: Audit Send on Behalf Permissions
Send on Behalf shows the delegate's identity:
Connect-ExchangeOnline
$mailboxes = Get-Mailbox -ResultSize Unlimited
$sendOnBehalfReport = @()
foreach ($mbx in $mailboxes) {
if ($mbx.GrantSendOnBehalfTo) {
foreach ($delegate in $mbx.GrantSendOnBehalfTo) {
$sendOnBehalfReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
MailboxDisplayName = $mbx.DisplayName
Delegate = $delegate
MailboxType = $mbx.RecipientTypeDetails
}
}
}
}
Write-Host "Found $($sendOnBehalfReport.Count) Send on Behalf delegations"
$sendOnBehalfReport | Format-Table -AutoSize
$sendOnBehalfReport | Export-Csv -Path "SendOnBehalfDelegations.csv" -NoTypeInformation
Step 5: Audit Calendar Delegates
Calendar delegates have access to schedule meetings:
Connect-ExchangeOnline
$mailboxes = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox
$calendarReport = @()
foreach ($mbx in $mailboxes) {
try {
$calendarPerms = Get-MailboxFolderPermission -Identity "$($mbx.UserPrincipalName):\Calendar" -ErrorAction SilentlyContinue |
Where-Object {
$_.User -notlike "Default" -and
$_.User -notlike "Anonymous" -and
$_.AccessRights -ne "None"
}
foreach ($perm in $calendarPerms) {
$calendarReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
Delegate = $perm.User
AccessRights = ($perm.AccessRights -join ", ")
SharingPermissionFlags = $perm.SharingPermissionFlags
}
}
}
catch {
Write-Warning "Could not access calendar for $($mbx.UserPrincipalName)"
}
}
Write-Host "Found $($calendarReport.Count) calendar delegations"
$calendarReport | Format-Table -AutoSize
$calendarReport | Export-Csv -Path "CalendarDelegations.csv" -NoTypeInformation
Step 6: Comprehensive Delegation Report
Create a combined report for all delegation types:
Connect-ExchangeOnline
$mailboxes = Get-Mailbox -ResultSize Unlimited
$comprehensiveReport = @()
foreach ($mbx in $mailboxes) {
# Full Access
$fullAccess = Get-MailboxPermission -Identity $mbx.UserPrincipalName |
Where-Object { $_.User -notlike "NT AUTHORITY\*" -and $_.User -ne "SELF" -and $_.IsInherited -eq $false }
foreach ($perm in $fullAccess) {
$comprehensiveReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
Delegate = $perm.User
DelegationType = "Full Access"
AccessRights = ($perm.AccessRights -join ", ")
RiskLevel = "High"
}
}
# Send As
$sendAs = Get-RecipientPermission -Identity $mbx.UserPrincipalName |
Where-Object { $_.Trustee -ne "NT AUTHORITY\SELF" -and $_.AccessRights -contains "SendAs" }
foreach ($perm in $sendAs) {
$comprehensiveReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
Delegate = $perm.Trustee
DelegationType = "Send As"
AccessRights = "SendAs"
RiskLevel = "High"
}
}
# Send on Behalf
if ($mbx.GrantSendOnBehalfTo) {
foreach ($delegate in $mbx.GrantSendOnBehalfTo) {
$comprehensiveReport += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
Delegate = $delegate
DelegationType = "Send on Behalf"
AccessRights = "SendOnBehalf"
RiskLevel = "Medium"
}
}
}
}
# Summary
Write-Host "`nDelegation Summary:"
Write-Host "==================="
$comprehensiveReport | Group-Object DelegationType | Format-Table Name, Count
# Export
$comprehensiveReport | Export-Csv -Path "ComprehensiveDelegationReport.csv" -NoTypeInformation
Step 7: Identify Risky Delegations
Flag delegations that require immediate review:
Connect-ExchangeOnline
$riskyCriteria = @{
# Executives shouldn't have unexpected delegates
ExecutiveMailboxes = @("ceo@company.com", "cfo@company.com", "cto@company.com")
# External delegates are risky
ExternalDelegates = $true
# Departed users still having access
DisabledDelegates = $true
}
# Find risky delegations
$allDelegations = # ... (from comprehensive report above)
$riskyDelegations = $allDelegations | Where-Object {
# Check if external
$_.Delegate -like "*#EXT#*" -or
# Check if to executive mailbox
$_.Mailbox -in $riskyCriteria.ExecutiveMailboxes
}
Write-Host "RISKY DELEGATIONS REQUIRING REVIEW:" -ForegroundColor Red
$riskyDelegations | Format-Table -AutoSize
Step 8: Remove Unauthorized Delegations
Remove Full Access
Connect-ExchangeOnline
$mailbox = "user@company.com"
$delegate = "unauthorized@company.com"
Remove-MailboxPermission -Identity $mailbox -User $delegate -AccessRights FullAccess -Confirm:$false
Write-Host "Full Access removed from $mailbox for $delegate"
Remove Send As
Connect-ExchangeOnline
$mailbox = "user@company.com"
$delegate = "unauthorized@company.com"
Remove-RecipientPermission -Identity $mailbox -Trustee $delegate -AccessRights SendAs -Confirm:$false
Write-Host "Send As removed from $mailbox for $delegate"
Remove Send on Behalf
Connect-ExchangeOnline
$mailbox = "user@company.com"
$delegate = "unauthorized@company.com"
# Get current delegates
$mbx = Get-Mailbox -Identity $mailbox
$currentDelegates = $mbx.GrantSendOnBehalfTo
# Remove specific delegate
$newDelegates = $currentDelegates | Where-Object { $_.Name -ne $delegate }
Set-Mailbox -Identity $mailbox -GrantSendOnBehalfTo $newDelegates
Write-Host "Send on Behalf removed from $mailbox for $delegate"
Step 9: Configure Delegation Alerts
Monitor for new delegation assignments:
Via Audit Log Search
Connect-ExchangeOnline
$startDate = (Get-Date).AddDays(-7)
$endDate = Get-Date
# Search for delegation changes
$auditLogs = Search-UnifiedAuditLog -StartDate $startDate -EndDate $endDate `
-Operations "Add-MailboxPermission", "Add-RecipientPermission", "Set-Mailbox" `
-ResultSize 1000
$delegationChanges = $auditLogs | ForEach-Object {
$auditData = $_.AuditData | ConvertFrom-Json
[PSCustomObject]@{
Date = $_.CreationDate
Operation = $_.Operations
User = $_.UserIds
Target = $auditData.ObjectId
Parameters = ($auditData.Parameters | ConvertTo-Json -Compress)
}
}
$delegationChanges | Format-Table -AutoSize
Create Alert Policy
- Navigate to Microsoft Defender portal → Policies & rules
- Go to Alert policies
- Create new alert:
- Name: "Mailbox Delegation Added"
- Category: Permissions
- Activity: Add-MailboxPermission, Add-RecipientPermission
- Severity: Medium
- Recipients: Security team
Step 10: Establish Governance Controls
Delegation Request Process
Document a formal process:
- Request Submission: User submits IT ticket
- Manager Approval: Manager approves business need
- Security Review: Security validates request
- Implementation: IT configures delegation
- Documentation: Record in asset management
- Review Date: Set expiration or review date
Regular Review Schedule
| Mailbox Type | Review Frequency | Reviewer |
|---|---|---|
| Executive | Monthly | IT Security |
| Shared Mailbox | Quarterly | Mailbox Owner |
| User Mailbox | Semi-annually | Manager |
| Service Account | Annually | Service Owner |
Automated Review Reminder
# Schedule monthly
Connect-ExchangeOnline
$executiveMailboxes = @("ceo@company.com", "cfo@company.com")
foreach ($exec in $executiveMailboxes) {
$delegations = Get-MailboxPermission -Identity $exec |
Where-Object { $_.User -notlike "NT AUTHORITY\*" -and $_.User -ne "SELF" }
if ($delegations) {
$body = "The following delegations exist for $exec:`n`n"
$body += ($delegations | Format-Table User, AccessRights | Out-String)
$body += "`nPlease confirm these are still required."
Send-MailMessage -To "security@company.com" -Subject "Monthly Delegation Review: $exec" -Body $body
}
}
Step 11: Document Delegation Policy
Mailbox Delegation Policy
=========================
1. SCOPE
Applies to all Exchange Online mailboxes.
2. DELEGATION TYPES
- Full Access: Requires Security approval
- Send As: Requires Manager + Security approval
- Send on Behalf: Requires Manager approval
- Calendar Delegate: Requires mailbox owner approval
3. RESTRICTIONS
- No external (guest) delegates for user mailboxes
- Executive mailboxes require executive approval
- Maximum delegation period: 1 year (requires renewal)
4. MONITORING
- All delegations logged in audit
- Weekly review of new delegations
- Monthly executive mailbox review
5. REMOVAL
- Immediate removal upon employment termination
- Automatic removal after expiration date
- Annual review for all standing delegations
Verification Checklist
After completing the delegation audit:
- Full Access permissions audited
- Send As permissions audited
- Send on Behalf permissions audited
- Calendar delegations audited
- Comprehensive report generated and exported
- Risky delegations identified and flagged
- Unauthorized delegations removed
- Alert policies configured
- Audit log monitoring enabled
- Governance process documented
- Review schedule established
- Stakeholders notified of findings
Troubleshooting
Issue: Cannot see all delegations
Cause: May not have sufficient permissions.
Solution:
- Ensure you have Exchange Administrator role
- For specific mailboxes, you may need Organization Management
- Some delegations require Get-MailboxFolderPermission for folder-level access
Issue: Delegation persists after removal
Cause: Caching or synchronization delay.
Solution:
- Wait 15-30 minutes for changes to propagate
- User may need to restart Outlook
- Remove automapping if Full Access was removed:
Remove-MailboxPermission -Identity "mailbox" -User "delegate" -AccessRights FullAccess -Confirm:$false Add-MailboxPermission -Identity "mailbox" -User "delegate" -AccessRights FullAccess -AutoMapping $false Remove-MailboxPermission -Identity "mailbox" -User "delegate" -AccessRights FullAccess -Confirm:$false
Issue: Inherited permissions cannot be removed
Cause: Permissions may come from group membership.
Solution:
- Identify the source group:
Get-MailboxPermission -Identity "mailbox" | Where-Object { $_.IsInherited -eq $true } - Remove user from the group instead of direct removal
- Or add explicit deny permission if necessary
Issue: Cannot identify who added the delegation
Cause: Audit log may have expired or not captured the event.
Solution:
- Check unified audit log:
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) ` -Operations "Add-MailboxPermission" -ObjectIds "mailbox@company.com" - If outside retention, check backup audit logs
- Implement longer audit retention for future
Issue: Delegation needed for service account
Cause: Applications may require mailbox access.
Solution:
- Document the business requirement
- Use service principal with Graph API instead if possible
- If delegation required, document and add to exception list
- Use application access policies for scope limitation
Issue: Calendar delegation shows unexpected permissions
Cause: Outlook may auto-grant permissions when scheduling.
Solution:
- Review Outlook's automatic processing settings
- Check for calendar publishing settings
- Review permission levels (Editor vs. Author vs. Reviewer)
Related Resources
- Manage mailbox permissions
- Give mailbox permissions to another user
- Calendar delegation in Outlook
- Search the audit log
Last updated: January 2025