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:

  DelegateManagement.zip (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:

  Remove-TextMessagingConfiguration.zip (3.1 KiB)


Script to set retention tag on default folder items updated to v1.1.1

Articles in the "Retention tag on default folder items" series

  1. Use EWS to apply retention policy to items in a default folder
  2. Script to set retention tag on default folder items updated to v1.1.1 [This article]
  3. Default folder retention tag script updated to 1.3
  4. Updated script that applies retention tag to items in a default folder

When running v1.0 of the script in a folder with lots of items, it would keep stopping with no errors, but there were more items to process. I found that this was happening when the number of items to process changes because a new item was added to the folder. In other words, while processing deleted items and another item is added to the Deleted Items folder, the total number of items changes, resulting in Exchange not returning the next set of items correctly. v1.1 correctly accounts for this condition.

Additionally, I found that calendar items in the Deleted Items folder cannot be processed with the API. Trying to change any property returns an error that it can’t update calendar items that are already deleted. But since you can manually assign a tag to it in Outlook, I consider it a bug that you can’t update calendar items in the Deleted Items folder. So, I updated the search filter to exclude calendar items when not searching in the Calendar folder (processing meeting responses is okay; it is only appointment/meeting items that are affected).

You can now choose the default folder to process. If you don’t specify one, the Deleted Items folder is selected. I included all default folders that can have a retention policy tag assigned AND have a well-known folder ID. This means that you can’t use the script to process items in the Clutter or RSS Feeds folders. If you are interested in having the script work against those folders, let me know and I will add the code necessary to do so.

Download the updated version below. (The inline code of the first post has been updated, too.)

  Set-DefaultFolderItemsTag.ps1 (9.4 KiB)

Use EWS to apply retention policy to items in a default folder

Articles in the "Retention tag on default folder items" series

  1. Use EWS to apply retention policy to items in a default folder [This article]
  2. Script to set retention tag on default folder items updated to v1.1.1
  3. Default folder retention tag script updated to 1.3
  4. Updated script that applies retention tag to items in a default folder

When working with retention policies and the types of tags you can apply to folders and items, you can assign a personal tag to any item and to any custom folder. You cannot, however, assign a personal tag to a default folder (such as Deleted Items), even if a retention policy tag has not been assigned to the folder. This means that if no default policy tag has been assigned to the policy, the items in that folder will never expire. The only way for a user to expire items in that folder is to assign a personal tag to each and every item. For the deleted items folder, that can be a lot of items, and its contents are changing daily.

This script uses the EWS Managed API to get all items in the Deleted Items folder that do not have a tag assigned to them and then assign a specific tag to each. To start, you need to connect to EWS:

I am using a function because I lifted this code from another one of my scripts, so calling this function returns an object for the Deleted Items folder. Based on the credentials format and EWS URL, you can see that I am connecting to Exchange Online. This can be easily changed to support on-premises. I am not using autodiscover because it is very slow when querying EXO, and since all EXO mailboxes can be accessed with the a single FQDN, it is simpler this way. I am not using impersonation because I am running this against my own mailbox, but you can uncomment the line if you choose to use it.

To search for items that do not have a tag assigned, this is used:

The MAPI property that indicates whether a tag has been assigned (whether implicitly or explicitly) is an extended property that you declare and add to a property set. The property is binary and contains the GUID of the tag, but since I am only looking for items without a tag, I only care if the property has a value. To do this, you first define a search filter object that says to include items where the property exists. Then to negate that, so I can find items without that property, you create another search filter object using the Not class that contains the other search filter.

Then you can apply the tag and its corresponding days until expiration value:

To assign the tag, you need to know its GUID. You can get this from PowerShell, but if you don’t have access to Exchange to get this, you can manually assign the tag to an item, then use MFCMAPI and look at the item’s properties for the value in PR_POLICY_TAG (0x30190102). The RAW representation of the GUID will need to be converted to the proper byte order, which can be done in a variety of ways, but this site is an easy way. When assigning the tag you also have to set the property that contains the number of days after which the tag is configured to expire. In my case, it is a 30-day tag. (I tested not setting the property and the result in Outlook does show that the tag is assigned but it doesn’t show the expiration date. I don’t know if the property is only used to calculate the displayed date or if MRM actually uses it when expiring items.)

The complete script can be downloaded from the link or copied from the code below. It includes checking for the EWS Managed API and a progress bar (since this is not a fast operation).

  Set-DefaultFolderItemsTag.ps1 (9.4 KiB)