Super duper delegate retrieval script

Update 5/11/12: EWS 1.1 download link no longer valid, so the script has been updated to use 1.2.

Update 3/13/12: The script has been updated to accommodate running in either EMS or PowerShell with remoting. Support for additional delegate retrieval errors has been added, as well as loading the Exchange snap-in directly if no cmdlets are already in the session. Inline code and download have been updated.

Update 11/11/11: Minor update made to resolve a potential issue when translating user name to SID. Inline code and download have been updated.

Getting delegate information has always been tricky. Information is stored in multiple places, you have to use different tools to get that information, and business requirements can add to the data needed to paint a complete picture for a given delegate. Taking advantage of the EWS Managed API to do the heavy lifting for information not easily exposed, I wrote this script to report delegate permissions and settings, mostly to help when troubleshooting why a delegate can’t do something or is getting weird behavior.

Using the GetDelegates() method of the managed API not only retrieves the delegates and their permissions to the well-known folders, but also whether private items are visible, if meeting requests are sent to a specific delegate, and the meeting request handling of the owner. In reality, though, that isn’t enough information to give the total picture. Full mailbox access supersedes folder permissions, so knowing who has that is needed. My company uses the registry modifications that control where deleted items and sent items, which means a delegate needs permission to those folders, so getting that is needed. Lastly, delegates are granted send on behalf of permission (which, incidentally, isn’t even required for a delegate to send a meeting request on behalf of the owner), but if a delegate has send as permission, that takes precedence when sending email messages, so knowing if a delegate has that right is also necessary.

The only required argument is an owner’s identity, such as email address, username, alias, etc. An optional switch parameter is -includeSendAs. I made it an opt-in feature because getting the send as information from AD makes the entire script run over 12 times longer (in my company’s infrastructure): 1.7 seconds versus 20.7 seconds for an owner with five delegates. Although I prefer to not hard-code information that can be determined dynamically, I took the poor man’s route for the EWS URL, so you need to enter that on line 26. If you don’t care about any of the details of the script contents, you can simply stop reading, download it, and run with it. (2.5 KiB)

Thanks to the managed API, retrieving the delegates can be done in seven lines (four, if you specify the DLL path when loading it instead of putting it in a variable, the same for setting the EWS URL, and if you specify the Exchange version directly when creating the service object).

After getting the delegates, full mailbox access and, optionally, send as are retrieved. Because of the added time to get send as, I added a progress bar while information is being retrieved. The delegate properties stored in the owner’s mailbox include email address, display name, and SID. The property returned for full mailbox access and send as is username. Since there isn’t a common property between the two collections, the usernames must be converted to SIDs:

Because of the registry changes to store items deleted from the owner’s mailbox in the owner’s Deleted Items folder, and to store items sent by the delegate from the owner in the owner’s Sent Items folder, Author permission or higher is needed for those folders. This is easy to do with Get-MailboxFolderPermission.

Once all the information has been retrieved, you need to loop through the collection of delegates. When an orphaned delegate is on an owner’s mailbox, Exchange returns the delegate in the collection, but only as an error, not with any identifying information. I look for a matching error message and then direct you to the hidden message and property that lists all the delegates to find out which delegate is orphaned:

If a delegate has a configuration error, such as not being listed in the publicDelegates attribute or in the Freebusy Data folder’s ACL, the delegate won’t be returned, so this specific error is noted. And if any other error occurs retrieving a delegate, instead of just continuing and throwing errors later, a generic error will be noted:

To report whether a given delegate has full mailbox access or send as permission, the SID of the delegate is matched against the respective array from the GetSID() function above:

The collection includes the granted permission to the well-known folders exposed in Outlook’s delegation wizard. Since it is rare for someone to use or grant permission to Journal or Notes, rather than just echo the permission array, I display just the other four folders:

To include the delegate’s potential permission to Deleted Items and Sent Items, I pass the respective folder permission object to the pipeline looking for a display name match:

Lastly, whether a delegate receives meeting requests or can view private items is a property of the individual delegate’s entry in the collection. How the owner wants meeting requests handled is not per delegate, so that setting is a property of the root collection. All of this is then output to the success stream, whether that is the screen (the default) or redirection to a file.

You can download the script from the link above, or copy the contents below (double-click in code area to highlight all).

8 thoughts on “Super duper delegate retrieval script

  1. Hi Scott
    I came across your site after looking for a script to get delegate info from an Exchange 2010 mailbox.
    I’ve downloaded your Get-Delegates.ps1 script and have followed the instructions you’ve kindly published…..but I’m getting errors when running the script and cannot see what needs to be amended. Can you look at the output errors and advise where the problem may be please?

    When I run the script against my mailbox, I get the following output:
    [PS] C:\scripts>.\Get-Delegates.ps1 matthew.pollock -includeSendAs
    Exception calling “GetDelegates” with “3” argument(s): “The specified object was not found in the store.”
    At C:\scripts\Get-Delegates.ps1:56 char:23
    + $service.GetDelegates <<<< ($owner,$true)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    New-Object : Constructor not found. Cannot find an appropriate constructor for type System.Security.Principal.NTAccount
    At C:\scripts\Get-Delegates.ps1:46 char:23
    + $adUser = New-Object <<<< System.Security.Principal.NTAccount($_.User)
    + CategoryInfo : ObjectNotFound: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand

    You cannot call a method on a null-valued expression.
    At C:\scripts\Get-Delegates.ps1:47 char:29
    + $aSID += $adUser.Translate <<<< ([System.Security.Principal.SecurityIdentifier]).Value
    + CategoryInfo : InvalidOperation: (Translate:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Matt Pollock has delegate(s)

    Many Thanks

    Matt Pollock

  2. Exception calling “GetDelegates” with “2” argument(s): “The specified object was not found in the store.”
    At \\cfpbgfs01\users\justinr\Documents\DelegateManagement\DelegateManagement.psm1:680 char:4
    + $currentDelegates = $exchangeService.GetDelegates($EWSMailbox,$true)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServiceResponseException

    I see a few people getting this. I donot have delegates on my account and I get no error. However I cant run this script against people that I know have delegates.

  3. Where does the file get output to? I have run it, dont get any errors but never get a file with the data.

  4. The output is a PowerShell object which, by default, will display in the shell/console. If you want to output to a file, you can pipe the object to Out-File or another Out- cmdlet.

  5. Outstanding! Used this to change private delegate settings on Shared Mailbox. Worked great on Exchange 2010.

  6. Hi,

    Is there a way to run this against every mailbox in our system, rather than one at a time?



  7. The Get-MailboxDelegate cmdlet targets a specific mailbox, but you can pipe the output of Get-Mailbox into it to process all of those mailboxes. For example, Get-Mailbox -ResultSize unlimited | Get-MailboxDelegate or, depending on your target environment and how you are connected, something like (Get-Mailbox) | %{Get-MailboxDelegate $_.Identity}.

Leave a Reply

Your email address will not be published. Required fields are marked *