Delegate management module updated to 1.4.6

A small update has been made so that comparing delegates to users with Full Access and Send As works for Exchange Online accounts. The code to translate a user ID to a SID returns an error when used against Exchange Online. If the connection mode is for Exchange Online, it will now use SMTP address to match a delegate to a user with Full Access and Send As permissions. Download the updated module here: (9.2 KiB)

Default folder retention tag script updated to 1.3

I spent some time figuring out why calendar items in the Deleted Items folder that would be immediately expired could not be updated with a tag. I found that Exchange wants to send an update to the organizer, regardless whether the calendar item is even a meeting or one that is in the past. If the item is in a different folder when the tag is applied, the error is that there is no recipient on the message, whereas the error when in the Deleted Items is that it can’t update an item that is already deleted. The latter error being misleading is why I originally couldn’t figure it out what they meant or why it was happening.

The solution is to use a second argument in the Update method to tell Exchange not to send an update message for invitations or cancellations. You have to do this even if it isn’t a meeting. Presumably, this is a bug where Exchange is running through logic against all IPM.Appointment-class items, rather than skipping those that aren’t meetings.

The script has been updated to account for this, plus some minor updates since the last version posted.

  Set-DefaultFolderItemsTag.ps1 (9.4 KiB)

Search mailboxes for large items that may impede migrations to Exchange Online

I have a customer that will be enabling hybrid mode soon and moving mailboxes to Exchange Online. One part of the project entails finding mailboxes that will not have a successful migration because they contain items over 150 MB. I referred them to the script on the TechNet Gallery that does exactly that. When that script was run against an admin’s mailbox, it took 10 minutes to complete. Extrapolating that single mailbox’s time for all 80,000 mailboxes (555 days) is far from accurate, but it does indicate that the process would likely take far longer than they have available to complete that part. (The extrapolation also doesn’t factor running multiple threads of the script.) So I looked at the script to see how it is doing what it does. It enumerates every folder, searches every folder for every item in it, then looks at the size of each item so it can report how many total items there are in each folder and how many are over the size limit.

So they could get results in far shorter time, I wrote a script that uses the EWS Managed API and leverages the hidden AllItems search folder created by Outlook 2010+ when it connects to a mailbox. Since it isn’t a well-known folder name, you have to find the folder first. The following code searches the root of the mailbox for a search folder (folder property type is 2) whose display name is AllItems:

What if the folder doesn’t exist? If Outlook 2010+ for Windows hasn’t been used against the mailbox, the folder won’t exist. If this is the case, the folder needs to be created. To determine the search restriction used for the folder when created by Outlook, I used MFCMAPI and saw that there is only one: the item has the message class property populated. To create the same search folder with EWS:

When you create a search folder you need to specify the folders to search and whether subfolders are included, the name of the folder, the search parameters (restrictions), and where to put it. In this case, the folder to search is the well-known folder MsgFolderRoot, which is the visible root folder in the IPM subtree, and subfolders are included by specifying a deep traversal. (This means the recoverable items folders are not included. If you want to include them, you can add to RootFolderIds with the well-known folder for Recoverable Items.) The search parameter is that the ItemClass property exists. (This translates to PR_MESSAGE_CLASS when viewed with MFCMAPI.) The folder is then saved in the root of the mailbox. The folder search can be run again to get the newly created folder.

To get a count of any items that are over 150 MB, do a search against that search folder using a query string. This type of search leverages the content index and is faster than using a search filter with a restriction. This search returns the count of any items over 150 MB, where $maxSize is an integer representing the limit in MB:

Putting this all together, the script takes an email address (or mailboxes or email addresses from the pipeline), looks for the search folder, creates it if missing and looks for it again, searches for any items in the search folder over a given size, and outputs an object with the email address, the number of items found, and any errors. Running it in the customer’s environment went from 10 minutes per mailbox to 14 mailboxes per minute. You can pipeline the output to CSV to use a source with the large item script from TechNet to get more details of which folder has the oversize items, etc.

For performance, the autodiscover URL of the first mailbox in the pipeline will be cached and used for subsequent mailboxes. Or you can specify a URL to use instead. The default item limit is 150 MB, but you can specify any size you want. There is a switch to use impersonation; otherwise, full mailbox access is needed. I found that you don’t need any permission to a mailbox in order to bind to the root folder, so if you then do a search for the search folder, you get the same result when there isn’t a folder or when you don’t have permission. Therefore, the script checks the account’s permission to the root folder (which is contained in the bind response). Depending on what you want to do with the output, such as feed it to the TechNet script, you can choose to not include mailboxes with 0 large items in the output with the appropriate switch. Lastly, since creating a search folder and waiting for it to initially be populated can take a little time, when a mailbox needs the search folder created and you want to know that it is doing so, use the Verbose switch to see that in the console.

You can download the script via the link below or expand and copy code:

  Get-MailboxLargeItemCount.ps1 (8.3 KiB)

Remove text messaging settings on behalf of users

1/24/17 Update:  The script has been updated to v1.3, fixing support for mailboxes on Exchange 2010.  There are also some minor fixes, such as correctly using autodiscover when neither an EWS URL nor the Exchange Online switch is used.

4/18/16 Edit: I was reviewing this script for an unrelated reason when I discovered that I had used incorrect construction in the begin block since you cannot access parameter values in it.  I have updated the script in the download and the inline code at the end of the post that any any code that references parameters has been moved to the process block.

Exchange has the ability to send text messages to specific carriers in a few countries, and is enabled by default. This allows users to configure calendar notifications (such as changes to meetings that are occurring in the next three days) and rules to forward email as a text message. Users have to use OWA (or if you prefer the new name, Outlook on the web) to configure this. But what if your users do this before you realize it is enabled by default and now you want to disable it?

If you modify the role assignment policy to remove MyTextMessaging or modify OWA Mailbox policy to remove Text Messaging, it hides this feature from users, but it doesn’t disable anything already in place. You then decide to use PowerShell to run Clear-TextMessagingAccount for someone, but it says the user cannot be read. You can run it for your own account, but nobody else, even as an admin. This is because the write scope of the role that contains the cmdlet is Self. So how to remove the settings for another user?

I wrote a script that uses the EWS Managed API modify the hidden messages that contain the settings and delete any inbox rules that are forwarding to a mobile device. I should point out that doing it this way is unsupported, but I have used it successfully for mailboxes on Exchange 2013 and in Exchange Online.

The calendar notification settings and text messaging configuration are stored in folder associated items (FAI) in the root folder of the mailbox, in the roaming XML property of a user configuration message. Because of this, you can use the Microsoft.Exchange.WebServices.Data.UserConfiguration class to easily get messages with a specific subclass and retrieve this property without having to define a property set with the extended MAPI property. The subclass for the calendar notification settings is CalendarNotification.001 and text messaging configuration is TextMessaging.001. If you already have a service object created, you can get the message for calendar notification with these two lines:

The roaming properties of a user configuration message are stored in the Dictionary, XmlData, and BinaryData properties of the search result object. The property for the calendar notification settings (PR_ROAMING_XMLSTREAM as the XmlData property) is a binary value returned as a byte array, so it needs to be converted to a string cast as an XML object so it can be manipulated with XML methods:

The three notification types have their own node and contains an element whose value indicates whether it is enabled. Since I don’t care what the other options are, only that they are disabled, this can be done by directly setting the value for the element:

To write the data back to the XmlData property and save it in the mailbox, it needs to be converted back to a byte array. This isn’t done with a one-liner like converting from a byte array. The XML data is converted to a string, which is then converted to a byte array. There could be a more efficient way of doing this, but I don’t know it at the time of this writing. The first line is the one-liner to take the XML data and store it as a byte array in the property, the second saves the message back to the mailbox, and the two functions that convert XML to a string and a string to a byte array follow:

For the text messaging configuration, it is in the same property of its message. Once converted to XML, devices are stored in the MachineToPersonMessagingPolicies node, with a PossibleRecipient node for each device that has ever been configured. To simply delete any devices, you can remove all sub-nodes since there aren’t any others:

Then convert the XML data back to a byte array and save the message the same as before.

What remains are any inbox rules that may have been created that forward to a text messaging device. As an admin, you can use PowerShell to get rules, but you won’t see any rules that have been disabled in Outlook. Even if a rule is visible because it is enabled or has been disabled via OWA, and so you are able to see if a given rule is forwarding to a text messaging device, if you delete the rule, you will also delete any rules that are currently disabled via Outlook. What’s worse, you won’t even know if there are disabled rules that will be deleted because the warning is presented for every mailbox regardless of the existence of any applicable rules.

So the script will get all FAI messages that are rules and delete any that are forwarding to a device configured via the text messaging feature. The first step is to get the rules by searching for all FAIs in the inbox whose class is that of a rule:

After getting the rules, we need to retrieve the property that contains a rule’s actions, which is PR_EXTENDED_RULE_ACTIONS (0x0E990102) when on Exchange 2013+ or 0x65EF0102 when on Exchange 2010, a binary property:

Parsing the binary data is not easy (for me) because it includes pieces of variable-length information. If the entire value is converted to a string, however, an action that forwards to a configured text messaging device contains the string MOBILE: followed by the E.164-formatted phone number. So, all that needs to be done is to get the rule’s actions, convert it to string, check for MOBILE, and delete the rule:

The script supports on-premises and Exchange Online, autodiscover or specified URL, pipelining mailboxes into it, impersonation and specifying credentials. The output will contain what actions it took on a mailbox, including whether any of the features were not configured in the first place. You can run it multiple times against a mailbox without it having an issue that any or all features are not configured. The full script can be expanded below, and it can also be downloaded via the following link: (3.1 KiB)

PowerShell module for managing Exchange mailbox delegates

I have converted my script that displays a mailbox’s delegates into a module that adds the ability to add, modify, and remove a mailbox’s delegates. The benefit of the module is that it negates the need to create an Outlook profile for the owner/manager in order to manipulate his or her delegates. The module requires any version of the EWS Managed API and the Exchange cmdlets via remoting, as detailed below.

Upon importing the module, it will check for any version of the EWS Managed API, starting with version 2.0 and working its way back. This allows for the freedom to have any version installed instead of requiring everyone who uses it to ensure they have the specific one I used when writing it. After the API is loaded, it will check for the Exchange cmdlets. If not loaded into the shell, it will retrieve a list of Exchange servers from Active Directory and attempt to connect to one until successful. If neither the API nor remoting to Exchange is successful, the module will fail to load, telling you why. (The module doesn’t distinguish between the cmdlets being available because they were locally loaded from the snap-in or from remoting. However, since certain cmdlets will fail when not executed remotely because they bypass RBAC, you need to be sure that you have not locally loaded the snap-in.)

Access to a mailbox is done using full mailbox access. If you want to use impersonation, you will want to uncomment line 96. Granting impersonation rights is explained here. The URL used for EWS is determined by autodiscover of the owner/manager mailbox.

These are the cmdlets the module exposes:


The alias for Get-MailboxDelegate is gmd.  It is basically the same as my Get-Delegates.ps1 script, but it has gotten a makeover to support pipelining into and out of. The -Identity parameter (aliased to -Owner and -Manager) is any standard Exchange identity (display name, email address, alias, dn, etc.) and it supports pipelining by property name. If the objects you are pipelining into Get-MailboxDelegate don’t have a property name of Identity, then you will need to use a ForEach loop and use $_.PropertyName to designate which property should be used.

Without any other parameters, all delegates will be retrieved. If you want to get only a specific delegate, you can use the -Delegate parameter. The default output will be to list the properties, but since it is now a collection of objects, you can choose to output it to a table, to a grid view, or export to a file, using the appropriate cmdlets. You can also use these output cmdlets to select only the properties you want. For example, if you only care about the private items property you could use ft owner,delegate,viewprivate. Or, if you only want those who actually can view private items, you could run something like this:

Note that I encapsulated in parentheses the command that I pipeline into Get-MailboxDelegate. This is necessary to avoid the concurrent pipeline limitation in remote PowerShell. It is only necessary if the command prior to the pipeline will be running a cmdlet that leverages remoting. Another option is to store the results of the prior command in a variable and then pipeline that into Get-MailboxDelegate.

All of the module’s cmdlets have built-in help, so you can use PowerShell’s help cmdlet to learn the details of all of them, such as the parameters and their descriptions, usage examples, etc.


The alias for Add-MailboxDelegate is amd. To use this cmdlet, provide an owner and a delegate. You can optionally specify folder permission for Inbox, Calendar, Tasks, Contacts, Sent Items, and Deleted Items; if private items are viewable; if meeting requests are to be received; and the owner’s global handling of meeting requests. I didn’t include the option of setting permission for Journal or Notes because, well, who uses them? The ability to set the permission for Sent Items and Deleted Items is to accommodate those who use GPO to have Outlook store messages sent from another user in that person’s Sent Items folder, and likewise for messages deleted from another mailbox. The option to set the meeting request delivery scope applies to the owner, not the delegate being added, so it is only necessary to include it if you are adding a delegate and you want to change the current setting.


The alias for Set-MailboxDelegate is smd. Use this cmdlet to change any settings for an existing delegate (or to change the meeting request delivery scope for the owner). Provide the owner and the existing delegate to modify and, optionally, which setting you want to change. All other settings will remain as is. If you want to change just the meeting request delivery scope for the owner, specify any existing delegate, but not any other settings (except the delivery scope). Unlike the valid roles for folder permission with Add-MailboxDelegate, you can use None if you want to remove a folder permission. If you want to remove the ability to view private items or to not receive meeting requests, use -ViewPrivateItems:$false or -ReceiveMeetingRequests:$false, respectively. The colon is necessary because both parameters are switches, so their inclusion alone in the command means true, whereas to explicitly set them to false means using the syntax above. (The cmdlet checks if either switch is actually present in the command, so don’t be concerned that not including a switch implies that it should be false.)


The alias for Remove-MailboxDelegate is rmd. Provide the owner and the delegate to remove. That’s it.

All of the cmdlets perform the expected error checking: the owner is a valid mailbox; any delegate to add, modify, or remove is a valid mailbox; adding a delegate when the delegate already exists; modifying or removing a delegate that is not an existing delegate; using a valid (albeit the limited subset exposed in the API) role for a folder permission; and using a valid meeting request delivery scope.

Download the module or view/copy the code below: (9.2 KiB)

Configure SCOM to alert when an Exchange database fails over

Using Microsoft System Center Operations Manager 2007 R2 (SCOM 2007) to monitor Exchange 2010 is a job unto itself.  It takes a lot of time and knowledge of SCOM to effectively monitor your Exchange environment.  This is due to SCOM’s architecture, but compounded by the Exchange 2010 correlation engine used to generate alerts (which MS has done away with in Exchange 2013).  Anyway, one of the things SCOM doesn’t do is alert you in any way when a database has failed over to another node in a DAG.  I can only speculate why this is the case, but I assume it has to do with a failover, assuming it works successfully, is a non-service-impacting event and that you will presumably have other monitoring in place via SCOM or a different tool to alert you to the root cause of the event.

This doesn’t preclude the fact that I want to know when a database has failed over to another node.  To create an alert for this in Exchange 2010/2013/2016, you are limited to what Exchange puts in the event log in the first place.  Things related to databases and DAGs are in the Crimson logs under Microsoft-Exchange-HighAvailability.  The log to monitor for *over events is Operational.  Event ID 306 is what is logged on the Primary Active Manager (PAM) server when a database is moved to another node.  If you look at the description of the log, you see that it doesn’t contain anything more than the database name, the source and target servers, and any move comment:


Event 306 Description


The event description doesn’t distinguish between switchovers and failovers, so you might think there isn’t a way to be alerted only when a failover occurs.  Exchange logs, however, much more data than what is shown in the description.  Click on the Details tab and within the Friendly view, you will see what Exchange actually logs about the event:


Event 306 Full Details

Event with list of all parameters


You can see that Exchange does, in fact, log why the *over happening.  It notes whether it is a failover (ActionInitiator is Automatic) or switchover (ActionInitiator is Admin) and why the move is happening (SystemShutdown, NodeDown, FailureItem, or Cmdlet).  There are other properties that may be helpful for event log collection and/or alerting, too.

To have SCOM generate an alert for an event that indicates a database failover is starting, an alert rule needs to be created.  In the SCOM console, go to the Authoring navigation pane, right-click on Rules (under Management Pack Objects) and select Create a new rule…  Under Alert Generating Rules, then Event Based, select NT Event Log (Alert).  Assuming you have created a custom management pack for Exchange customizations, select that in the drop-down at the bottom of the window, then click Next.

Enter a rule name and an optional description.  Change the Rule Category to be Alert.  In the Rule target, click the Select button, then type in Mailbox, select it in the results pane, then click OK.  Setting the target to mailbox means that the alert will automatically apply to all mailbox servers, watching the appropriate event log on those servers, while not watching other servers where the event will never happen anyway.  When done, the screen should look similar to this:


The General tab when creating a new alert rule.


Click Next to proceed to the Event Log Type tab.  In the Log name field, enter Microsoft-Exchange-HighAvailability/Operational.  If you have access from the console to an Exchange server with the mailbox role installed, you can browse to it by clicking the ellipsis and changing to the server name, but you can manually enter the name.  If entering it manually, however, it is important to enter it exactly as listed above.  Then click Next.

In the Build Event Expression tab, enter 306 for the value of the first field (Event ID).  The event source can be confusing, depending on where you are looking at it.  For example, if using the native event viewer, the source will be displayed only as HighAvailability.  The source, however, is Microsoft-Exchange-HighAvailability, which can been seen in the Provider Name under System of the Friendly View of the event’s details.  So, enter this value for the Event Source parameter.

Since the alert should only fire for a database failover, not a switchover, you need to filter the event on another value.  To add this, click the Insert drop-down and select Expression.  Click the ellipsis next to the empty parameter name to bring up the Event Property dialog.  If you look at the common event properties drop-down, nothing in there looks like it will work, so how do you restrict the alert to a value in one of the fields listed in the Details tab?  This is where event log parameters come in.

The description of an event is built from a canned string in the event’s message file, often interspersed with one or more variables seen in the Details tab, known as parameters.  Each event defines its own parameters, any of which can be used in the description.  The fact that the database is failing over is noted in the parameter named ActionInitiator.  Parameter names, however, are not used when being referenced; instead, the index number of the parameter in the UserData/EventXML collection.  To determine which parameter number to use for ActionInitiator, use the Friendly View and count down the list of parameter names.  Note that parameters are stored in a 1-based array, so start with 1 (not 0).  ActionInitiator, therefore is in parameter 6.

Back in the Event Property dialog, click the radio button to specify a parameter to use and change the default value of 1 to 6, then click OK.  The Parameter Name will now say Parameter 6.  Set the Operator to Equals, and manually enter a Value of Automatic.  The Build Event Expression tab should look like this:


The event parameters to use to detect a database failover.


On the Configure Alerts tab you can customize the how alert should manifest: priority, severity, the description, etc.  Using the default event description in the alert description is probably fine, but you can opt to include one or more of the event parameters in the description.  For example, you could have the alert include the ActionReason (parameter 7) so you know why the failover is occurring.  Because this is an alert from a rule, as opposed to a monitor, it requires manual closure, which is a good thing in this case since the point is to be notified when a database fails over so you can investigate as needed.