Sunday, 13 November 2011

Auditing Exchange Inbox Rules

As part of a security review I needed to find out if any Exchange mailboxes had inbox rules configured to forward mail to external addresses, and highlight any rules which forwarded all e-mail to an external address (unconditional forwards).

Fortunately for me, the Exchange 2010 shell includes the Get-InboxRule cmdlet, which can be used to enumerate the inbox rules in a mailbox.

This is the script I wrote to report on the forwarding rules across all mailboxes in the organisation. It scans the inbox rules of all user mailboxes, compiles a report, and e-mails it to an administrator. The e-mail lists any unconditional forwarding rules with external recipients in the body (if there are any) and has an attachment listing all conditional and unconditional external forwarding rules. The report is also saved in a text file in the working directory.

# Set the working directory and the log file name
[Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath
$Logfile = ".\ExternalRules.txt"

# Load the Exchange 2010 Snapin
If (-not (Get-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction:SilentlyContinue) ) {
 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010

# Create a progress bar
Write-Progress -Activity "Mailbox Rule Audit" -Status "Getting mailboxes"

# Get the mailboxes we want to audit
$Mailboxes = Get-Mailbox bl* -Filter {(RecipientTypeDetails -eq "UserMailbox" -or RecipientTypeDetails -eq "SharedMailbox")} -resultsize unlimited

# Declare arrays to store the output in
$ExternalForwardingRules = @()
$UnconditionalForwardingRules = @()

# Loop through the mailboxes
ForEach ($Mailbox in $Mailboxes) {
 # Counter for the progress bar
 $MailboxCounter ++
 # Update the progress bar
 Write-Progress -Activity "Mailbox Rule Audit" -Status "Auditing Mailboxes" -CurrentOperation $Mailbox.DisplayName -PercentComplete $($MailboxCounter/$Mailboxes.Count*100)
 # Get the mailbox rules
 $Rules = Get-InboxRule -Mailbox $Mailbox -WarningAction SilentlyContinue 
 # Loop through the mailbox rules
 ForEach ($Rule in $Rules) {
  # Set a flag so we can only report on the rules forwarding externally
  $ExternalForwardingRule = $False
  # Look at the ForwardTo property of each rule to see if it is a "forwarding" rule
  If ($Rule.ForwardTo) {
   # Loop through the ForwardTo property to find any external forwards
   ForEach ($Forward in $Rule.ForwardTo) {
    # Set the flag to true if this is an external forward
    If ($Forward.RoutingType -eq "SMTP") { $ExternalForwardingRule = $True }
   # If the flag is true append to the result set
   If ($ExternalForwardingRule) {
    $ExternalForwardingRules = $ExternalForwardingRules + $Rule
    # Check if the rule is unconditional - the easiest way is to look for a text pattern in the description
    If (-not $Rule.Description.ToString().Contains("If the message")) {
     # If the rule is unconditional append it to the second result set
     $UnconditionalForwardingRules = $UnconditionalForwardingRules + $Rule

# Delete the log file if it exists
If (Test-Path $Logfile) {
 Remove-Item $Logfile

# Write the report of all the external forwarding rules to the log file
If ($ExternalForwardingRules) {
 $ExternalForwardingRules | Format-List MailboxOwnerId, Identity, Name, Description | Out-File $Logfile

# Create a new SMTP Client object
$SmtpClient = New-Object System.Net.Mail.SmtpClient

# Set the SMTP server
$ = ""

# Insert the message body
If ($UnconditionalForwardingRules) {
 $Body = $Body + "Unconditional forwarding rules:"
 $Body = $Body + ($UnconditionalForwardingRules | Select-Object MailboxOwnerId, Name, Identity, Description | Format-List | Out-String)

If ($ExternalForwardingRules) {
 $Body = $Body + "A report of all external forwarding rules is attached."
} Else {
 $Body = $Body + "No external forwarding rules found."

# Create the message object
$Message = New-Object System.Net.Mail.MailMessage "", "", "Inbox Rule Audit - $(Get-Date -f f)", $Body

# Add the log file as an attachment
If (Test-Path $Logfile) {
 $Attachment = New-Object System.Net.Mail.Attachment($Logfile)

# Send the mail message

# Clean up the mail attachment to clear the lock on the log file

Download Script

You'll need the Exchange 2010 management tools installed to run this script. If you want to change the scope to limit (or expand) the mailboxes checked you can modify the Get-Mailbox command on line 14. For the e-mail to work you'll need to change the SMTP server on line 72 and the sender and recipient addresses on line 87.

The script can be configured to run as a scheduled task to create a regular report.

- Ben


  1. This is great! Thank you very much for sharing.

  2. small typo at line 14 " Get-Mailbox bl* " the 'bl' must be removed

  3. You may want to initialize the $MailboxCounter variable to 0 before the Foreach loop or the Write-progress command won't work correctly. I've copied the fragment below of how I changed this to work in case you run the script multiple times in a row.

    #Initialize $MailboxCounter
    $MailboxCounter = 0

    # Loop through the mailboxes
    ForEach ($Mailbox in $Mailboxes) {

  4. This. Rocks. Thank you kindly.

  5. We re not getting the log file it does not save anything to the location defined in the variable ?

    # Write the report of all the external forwarding rules to the log file
    If ($ExternalForwardingRules) {
    $ExternalForwardingRules | Format-List MailboxOwnerId, Identity, Name, Description | Out-File $Logfile