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)

Delegate management module updated to v1.4.5

The module has been updated mostly for fixing issues when working with Exchange Online. The first version that supported it didn’t account account for object properties that are different compared to on-premises, as well as how to get user information. These are the changes in this version:

  • Fixed when using a default connection mode of EXO so that the rest of the module knows it.
  • Added option to not use autodiscover when using EXO since those lookups can sometimes add a lot of time to the cmdlets running.  If default mode is EXO and you don’t want to use autodiscover, uncomment that line below the default mode.  If using EXO on-demand, you can set the option with the DoNotUseAutodiscover switch parameter of Set-DelegateManagementMode. (The cmdlet’s help has been updated to reflect this.)
  • Added usage of the Azure Active Directory module when using EXO mode.  This means you need to have the WAAD module installed to work against Exchange Online.  Since that module is 64-bit only, you can only run the delegate management module in a 64-bit PowerShell session.
  • Fixed (hopefully and finally) the Write-Progress prompt that some people were getting that interrupted the cmdlets.  (Thanks, Jim.)
  • Fixed getting Send As permission in EXO due to it using a different cmdlet.
  • Fixed getting folder permissions in EXO due to the object properties being different.
  • Added removal of Deleted Items and Sent Items folder permissions when removing a delegate.

There are other things I have discovered need fixing: Exchange cmdlets loaded by the module are not accessible outside of the module; Exchange cmdlet errors are not caught in PowerShell 4 so the module cmdlets keep running after a terminating error would be detected if running in PowerShell 2; if multiple objects are pipelined to Get-MailboxDelegate and one of them does not have a mailbox, the cmdlet terminates without processing the remaining objects in the pipeline.

I also still intend to add support for hybrid mode.  It is more complicated, though, such as with adding delegates since I need to account for attempts at cross-premises delegation, which isn’t supported. (9.2 KiB)

Delegate management module updated to support Exchange Online

The module for managing Exchange mailbox delegates has been updated with support for Exchange Online. In its current version (v1.4) you can use one mode or the other. The default mode is on-premises, but you can change this on demand to use Exchange Online by using Set-DelegateMananagementMode, a new cmdlet added in this version. If you change it on demand, you will be prompted for your Office 365 credentials. If you will be exclusively working with Exchange Online, you can change the line near the top of the module to default to using that method. In that case, you will be prompted for credentials the first time you run a cmdlet.

It is my intention to update the module to support a hybrid environment, but I first need to set up one in my lab in order to test it.

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)