All scripts
Governance 777
Mailbox Inbox Rule Audit Report
Scans every Exchange Online mailbox for inbox rules that forward, redirect, or silently delete mail, the pattern most often left behind by a compromised account, and exports the results to CSV.
Get-MailboxInboxRuleReport.ps1
<#
.SYNOPSIS
Reports inbox rules across Exchange Online mailboxes, flagging rules that forward, redirect, or silently delete mail.
.DESCRIPTION
Connects to Exchange Online and loops through mailboxes, pulling every inbox rule set on each one. It flags rules that forward or redirect mail to an external domain, or that delete messages without moving them to a folder first, since these are the patterns most often left behind by a compromised account. Results are exported to a CSV you can review or hand to your security team.
.PARAMETER OutputPath
Path for the CSV report. Defaults to .\InboxRuleReport.csv in the current folder.
.PARAMETER ExternalDomains
Optional list of your own accepted domains, used to tell internal forwarding apart from external forwarding. If you skip this, every forwarding or redirect rule gets flagged so you can review it yourself.
.EXAMPLE
.\Get-MailboxInboxRuleReport.ps1 -OutputPath C:\Reports\InboxRules.csv -ExternalDomains "contoso.com"
Runs the report against every mailbox and writes the results to the given path, treating contoso.com as internal.
.AUTHOR
Shehryar Hassan
#>
[CmdletBinding()]
param(
[string]$OutputPath = ".\InboxRuleReport.csv",
[string[]]$ExternalDomains = @()
)
if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
Write-Error "The ExchangeOnlineManagement module is not installed. Run: Install-Module ExchangeOnlineManagement"
return
}
Import-Module ExchangeOnlineManagement
try {
Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop
}
catch {
Write-Error "Could not connect to Exchange Online: $($_.Exception.Message)"
return
}
$results = New-Object System.Collections.Generic.List[Object]
$mailboxes = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox
foreach ($mailbox in $mailboxes) {
$rules = Get-InboxRule -Mailbox $mailbox.PrimarySmtpAddress -ErrorAction SilentlyContinue
if (-not $rules) { continue }
foreach ($rule in $rules) {
$forwardTargets = @()
if ($rule.ForwardTo) { $forwardTargets += $rule.ForwardTo }
if ($rule.ForwardAsAttachmentTo) { $forwardTargets += $rule.ForwardAsAttachmentTo }
if ($rule.RedirectTo) { $forwardTargets += $rule.RedirectTo }
$isExternalForward = $false
foreach ($target in $forwardTargets) {
$targetText = $target.ToString()
$isKnownInternal = $false
foreach ($domain in $ExternalDomains) {
if ($targetText -like "*$domain*") { $isKnownInternal = $true }
}
if ($forwardTargets.Count -gt 0 -and -not $isKnownInternal) { $isExternalForward = $true }
}
$isSilentDelete = ($rule.DeleteMessage -eq $true)
if ($forwardTargets.Count -gt 0 -or $isSilentDelete) {
$results.Add([PSCustomObject]@{
Mailbox = $mailbox.PrimarySmtpAddress
RuleName = $rule.Name
Enabled = $rule.Enabled
ForwardsMail = ($forwardTargets.Count -gt 0)
ForwardTargets = ($forwardTargets -join "; ")
PossiblyExternal = $isExternalForward
DeletesSilently = $isSilentDelete
StopsProcessing = $rule.StopProcessingRules
})
}
}
}
if ($results.Count -eq 0) {
Write-Host "No forwarding, redirect, or silent delete rules found."
}
else {
$results | Sort-Object PossiblyExternal -Descending | Export-Csv -Path $OutputPath -NoTypeInformation
$flagged = ($results | Where-Object { $_.PossiblyExternal -eq $true }).Count
Write-Host "Found $($results.Count) rule(s) worth reviewing, $flagged possibly forwarding outside your domains."
Write-Host "Report saved to $OutputPath"
}
Disconnect-ExchangeOnline -Confirm:$false
Read it before you run it, and test in a safe tenant first.