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 TypeDescriptionRisk Level
Full AccessComplete access to mailbox contentsHigh
Send AsSend email appearing as the mailbox ownerHigh
Send on BehalfSend email showing both sender and on behalf ofMedium
Folder PermissionsAccess to specific folders onlyLow-Medium
Calendar DelegatesManage calendar on behalf of userLow
Mailbox AutomappingAutomatic Outlook accessLow

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

  1. Navigate to Microsoft Defender portalPolicies & rules
  2. Go to Alert policies
  3. 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:

  1. Request Submission: User submits IT ticket
  2. Manager Approval: Manager approves business need
  3. Security Review: Security validates request
  4. Implementation: IT configures delegation
  5. Documentation: Record in asset management
  6. Review Date: Set expiration or review date

Regular Review Schedule

Mailbox TypeReview FrequencyReviewer
ExecutiveMonthlyIT Security
Shared MailboxQuarterlyMailbox Owner
User MailboxSemi-annuallyManager
Service AccountAnnuallyService 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:

  1. Ensure you have Exchange Administrator role
  2. For specific mailboxes, you may need Organization Management
  3. Some delegations require Get-MailboxFolderPermission for folder-level access

Issue: Delegation persists after removal

Cause: Caching or synchronization delay.

Solution:

  1. Wait 15-30 minutes for changes to propagate
  2. User may need to restart Outlook
  3. 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:

  1. Identify the source group:
    Get-MailboxPermission -Identity "mailbox" | Where-Object { $_.IsInherited -eq $true }
    
  2. Remove user from the group instead of direct removal
  3. 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:

  1. Check unified audit log:
    Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) `
        -Operations "Add-MailboxPermission" -ObjectIds "mailbox@company.com"
    
  2. If outside retention, check backup audit logs
  3. Implement longer audit retention for future

Issue: Delegation needed for service account

Cause: Applications may require mailbox access.

Solution:

  1. Document the business requirement
  2. Use service principal with Graph API instead if possible
  3. If delegation required, document and add to exception list
  4. Use application access policies for scope limitation

Issue: Calendar delegation shows unexpected permissions

Cause: Outlook may auto-grant permissions when scheduling.

Solution:

  1. Review Outlook's automatic processing settings
  2. Check for calendar publishing settings
  3. Review permission levels (Editor vs. Author vs. Reviewer)

Related Resources


Last updated: January 2025