Mailbox permissions are one of the most sensitive yet frequently overlooked areas in Exchange Online security governance. Excessive or unmanaged mailbox delegation can expose organizations to:
Permissions such as:
can provide extensive access to sensitive communications and business data if not regularly reviewed.
This PowerShell automation solution helps administrators:
The solution is ideal for:
Try the M365Corner Microsoft 365 Reporting Tool â your DIY pack with 20+ out-of-the-box M365 reports for Users, Groups, and Teams.
Mailbox delegation permissions are commonly assigned for:
However, over time these permissions can become:
Unreviewed mailbox permissions increase the risk of:
Regular mailbox permission audits help organizations maintain stronger Exchange Online governance and reduce mailbox exposure risks.
| Permission Type | Description |
|---|---|
| Full Access | Allows opening and reading mailbox contents |
| Send As | Allows sending emails as the mailbox identity |
| Send on Behalf | Allows sending emails on behalf of the mailbox owner |
Among these permissions:
The script treats delegates as higher risk if they cannot be verified as internal Microsoft 365 recipients.
This includes:
External or unresolved delegates should always be reviewed because they may indicate:
Install the required PowerShell modules:
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Install-Module Microsoft.Graph -Scope CurrentUser
Connect to Exchange Online:
Connect-ExchangeOnline
Connect to Microsoft Graph:
Connect-MgGraph -Scopes `
"Domain.Read.All",
"Mail.Send"
# Import required modules
Import-Module ExchangeOnlineManagement
Import-Module Microsoft.Graph
# Connect to Exchange Online
Connect-ExchangeOnline
# Verify EXO connection
$EXOConnection = Get-ConnectionInformation
if (-not $EXOConnection) {
Write-Host "Exchange Online connection failed. Please reconnect using Connect-ExchangeOnline." -ForegroundColor Red
return
}
# Connect to Microsoft Graph
Connect-MgGraph -Scopes `
"Domain.Read.All",
"Mail.Send"
# Report path
$ReportPath = "C:\Reports\MailboxPermissionSecurityAudit.csv"
# Email settings
$Sender = "admin@contoso.com"
$EmailRecipient = "securityteam@contoso.com"
# Internal verified domains
$AcceptedDomains = (
Get-MgDomain |
Where-Object {
$_.IsVerified -eq $true
}
).Id
$AuditReport = @()
# Get user and shared mailboxes
$Mailboxes = Get-EXOMailbox `
-ResultSize Unlimited `
-RecipientTypeDetails UserMailbox,SharedMailbox `
-Properties GrantSendOnBehalfTo
foreach ($Mailbox in $Mailboxes) {
Write-Host "Processing mailbox: $($Mailbox.UserPrincipalName)" -ForegroundColor Cyan
# Full Access permissions
$FullAccessPermissions = Get-EXOMailboxPermission `
-Identity $Mailbox.UserPrincipalName `
-ErrorAction SilentlyContinue |
Where-Object {
$_.AccessRights -contains "FullAccess" -and
$_.IsInherited -eq $false -and
$_.User -notlike "NT AUTHORITY\SELF"
}
foreach ($Permission in $FullAccessPermissions) {
$DelegateIdentity = $Permission.User.ToString()
$DelegateName = $DelegateIdentity
$DelegateAddress = "Unknown"
$DelegateType = "Unknown"
$DomainCategory = "Unknown"
try {
$ResolvedDelegate = Get-EXORecipient `
-Identity $DelegateIdentity `
-ErrorAction Stop
$DelegateName = $ResolvedDelegate.DisplayName
$DelegateType = $ResolvedDelegate.RecipientTypeDetails
if ($ResolvedDelegate.PrimarySmtpAddress) {
$DelegateAddress = $ResolvedDelegate.PrimarySmtpAddress.ToString()
$Domain = (
$DelegateAddress -split "@"
)[-1].ToLower()
if ($AcceptedDomains -contains $Domain) {
$DomainCategory = "Internal"
}
else {
$DomainCategory = "External or Unknown"
}
}
}
catch {
$DomainCategory = "Unresolved"
}
$RiskScore = 30
$Severity = "Medium"
$Recommendation = "Review Full Access permission"
if ($Mailbox.RecipientTypeDetails -eq "SharedMailbox") {
$RiskScore += 10
$Recommendation += "; validate shared mailbox access"
}
if ($DomainCategory -ne "Internal") {
$RiskScore += 30
$Severity = "High"
$Recommendation += "; investigate external or unresolved delegate"
}
$AuditReport += [PSCustomObject]@{
Mailbox = $Mailbox.UserPrincipalName
DisplayName = $Mailbox.DisplayName
MailboxType = $Mailbox.RecipientTypeDetails
PermissionType = "Full Access"
DelegateName = $DelegateName
DelegateAddress = $DelegateAddress
DelegateType = $DelegateType
DomainCategory = $DomainCategory
Severity = $Severity
RiskScore = $RiskScore
Recommendation = $Recommendation
}
}
# Send As permissions
$SendAsPermissions = Get-RecipientPermission `
-Identity $Mailbox.UserPrincipalName `
-ErrorAction SilentlyContinue |
Where-Object {
$_.AccessRights -contains "SendAs" -and
$_.Trustee -notlike "NT AUTHORITY\SELF"
}
foreach ($Permission in $SendAsPermissions) {
$DelegateIdentity = $Permission.Trustee.ToString()
$DelegateName = $DelegateIdentity
$DelegateAddress = "Unknown"
$DelegateType = "Unknown"
$DomainCategory = "Unknown"
try {
$ResolvedDelegate = Get-EXORecipient `
-Identity $DelegateIdentity `
-ErrorAction Stop
$DelegateName = $ResolvedDelegate.DisplayName
$DelegateType = $ResolvedDelegate.RecipientTypeDetails
if ($ResolvedDelegate.PrimarySmtpAddress) {
$DelegateAddress = $ResolvedDelegate.PrimarySmtpAddress.ToString()
$Domain = (
$DelegateAddress -split "@"
)[-1].ToLower()
if ($AcceptedDomains -contains $Domain) {
$DomainCategory = "Internal"
}
else {
$DomainCategory = "External or Unknown"
}
}
}
catch {
$DomainCategory = "Unresolved"
}
$RiskScore = 40
$Severity = "High"
$Recommendation = "Review Send As permission immediately"
if ($DomainCategory -ne "Internal") {
$RiskScore += 30
$Severity = "Critical"
$Recommendation += "; investigate external or unresolved Send As delegate"
}
$AuditReport += [PSCustomObject]@{
Mailbox = $Mailbox.UserPrincipalName
DisplayName = $Mailbox.DisplayName
MailboxType = $Mailbox.RecipientTypeDetails
PermissionType = "Send As"
DelegateName = $DelegateName
DelegateAddress = $DelegateAddress
DelegateType = $DelegateType
DomainCategory = $DomainCategory
Severity = $Severity
RiskScore = $RiskScore
Recommendation = $Recommendation
}
}
# Send on Behalf permissions
if ($Mailbox.GrantSendOnBehalfTo) {
foreach ($Delegate in $Mailbox.GrantSendOnBehalfTo) {
$DelegateIdentity = $Delegate.ToString()
$DelegateName = $DelegateIdentity
$DelegateAddress = "Unknown"
$DelegateType = "Unknown"
$DomainCategory = "Unknown"
try {
$ResolvedDelegate = Get-EXORecipient `
-Identity $DelegateIdentity `
-ErrorAction Stop
$DelegateName = $ResolvedDelegate.DisplayName
$DelegateType = $ResolvedDelegate.RecipientTypeDetails
if ($ResolvedDelegate.PrimarySmtpAddress) {
$DelegateAddress = $ResolvedDelegate.PrimarySmtpAddress.ToString()
$Domain = (
$DelegateAddress -split "@"
)[-1].ToLower()
if ($AcceptedDomains -contains $Domain) {
$DomainCategory = "Internal"
}
else {
$DomainCategory = "External or Unknown"
}
}
}
catch {
$DomainCategory = "Unresolved"
}
$RiskScore = 25
$Severity = "Medium"
$Recommendation = "Review Send on Behalf permission"
if ($DomainCategory -ne "Internal") {
$RiskScore += 25
$Severity = "High"
$Recommendation += "; investigate external or unresolved delegate"
}
$AuditReport += [PSCustomObject]@{
Mailbox = $Mailbox.UserPrincipalName
DisplayName = $Mailbox.DisplayName
MailboxType = $Mailbox.RecipientTypeDetails
PermissionType = "Send on Behalf"
DelegateName = $DelegateName
DelegateAddress = $DelegateAddress
DelegateType = $DelegateType
DomainCategory = $DomainCategory
Severity = $Severity
RiskScore = $RiskScore
Recommendation = $Recommendation
}
}
}
}
# Export report
$AuditReport | Export-Csv `
-Path $ReportPath `
-NoTypeInformation `
-Encoding UTF8
# Summary counts
$TotalFindings = $AuditReport.Count
$CriticalFindings = (
$AuditReport |
Where-Object {
$_.Severity -eq "Critical"
}
).Count
$HighFindings = (
$AuditReport |
Where-Object {
$_.Severity -eq "High"
}
).Count
$SendAsFindings = (
$AuditReport |
Where-Object {
$_.PermissionType -eq "Send As"
}
).Count
$ExternalDelegates = (
$AuditReport |
Where-Object {
$_.DomainCategory -ne "Internal"
}
).Count
# HTML preview
$HtmlPreview = (
$AuditReport |
Select-Object -First 10 |
ConvertTo-Html -Fragment
)
# Email body
$EmailBody = @"
<html>
<body>
<h2>Exchange Online Mailbox Permission Security Audit</h2>
<p>The automated mailbox permission security audit has completed successfully.</p>
<ul>
<li>Total Permission Findings: $TotalFindings</li>
<li>Critical Findings: $CriticalFindings</li>
<li>High Severity Findings: $HighFindings</li>
<li>Send As Permissions Found: $SendAsFindings</li>
<li>External or Unresolved Delegates: $ExternalDelegates</li>
</ul>
<p>Below is a preview of the first 10 permission findings:</p>
$HtmlPreview
</body>
</html>
"@
# Send report
$params = @{
message = @{
subject = "Exchange Online Mailbox Permission Security Audit"
body = @{
contentType = "HTML"
content = $EmailBody
}
toRecipients = @(
@{
emailAddress = @{
address = $EmailRecipient
}
}
)
attachments = @(
@{
"@odata.type" = "#microsoft.graph.fileAttachment"
name = "MailboxPermissionSecurityAudit.csv"
contentBytes = [System.Convert]::ToBase64String(
[System.IO.File]::ReadAllBytes($ReportPath)
)
}
)
}
saveToSentItems = "true"
}
Send-MgUserMail `
-UserId $Sender `
-BodyParameter $params
Write-Host "Mailbox permission security audit completed and emailed successfully." -ForegroundColor Green
The script imports:
and establishes authenticated sessions using:
Connect-ExchangeOnline
Connect-MgGraph
This allows the script to retrieve:
before the security audit begins.
The script verifies whether the Exchange Online session is active using:
Get-ConnectionInformation
If the connection fails, the script stops execution to prevent incomplete audit results.
The script retrieves verified Microsoft 365 domains using:
Get-MgDomain
These domains are treated as trusted internal domains and are later used to classify delegates as:
This improves mailbox permission governance visibility.
The script retrieves:
using: Get-EXOMailbox
The mailbox properties retrieved include:
required for the audit.
The script retrieves Full Access mailbox permissions using:
Get-EXOMailboxPermission
Only explicit non-inherited permissions are audited.
Full Access permissions allow delegates to:
making them important governance review targets.
The script retrieves Send As permissions using:
Get-RecipientPermission
Send As permissions allow delegates to send emails as the mailbox identity.
Because Send As permissions enable identity impersonation, they receive higher default risk scores and severity levels.
The script audits Send on Behalf delegates using:
GrantSendOnBehalfTo
Send on Behalf permissions are generally treated as lower risk because recipients can usually see the delegated relationship.
However, unresolved or external delegates still increase the risk score.
For every mailbox delegate, the script attempts to resolve:
using: Get-EXORecipient
Delegates are then classified as:
This helps administrators identify:
The script assigns risk scores based on:
Higher risk scores indicate:
| Permission Scenario | Base Score | Severity |
|---|---|---|
| Send on Behalf | 25 | Medium |
| Full Access | 30 | Medium |
| Full Access on Shared Mailbox | 40 | Medium |
| Send As | 40 | High |
| Full Access + External/Unresolved Delegate | 60+ | High |
| Send on Behalf + External/Unresolved Delegate | 50 | High |
| Send As + External/Unresolved Delegate | 70 | Critical |
The script increases severity levels when:
This helps prioritize mailbox permission investigations.
The script generates a mailbox permission governance report containing:
The report is exported using:
Export-Csv
This provides:
The script automatically:
The email includes:
This makes the solution ideal for:
Audit executive mailbox delegation permissions for excessive access.
Review shared mailbox exposure and delegation risks.
Identify suspicious or unresolved mailbox delegates.
Generate mailbox delegation governance reports for security and compliance teams.
You can automate this solution using:
This enables recurring mailbox permission governance reviews across Microsoft 365 environments.
| Error | Cause | Solution |
|---|---|---|
| Access Denied | The account lacks Exchange Online permissions. | Ensure the account has:
before running the script. |
| Get-EXOMailboxPermission is not recognized | Exchange Online PowerShell module is not installed or connected. | Reconnect using: Connect-ExchangeOnline |
| Insufficient privileges to complete the operation | Microsoft Graph permissions are missing. | Reconnect using: Connect-MgGraph -Scopes ` "Domain.Read.All", "Mail.Send" |
Mailbox delegation permissions are one of the most sensitive components of Exchange Online governance. Excessive or unresolved mailbox access can expose organizations to:
This PowerShell automation solution helps organizations:
By automating mailbox permission security audits, administrators can strengthen Exchange Online governance and maintain better visibility into sensitive mailbox delegation across Microsoft 365 environments.
© Created and Maintained by LEARNIT WELL SOLUTIONS. All Rights Reserved.