Exchange-Online/Get-MailboxPermissionReport.ps1

<#
.SYNOPSIS
    Audits mailbox permissions across Exchange Online.
.DESCRIPTION
    Retrieves Full Access, Send As, and Send on Behalf permissions for Exchange
    Online mailboxes. Essential for security reviews, onboarding/offboarding audits,
    and compliance reporting. Excludes system accounts (NT AUTHORITY, S-1-5-*) by default.
 
    Requires ExchangeOnlineManagement module and an active EXO connection.
.PARAMETER Identity
    One or more mailbox identities (UPN or alias) to audit. If not specified,
    all user mailboxes are audited.
.PARAMETER PermissionType
    Which permission types to include: FullAccess, SendAs, SendOnBehalf, or All.
    Defaults to All.
.PARAMETER OutputPath
    Optional path to export results as CSV. If not specified, results are returned
    to the pipeline.
.EXAMPLE
    PS> . .\Common\Connect-Service.ps1
    PS> Connect-Service -Service ExchangeOnline
    PS> .\Exchange-Online\Get-MailboxPermissionReport.ps1
 
    Audits all permission types on all user mailboxes.
.EXAMPLE
    PS> .\Exchange-Online\Get-MailboxPermissionReport.ps1 -Identity 'jsmith@contoso.com' -PermissionType FullAccess
 
    Checks only Full Access permissions on a specific mailbox.
.EXAMPLE
    PS> .\Exchange-Online\Get-MailboxPermissionReport.ps1 -OutputPath '.\mailbox-permissions.csv'
 
    Exports a full mailbox permission audit to CSV.
#>

[CmdletBinding()]
param(
    [Parameter()]
    [string[]]$Identity,

    [Parameter()]
    [ValidateSet('All', 'FullAccess', 'SendAs', 'SendOnBehalf')]
    [string]$PermissionType = 'All',

    [Parameter()]
    [string]$OutputPath
)

$ErrorActionPreference = 'Stop'

# Verify EXO connection
try {
    $null = Get-OrganizationConfig -ErrorAction Stop
}
catch {
    Write-Error "Not connected to Exchange Online. Run Connect-Service -Service ExchangeOnline first."
    return
}

# Get target mailboxes
if ($Identity) {
    $mailboxes = foreach ($id in $Identity) {
        try {
            Get-EXOMailbox -Identity $id -Properties DisplayName, PrimarySmtpAddress, GrantSendOnBehalfTo
        }
        catch {
            Write-Warning "Mailbox not found: $id"
        }
    }
}
else {
    Write-Verbose "Retrieving all user mailboxes..."
    $mailboxes = Get-EXOMailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox -Properties DisplayName, PrimarySmtpAddress, GrantSendOnBehalfTo
}

$mailboxes = @($mailboxes)
Write-Verbose "Processing $($mailboxes.Count) mailboxes..."

$results = [System.Collections.Generic.List[PSCustomObject]]::new()
$counter = 0

foreach ($mbx in $mailboxes) {
    $counter++
    Write-Verbose "[$counter/$($mailboxes.Count)] $($mbx.PrimarySmtpAddress)"

    # Full Access permissions
    if ($PermissionType -in 'All', 'FullAccess') {
        try {
            $fullAccessPerms = Get-MailboxPermission -Identity $mbx.PrimarySmtpAddress |
                Where-Object {
                    $_.User -notlike 'NT AUTHORITY\*' -and
                    $_.User -notlike 'S-1-5-*' -and
                    $_.IsInherited -eq $false -and
                    $_.AccessRights -contains 'FullAccess'
                }

            foreach ($perm in $fullAccessPerms) {
                $results.Add([PSCustomObject]@{
                    Mailbox        = $mbx.DisplayName
                    MailboxAddress  = $mbx.PrimarySmtpAddress
                    PermissionType = 'FullAccess'
                    GrantedTo      = $perm.User
                    Inherited      = $perm.IsInherited
                })
            }
        }
        catch {
            Write-Warning "Failed to get FullAccess permissions for $($mbx.PrimarySmtpAddress): $_"
        }
    }

    # Send As permissions
    if ($PermissionType -in 'All', 'SendAs') {
        try {
            $sendAsPerms = Get-RecipientPermission -Identity $mbx.PrimarySmtpAddress |
                Where-Object {
                    $_.Trustee -notlike 'NT AUTHORITY\*' -and
                    $_.Trustee -notlike 'S-1-5-*'
                }

            foreach ($perm in $sendAsPerms) {
                $results.Add([PSCustomObject]@{
                    Mailbox        = $mbx.DisplayName
                    MailboxAddress  = $mbx.PrimarySmtpAddress
                    PermissionType = 'SendAs'
                    GrantedTo      = $perm.Trustee
                    Inherited      = $false
                })
            }
        }
        catch {
            Write-Warning "Failed to get SendAs permissions for $($mbx.PrimarySmtpAddress): $_"
        }
    }

    # Send on Behalf permissions
    if ($PermissionType -in 'All', 'SendOnBehalf') {
        if ($mbx.GrantSendOnBehalfTo.Count -gt 0) {
            foreach ($delegate in $mbx.GrantSendOnBehalfTo) {
                $results.Add([PSCustomObject]@{
                    Mailbox        = $mbx.DisplayName
                    MailboxAddress  = $mbx.PrimarySmtpAddress
                    PermissionType = 'SendOnBehalf'
                    GrantedTo      = $delegate
                    Inherited      = $false
                })
            }
        }
    }
}

Write-Verbose "Found $($results.Count) permission entries across $($mailboxes.Count) mailboxes"

if ($OutputPath) {
    $results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
    Write-Output "Exported $($results.Count) permission entries to $OutputPath"
}
else {
    Write-Output $results
}