Ever needed to find Server Side Outlook rules that forward mail outside of your organisation?

Hi All

I was recently asked by a customer to check through Outlook Mailbox rules for rules that forward mail to external recipients. The customer was worried that company email was being auto-forwarded to external email addresses and hence wanted to get some information about how many mailboxes fell into that category.

I thought this sounded like a good challenge and something that should be able to be done with Powershell. The key cmdlet to use to find Server Side Outlook rules is the Get-InboxRule cmdlet.

Checking a Single Mailbox

Before we use cmdlets to interrogate a mailbox let's have a quick look at a Outlook rules for my test user Anna M. Anna's mailbox has two inbox rules, one that forwards to an external email address and one rule that forwards to an internal recipient.

Figure 1 - Forward to External

Figure 2 - Forward to internal recipient

When checking a particular mailbox for forwarding rules you can use the command (in Exchange Management Shell):

Get-Mailbox AnnaM | Get-InboxRule | select identity, isvalid, @{Name="Forward To";Expression={[string]::join(";",($_.ForwardTo))}} | ft

In the command I check Anna's mailbox with Get-Mailbox, the results of the cmdlet is then passed to the Get-InboxRule cmdlet. The results of the Get-Inboxrules cmdlet is forwarded to the Select (or Select-Object) cmdlet in order to bring back the Identity, IsValid and the ForwardTo properties.

The ForwardTo field is a multi value attribute and so I have used the string join function to join each of the ForwardTo email addresses into a single string.

As you can see from the results brought back from the above command, Anna has two inbox rules, the rule I am interested in is the rule with a Forward to that does not match my Exchange organisation. So what I am after is a rule whose ForwardTo does not match "EX:/ ".

Checking Mailboxes for your organisation

Now when looking to get each of the ForwardTo attributes for Inbox Rules for your whole organisation the logical next step is to use the Get-Mailbox cmdlet to bring back all mailboxes in your environment, rather than a single mailbox. You can then pass the list of mailboxes to the Get-InboxRule cmdlet.

Get-Mailbox -ResultSize unlimited | Get-InboxRule -ErrorAction:SilentlyContinue | Where {$_.ForwardTo -ne $null -and $_.ForwardTo -notmatch "EX:/"} | Select identity, isvalid, @{Name="Forward To";Expression={[string]::join(";",($_.ForwardTo))}} | ConvertTo-CSV | out-file d:\scripts\results.csv

As you can see the command to search all mailboxes is pretty similar to the individual mailbox command. The main differences are:
- The " -ResultsSize unlimited" flag. This flag is used to check all mailboxes. By default running the Get-Mailbox cmdlet will return the first 1000 mailboxes, so if your organisation has more than 1000 mailboxes not using the -ResultSize flag will mean your result set is truncated to 1000.
- The "Where {$_.ForwardTo -ne $null -and $_.ForwardTo -notmatch "EX:/"} . This option I am using to filter for those rules that include the ForwardTo attribute and to exclude those rules that are being forwarded to other mailboxes within the organisation.
- The "ConvertTo-CSV". This option is used to convert the results of the Select-Object cmdlet to CSV format so I can save it to file.
- The "Out-File d:\scripts\results.csv" option. Here I am saving the CSV data passed from ConvertTo-CSV and saving to the results.csv file.
- The "-ErrorAction:SilentlyContinue" option in the Get-InboxRule cmdlet. I am using this option as when a mailbox does not have any associated inbox rules I get a lovely error like the one below. The ErrorAction:SilentlyContinue option will suppress these errors.

The operation couldn't be performed because 'contoso.org/Staff/Otto Moore\' couldn't be found.
+ CategoryInfo : NotSpecified: (:) [Get-InboxRule], ManagementObjectNotFoundException
+ FullyQualifiedErrorId : 99CC88FB,Microsoft.Exchange.Management.RecipientTasks.GetInboxRule

I was at this point expecting to have solved the problem, but alas no. The customer I was working at had a large number of mailboxes (>45,000) and so running the Get-Mailbox -ResultSize unlimited | Get-InboxRule has quite a large impact on the system running the cmdlet. As you can imagine checking that many mailboxes is going to take a large amount of memory. So as a result the cmdlet did not complete as planned.

So in order to complete the same task I needed to tackle the task in a slightly different way. I decided to script pretty much the same commands but by checking each mailbox's inbox rules individually, meaning I was going to have to break the two large transactions down into one large command (get-mailbox) and many smaller commands (get-inboxrule).

Rather than going into the line by line logic behind my script the high level logic of the script is as follows:

  • Get all mailboxes
    • For Each Mailbox check if the mailbox has a server side rule that includes a populated ForwardTo attribute;
    • If an associated rule is found log it to the output file (using an append)

The script I wrote is attached at the bottom of this blog post. To use it the usage of the script is as follows (run this in Exchange Management Shell):

Check all mailboxes in the organisation - inbox rules found will be in the inbox.results.csv file

.\InboxRules.ps1 -action inbox -LogFile inbox.results.log -OutputFile inbox.results.csv -Debug $true -Unlimited $True

Check the first 100 mailboxes in the organisation

.\InboxRules.ps1 -action inbox -LogFile inbox.results.log -OutputFile inbox.results.csv -Debug $true -TotalCount 100

NOTE: In my script I haven't excluded those inbox rules that are being forwarded to internal mailboxes, so if you want to exclude those you'll need to update the line in the script that has the "Where {$_.ForwardTo -ne $null}" option.

So if you have been asked to do something similar to this please feel free to use my script. I know this is a no-brainer but please please please test your scripts in a development or test environment before executing in production.

Happy Scripting


PS Lync rocks, and so does the new Microsoft Surface!