PowerShell cmdlets to add or remove resource delegates in bulk

A while back, I noticed that Exchange Online’s Set-CalendarProcessing cmdlet no longer performs any validation for some of its parameters, and diligently issued a PSA about it. While Microsoft is looking at the issue (and will hopefully address it), an idea came to mind – provide a better cmdlet that not only does proper validation, but can be used in bulk scenarios. So in this article, let’s examine how to build and work with PowerShell cmdlets that will help us automate the process of adding or removing resource delegates across multiple mailboxes, without worrying about potentially overwriting existing values.

Implementation details (feel free to skip)

To configure a resource delegate, we need two things. First, we need the resource mailbox on which we want to configure the delegate(s). The important bit here is that not every mailbox can have resource delegates configured. Only resource mailbox objects, meaning either a room mailbox (with a RecipientTypeDetails value of RoomMailbox) or an equipment mailbox (RecipientTypeDetails value of EquipmentMailbox) are allowed. The Set-CalendarProcessing cmdlet will throw an error if you try to configure resource delegates for any other type of mailbox. Therefore, a check is added to our cmdlets in order to ensure that only “proper” mailbox values are accepted.

If we want to ensure the cmdlets will support bulk actions, that is adding or removing one or more resource delegates to one or more resource mailboxes in one go, it pays to add some additional logic to ensure the input values are properly validated. This is especially relevant to the scenario of removing delegates, as to date, ExO continues to return their Name as the only identifier under the ResourceDelegates property. And while recent improvements in the service do ensure that Name values are unique, this does not holds true for Exchange server.

Therefore, we want to ensure that only unique values will be passed, and to that purpose, the cmdlets will “resolve” and keep track of any input values provided, replacing them with unique identifiers such as the object’s Primary SMTP Address or its GUID. While this approach adds overhead by issuing additional executions of the Get-Mailbox and Get-Recipient cmdlets, it does have the added benefit of allowing any valid identifier (i.e. Alias, Name, GUID, or any email address associated with the object) as input. It will also minimize the possibility of errors during subsequent calls to the Set-CalendarProcessing cmdlet. For example:

Resource delegates bulk

On the screenshot above, we can see that the mailbox in question already has a set of resource delegates configured. One of the values corresponds to a “duplicate” object, one for which the resolution process used by Get-Recipient results in multiple entries being returned. While only a single object for which the Name property is set to said value exists, passing said value will still result in an error when executing the Set-CalendarProcessing cmdlet.

To account for such scenarios, the cmdlets resolve and keep track of identifier values. They will ignore any input values for which a matching Name was previously encountered. Any values that do not resolve to a valid recipient are ignored as well, thus eliminating the “no validation” issues to which the Set-CalendarProcessing cmdlet is currently prone to. Similarly, we ensure that the Identity parameter only accepts room and equipment mailbox identifiers as input.

In fact, let’s spend one minute recapping the validation issues we detailed previously. The behavior can be replicated not just for non-existent objects, but when passing an identifier for existing object of type (RecipientTypeDetails value of) User, or a RoleGroup one for that matter. By leveraging the Get-Recipient cmdlet to perform a check against the input value, we will automatically exclude such object types, as they are not a valid recipient. We can go a step further by restricting the type of recipient to match against, helping us exclude objects such as Shared mailboxes or Mail Contacts. In effect, we limit the set of accepted values to user mailboxes and group objects.

As a side note here, while (Exchange) groups can be used for delegation purposes, remember that only direct members of the group are honored, any nested groups are ignored. And another side note – we deliberately avoid the use of Exchange Online REST-based cmdlets, such as Get-EXORecipient, in order to ensure that our custom cmdlets will work for both ExO and on-premises scenarios. That said, they were primarily tested for Exchange Online use, so make sure to do some basic validation before running them against Exchange Server installs.

Once we have a proper list of mailboxes and delegates, we can call the Set-CalendarProcessing cmdlet in order to update the resource delegate collection. Of course, we want to ensure that any existing values in said collection will be preserved (for the Add- scenario that is), which necessitates some additional processing. We fetch the current list via Set-CalendarProcessing, then iterate over each input value returned and compare against it. If we determine that no changes are needed, we proceed to the next mailbox. Otherwise, we run the Set-CalendarProcessing cmdlet with the updated set of delegates.

While this approach offers a speed advantage, it makes it a bit harder to track individual entry addition/removals, thus error handling and output granularity are impacted. But as we cannot rely on the Set-CalendarProcessing cmdlet on that front, at least until Microsoft addresses the current issues with validation, we don’t have much choice here. Once the cmdlet behaves in a more appropriate manner, we would be able to check and act upon individual additions/removals, dynamically adjust the set of mailboxes and delegates based on the result and provide proper output. Until that happens though, we will rely on the pre-execution logic added to our custom cmdlet to detect any potential issues and update the resource delegate collection in one go.

Meet the bulk cmdlets

Without further ado, here are the two cmdlets which you can use to bulk add or remove resource delegates:

  • Add-RoomDelegate – use this cmdlet to add resource delegates to a resource mailbox. The cmdlet validates the input values as detailed in the previous section of the article. Bulk processing is supported for both the -User and the -Mailbox parameters.
  • Remove-RoomDelegate – use this cmdlet to remove resource delegates from a resource mailbox, while ensuring that existing delegates are preserved. The cmdlet validates the input values as detailed in the previous section of the article. Bulk processing is supported for both the -User and the -Mailbox parameters.

Both cmdlets support the same set of parameters:

  • Mailbox – use this parameter to specify the identity of the resource mailbox(es) for which you want to update the set of resource delegates. You can use any valid identifier corresponding to a room mailbox or equipment one. Bulk operations are supported by passing multiple mailbox values as an input array.
  • User – use this parameter to specify the identity of the resource delegate(es) you want to add to (or remove from). You can use any valid identifier corresponding to a user mailbox or one of the group types supported by Exchange. The full set of supported recipient types includes: UserMailbox, MailUniversalDistributionGroup, MailUniversalSecurityGroup, GroupMailbox, DynamicDistributionGroup.
    Bulk operations are supported by passing multiple parameter values as an input array.

In addition, they support the following common parameters:

  • WhatIf – use this parameter to preview the cmdlet execution, without committing any actual changes.
  • Verbose – use this parameter to instruct the script to output additional processing details.

To use the cmdlets, download the script from my GitHub repo, and either dot-source it, or copy-paste the cmdlet definitions themselves in your own solution. I deliberately decided not to release them as part of a module, as I didn’t want to create yet another repository for just two cmdlets. Plus, you should see those cmdlets as a proof of concept and a learning tool, not as a production-ready solution.

Anyway, here are some examples on how to run the cmdlets:

#Dot-source the script to load the cmdlets
. .\Room_delegate_bulk.ps1

#Add resource delegate(s) while preserving the existing set of delegates</pre>
Add-RoomDelegate -Mailbox room@domain.com -User userA@domain.com,userB@domain.com

#Remove resource delegate(s) while preserving the existing set of delegates
Remove-RoomDelegate -Mailbox room@domain.com -User userA@domain.com

#Add a given user as resource delegate to all room mailboxes in the organization
Add-RoomDelegate -Mailbox (Get-Mailbox -RecipientTypeDetails RoomMailbox) -User userA@domain.com

#Remove a given user as resource delegate from all room mailboxes in the organization
Remove-RoomDelegate -Mailbox (Get-Mailbox -RecipientTypeDetails RoomMailbox) -User userA@domain.com

The screenshot below shows the execution of the Remove-RoomDelegate cmdlet against all room mailboxes in the company, in order to remove a given user as resource delegate. The -Verbose switch is used to provide details as the cmdlet execution progresses, such as the full set of mailboxes to iterate over and what the resource delegate collection will look like for each mailbox after. If no changes are needed, the mailbox is skipped to speed up the script execution.

Resource delegates bulk1

As another example, here’s how to add all users within the Seattle office as resource delegates for set of room and equipment mailboxes. We populate the latter based on the Building information we can get from the Get-Place cmdlet. Sadly, the latter does not support server-side filtering, so we have to slightly adjust the input, but it’s still doable:

Add-RoomDelegate -Mailbox (Get-Place | ? {$_.Building -eq "Building 9"}).Identity -User (Get-User -Filter {Office -eq "Seattle"}) -Verbose

Resource delegates bulk2

While the cmdlets do support the -WhatIf switch, no logic has been added to support the -Confirm switch. In other words, the cmdlet will not ask you to confirm any addition or removal. The idea is to have them run in an automated manner, without interruption. Once Microsoft fixes the Set-CalendarProcessing cmdlet to properly validate delegates, it makes sense to switch to per-entry execution in order to dynamically remove invalid values, as well as provide some output details on the processing of each entry. Plugging in the ShouldProcess logic is the next logical step at that point, but for this initial release, no confirmation is required for cmdlet execution.

On that note, you might have noticed that there is no Set- cmdlet. The reason is simple – both the Add- and Remove- cmdlets respect the existing set of delegates and will preserve any entries found therein. And when you want to overwrite the existing list of delegates in one go, you can just leverage the Set-CalendarProcessing cmdlet. Or do something like this:

#Remove any existing resource delegates
Remove-RoomDelegate -Mailbox mailbox -User (Get-CalendarProcessing mailbox | select -ExpandProperty resourceDelegates)

#Add the new delegates
Add-RoomDelegate -Mailbox mailbox -User user1,user2,user3

And with that, we can close this article. I will circle back to this once Microsoft fixes the Set-CalendarProcessing cmdlet.

1 thought on “PowerShell cmdlets to add or remove resource delegates in bulk

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading