Back to blog

Office 365 Permissions Inventory: Mailbox Forwarding

May 19, 2017 by Vasil Michev

Continuing our “Permissions inventory” series, this article will demonstrate how to get a report on all the mailboxes in the company that have some form of forwarding configured. As usual, we will cover the basic cmdlets you can use to gather this information, discuss some tips and provide you with a script that generates a sample report.

To get started, let’s examine the different ways forwarding can be configured in Exchange. Users can setup forwarding on their own by opening OWA, navigating to the Settings page, selecting Mail, then Accounts and then Forwarding, or directly by accessing it through this link. Setting an address here populates the ForwardingSmtpAddress attribute for the mailbox, and one can also control whether a copy of the message should be preserved in the user mailbox. Both end users and admins have access to these settings.

Admins have another way to configure forwarding, by populating the ForwardingAddress attribute for a mailbox. In the GUI, this action is performed by accessing the Exchange Admin Center, navigating to the Recipients tab and double-clicking on the mailbox in question. Next, click on the Mailbox features tab on the left and clicking the View details link under Mail flow. This option is a bit more restrictive, as it only allows forwarding to recipients recognized by Exchange. In addition, the presence of two similar settings has created some confusion, and recently Microsoft has announced plans to consolidate them, and encourage the use of ForwardingSmtpAddress only, going forward.

To cover those two forwarding settings, you can use the Get-Mailbox cmdlet. What’s even better, is that those two properties are filterable, and to reduce the script run time, you can utilize the following one-liner to get all mailboxes that have at least one of these settings configured:

Get-Mailbox -Filter {ForwardingSmtpAddress -ne $null -or ForwardingAddress -ne $null} -ResultSize Unlimited | select Name,*forward*

Simple stuff, right? Unfortunately, this does not cover all the methods available for forwarding or redirecting messages to a different recipient. We could also set up Inbox rules, and use the Get-InboxRule cmdlet to report on these rules by using:

Get-InboxRule -Mailbox | ? {$_.ForwardTo -ne $null -or $_.ForwardAsAttachmentTo -ne $null -or $_.RedirectTo -ne $null} | Select Name,ForwardTo,ForwardAsAttachmentTo,RedirectTo

That’s only for a single mailbox though, if we are to cover all such rules, we would need to cycle over every mailbox in the company, as there is no filterable property we can use this time. This in turn means that covering Inbox rules in our script will substantially increase the run time. For this reason, we have added an optional parameter in the script, namely CheckInboxRules, which you can invoke only when necessary (more on this later).

Apart from Inbox rules, a form of forwarding can be configured by using the Delegate functionality: the option to forward Calendar requests to delegates. To configure it, the user opens Outlook, selects File, then presses the Account Settings button and selects Delegate Access. On the Delegate Access dialog, you can add or remove delegates, as well as control the forwarding settings for meeting requests and responses. In order for these options to become available, the “Delegate receives copies of meeting-related messages sent to me” checkbox must be selected.

To check for the presence of this setting via PowerShell, you can use the Get-CalendarProcessing cmdlet. The ResourceDelegates attribute indicates which delegates this setting is configured for. Exchange Online no longer allows this attribute to be configured via PowerShell for User mailboxes, but viewing it is still possible. As with the Inbox rules however, in order to get this information across the company, you will have to loop each individual mailbox. Here’s an example of how to do this:

Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | % {Get-CalendarProcessing $_.PrimarySmtpAddress | select Identity,ResourceDelegates }

Finally, a form of forwarding can also be configured by some admin-only functionalities such as Transport rules, Journaling, Supervisory review, and more. While it’s possible to cover all these in a single script, the intention here is to give you a good starting point, so we will just include Transport rules as an example:

Get-TransportRule | ? {$_.RedirectMessageTo -ne $null -or $_.BlindCopyTo -ne $null -or $_.AddToRecipients -ne $null -or $_.CopyTo -ne $null -or $_.AddManagerAsRecipientType -ne $null}

So, after covering the basic building blocks, let’s turn to the actual script. As with the previous sample scripts, a set of parameters is made available to cover the different mailbox types: User, Shared, Room and Equipment, Discovery, and Team mailboxes. If you don’t specify any of these, the full list of mailboxes in the company will be returned.

Similarly, parameters are introduced for the different types of forwarding required. Those include Inbox rules (via the -CheckInboxRules parameter), Calendar delegation (via -CheckCalendarDelegates) and Transport rules (via -CheckTransportRules). For the “regular” mailbox forwarding parameters, server-side filtering is possible, and data is always returned. You can of course customize the script to make any of these mandatory or optional.

Different handling is applied in terms of gathering mailbox inventory depending on the parameters specified when invoking the script. Because both the ForwardingSmtpAddress and the ForwardingAddress attributes support server-side filtering, we can directly return all the mailboxes that have them configured. On the other hand, checking Inbox rules or Calendar delegation requires cycling over each mailbox, as explained above. Therefore, if the -CheckCalendarDelegates or the -CheckInboxRules switches are used, we can use Invoke-Command to gather the list of mailboxes and only a very small subset of their attributes, similarly to what we did in the Mailbox permissions inventory scenario.

The script will output the results to the console host by default, and will also store them in the global $varForwarding variable. You can specify a different variable name via the -OutVariable parameter, or simply reuse the existing one:

You can find the full script on the TechNet Gallery:
Note that the intention of the script is to get you started, to show where to look, and surface some basic information. The script doesn’t provide a full-blown solution to discover and manage all forms of forwarding. We’ve tried to use the same formatting for the output of all types of forwarding, allowing you to get information about the object you should look at, as well as details of the recipients which the messages will be forwarded or redirected to. If you want to export the output to CSV, either use the $varForwarding variable, or simply remove the comment mark on the last line of the script.

As with previous script samples, the script does not handle connectivity to Exchange Online PowerShell. If an existing session is detected, the script will try to reuse it, otherwise it will return an error. You can of course incorporate it with your login script or modify it as you see fit.