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.

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

Database seeding completion estimate script updated

Articles in the "Database seeding estimation" series

  1. Estimate the time to complete seeding a mailbox database copy
  2. Database seeding completion estimate script updated [This article]

The script has been updated to include the average throughput from the sampling duration in the output. This will help gauge why the seeding completion time may vary between multiple executions of the script, such as during vs. outside of business hours. Additionally, some variable names have been updated (to better reflect their contents), and per scripting best practices, the help has an added example so that all parameters are used at least once among all the examples. The inline code has been updated, as has the download.

  Get-DatabaseSeedingCompletion.zip (1.5 KiB)

Estimate the time to complete seeding a mailbox database copy

Articles in the "Database seeding estimation" series

  1. Estimate the time to complete seeding a mailbox database copy [This article]
  2. Database seeding completion estimate script updated

Seeding a mailbox database copy, especially across a WAN, can take days to complete. The shell doesn’t give any indication of the time it will complete, though it does show a progress bar and the amount of data written. If you want to know when the seeding will actually finish, you can extrapolate this from the throughput of the seeding, dividing that into the amount of data remaining to be copied.

The throughput of a copy being seeded is available as a performance counter, as are the percentage complete and the amount written. Using this information I wrote a script to estimate the date and time the database seeding will complete. Because throughput can vary greatly from second to second, the default sample duration is 60 seconds. You can specify any number of seconds, choosing accuracy or impatience. Retrieving performance counters can take some time (not even counting any sampling you may do), so a progress indicator will indicate any time that is occurring. (Three separate queries for performance counters occur to provide the output.)

To run the script, provide a database copy and, optionally, a sample duration. The copy will be validated that it is being seeded, then the seeding throughput will be retrieved for 60 seconds or the duration you specified. Then the amount of data written so far is retrieved, comparing that to the total size of the database, so that the remaining amount can be determined. That amount is divided by the average throughput during the sample duration and calculating the time it will be done. Bear in mind that this calculation does not include the time to seed the content index or copy and replay transaction logs that have been generated since seeding began. The output will show the percentage of the database size that has been seeded thus far and the estimated date and time the seeding will complete.

You can copy the code below or download the script via the link.

  Get-DatabaseSeedingCompletion.zip (1.5 KiB)


Delegate management module updated to v1.3.5

The module has been updated so that the function that retrieves Send As permission will work in the Exchange Management Shell. I use implicit remoting rather than EMS, so I didn’t realize that the filter for Send-As needs to be different (though I don’t know why, which is aggravating). I previously also changed the default access method to use FMA instead of impersonation. If you want to use impersonation, uncomment line 96. The inline code and the download have been updated.

  DelegateManagement.zip (9.2 KiB)

Delete all empty subfolders of Deleted Items

At my company we use a retention policy to delete items in the Deleted Items folder that are older than 14 days. (This is implemented via Managed Folders, a.k.a. MRM 1.0.) Managed Folders, and even Retention Policies (MRM 2.0), act on items, not folders. A common scenario, though, is that users will soft-delete a folder as an easy way to move all of its items to the Deleted Items folder. After 14 days, the managed folder policy will delete the items in that folder, but it leaves the folder itself. Over time, users may have dozens of empty folders in the Deleted Items folder, and the only way they will ever be deleted is if users delete them one by one. It goes without saying (though I am obviously saying it anyway) that users don’t do this, so lots and lots of empty folders build up over time.

I don’t like this, so I wrote a script that deletes the empty subfolders of Deleted Items for all the mailboxes in the organization. (I actually wrote two scripts, one for the Deleted Items, and one for any specified parent folder.) The script uses the EWS Managed API. You can have any version installed, and it will check for one, starting with the most recent version. The intent of the script is to process all mailboxes in the org, so it retrieves all mailboxes and pipes them into a ForEach-Object loop:

You can always change this line to restrict the returned mailboxes, or use the other script in the download, which supports pipelined input. Note the use of the parentheses when getting the collection of mailboxes. This is to avoid the pipeline constraint when using implicit remoting. The first function called is to get the folder ID of the Deleted Items folder for a given mailbox:

The FolderScope parameter is set to the Deleted Items folder, which is piped to Select-Object to limit the result to the top-level folder, ignoring any subfolders that will also be returned. The FolderId property is the function’s output.

The next step is to convert the folder ID from the management shell to one that is usable by Exchange Web Services:

Any plus (+) signs are transposed to hex since they get in the way. A typical connection to EWS is then established. The AlternateId class is used to specify an ID and what format it is in. Folder IDs in the management shell’s cmdlets are in the OwaId format. To convert them to the EwsId format needed by EWS when referencing an item, the ConvertId() method of the ExchangeService class (the base class of the EWS connection) is used. The UniqueId property is the function’s output.

The last function called is to actually delete any subfolders of the folder with the ID the previous function returned:

You can see this function redundantly establishes a new EWS connection. This is because I was adapting functions used elsewhere for this script and I didn’t optimize it. The function may take less time to complete by using a session that has already been established, but I didn’t determine that or change the previous function to create the session variable in the parent scope so it could be reused. After the session initialization, the function connects to the mailbox and binds to the Deleted Items folder using the folder ID from the previous function.

In order to get the list of empty subfolders, a search is performed for folders that contain no items. This is done by creating a view that will hold the results and defining a search filter whose only restriction is that it contains no items. The search is executed and then looped through each returned folder. Because the search is recursive, the loop will ignore any folder whose parent folder is not Deleted Items. The folder is then deleted.

Write-Output is used, not only to save puppies, but so that you can see what is happening while still allowing you to save the output to file. This can be accomplished with Tee-Object, e.g.:

One caveat of the script as written is that a subfolder that also contains subfolders, which may contain items, will still be deleted. Since I am deleting subfolders of Deleted Items, whose items are deleted after 14 days anyway, I am not too concerned about inadvertently deleting items. If this will be a problem for you, you will need to modify the script accordingly. (I may do so in the future.) Another workaround if this is a problem, is to use the second script (explained next) which has a parameter for skipping folders that have subfolders, regardless of their content.

While writing the script, I thought it might be beneficial to be able to run this process against any parent folder. So I converted the Deleted Items script into one that does just that. You can run this script with parameters that will do the exact same thing as the first script, but it also lets you specify any of the other folder scopes or a custom folder path. You can also indicate that you don’t want subfolders that have subfolders to be included (to avoid the potential issue using the first script). Lastly, this script requires that you provide a mailbox to act on. This can be manually specified with the Identity parameter, or you can pipe any number of mailboxes into it.

The FolderScope and FolderPath parameters are mutually exclusive. Use FolderScope when you want to easily specify one of the inbuilt scopes available to the Get-MailboxFolderStatistics cmdlet. This could be, for example, Inbox, RssSubscriptions, or SentItems. Use FolderPath when you want to specify a literal path to the parent folder, such as /Inbox/Subfolder1. The path to the folder is validated to ensure it starts with a forward slash and does not end with one, which matches the output of the Get-MailboxFolderStatistics cmdlet.

The functions in this script are broken down to be inline (not using functions) in order to support pipelining. The other change is supporting the exclusion of subfolders that have subfolders. This is done by modifying the search to use an additional restriction:

If the parameter to exclude subfolders is used, a search filter collection has to be used because of the need for multiple criteria. You define the search filters the same (using a unique name for each variable), but then you create a search filter collection object, add the search filters to the collection, and then use that object as the search filter in the search execution.

Both scripts are included in the download file, or you can copy the code below.

  Remove-EmptySubfolders.zip (3.4 KiB)

Remove-EmptyDeletedItemsSubfolder

Remove-EmptySubfolder