All scripts
Governance 452
Get-MailboxForwardingRules
Scans every mailbox in the tenant for external forwarding, both the ForwardingSmtpAddress set on the mailbox itself and inbox rules that forward or redirect to an outside address. Useful for catching a compromised account or an old forwarding rule someone set up and forgot about.
Get-MailboxForwardingRules.ps1
<#
.SYNOPSIS
Reports every mailbox in the tenant that is forwarding mail outside the organization.
.DESCRIPTION
Checks two places mail can leave your tenant without anyone noticing: the
ForwardingSmtpAddress set on the mailbox itself, and inbox rules that forward
or redirect messages to an external address. Compromised accounts and old
"forward to my personal email" rules both show up here. Run it regularly,
not just once, since a new forwarding rule can appear at any time.
.AUTHOR
Shehryar Hassan
.EXAMPLE
Connect-ExchangeOnline
.\Get-MailboxForwardingRules.ps1 -CsvPath "C:\Reports\ExternalForwarding.csv"
Connects to Exchange Online first, then runs the report and saves the
results to a CSV file for review.
#>
[CmdletBinding()]
param(
[string]$CsvPath = ".\ExternalForwarding-Report.csv"
)
if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
Write-Error "The ExchangeOnlineManagement module is not installed. Run: Install-Module ExchangeOnlineManagement"
return
}
try {
Get-ConnectionInformation -ErrorAction Stop | Out-Null
}
catch {
Write-Error "Not connected to Exchange Online. Run Connect-ExchangeOnline first."
return
}
Write-Host "Pulling mailbox list, this can take a while on larger tenants..." -ForegroundColor Cyan
$mailboxes = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox
$findings = New-Object System.Collections.Generic.List[Object]
foreach ($mailbox in $mailboxes) {
# Mailbox level forwarding, set directly on the mailbox object.
if ($mailbox.ForwardingSmtpAddress -or $mailbox.ForwardingAddress) {
$target = if ($mailbox.ForwardingSmtpAddress) { $mailbox.ForwardingSmtpAddress } else { $mailbox.ForwardingAddress }
$findings.Add([PSCustomObject]@{
Mailbox = $mailbox.PrimarySmtpAddress
Source = "Mailbox forwarding"
ForwardsTo = $target
DeliverToBoth = $mailbox.DeliverToMailboxAndForward
RuleName = $null
})
}
# Inbox rules that forward or redirect to an outside address.
$rules = Get-InboxRule -Mailbox $mailbox.PrimarySmtpAddress -ErrorAction SilentlyContinue |
Where-Object { $_.ForwardTo -or $_.RedirectTo -or $_.ForwardAsAttachmentTo }
foreach ($rule in $rules) {
$targets = @($rule.ForwardTo) + @($rule.RedirectTo) + @($rule.ForwardAsAttachmentTo) |
Where-Object { $_ } |
ForEach-Object { $_.ToString() }
$externalTargets = $targets | Where-Object { $_ -notmatch [regex]::Escape(($mailbox.PrimarySmtpAddress -split "@")[1]) }
if ($externalTargets) {
$findings.Add([PSCustomObject]@{
Mailbox = $mailbox.PrimarySmtpAddress
Source = "Inbox rule"
ForwardsTo = ($externalTargets -join "; ")
DeliverToBoth = $rule.Enabled
RuleName = $rule.Name
})
}
}
}
if ($findings.Count -eq 0) {
Write-Host "No external forwarding found. Nothing to report." -ForegroundColor Green
return
}
$findings | Sort-Object Mailbox | Format-Table -AutoSize
$findings | Sort-Object Mailbox | Export-Csv -Path $CsvPath -NoTypeInformation
Write-Host ""
Write-Host "$($findings.Count) forwarding entries found. Saved to $CsvPath" -ForegroundColor Yellow
Write-Host "Check each one, some are legitimate (a shared mailbox forwarding to a ticketing system), but anything you do not recognize is worth a closer look." -ForegroundColor Yellow
Read it before you run it, and test in a safe tenant first.