Thursday, April 30, 2009

Add Delegates to a Mailbox with Powershell and the EWS Managed API

****Update if you looking for something to delete invalid delgates have a read of this http://gsexdev.blogspot.com/2011/03/dealing-with-invalid-delegates-with.html as well ****

****Update 2  for a better post on adding/removing and updating delegates see http://gsexdev.blogspot.com.au/2012/03/ews-managed-api-and-powershell-how-to.html
 


Someone emailed me about this one today and its one thing that can now easily be solved using the EWS Managed API. There’s already a good C# sample on MSDN for doing this http://msdn.microsoft.com/en-us/library/dd633621.aspx. To convert this to something you can use in Powershell once you have downloaded and installed the EWS Managed API would look like the following below. This example sets the calendarpermissions to editor and the inbox to read for full details of what other permissions you can set have a look at the SDK.
$delegatetoAdd = "delage@youdomain.com"

$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$sidbind = "LDAP://<SID=" + $windowsIdentity.user.Value.ToString() + ">"
$aceuser = [ADSI]$sidbind

$service.AutodiscoverUrl($aceuser.mail.ToString())

$mbMailbox = new-object Microsoft.Exchange.WebServices.Data.Mailbox($aceuser.mail.ToString())
$dgUser = new-object Microsoft.Exchange.WebServices.Data.DelegateUser($delegatetoAdd)
$dgUser.ViewPrivateItems = $false
$dgUser.ReceiveCopiesOfMeetingMessages = $false
$dgUser.Permissions.CalendarFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Editor
$dgUser.Permissions.InboxFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Reviewer
$dgArray = new-object Microsoft.Exchange.WebServices.Data.DelegateUser[] 1
$dgArray[0] = $dgUser
$service.AddDelegates($mbMailbox, [Microsoft.Exchange.WebServices.Data.MeetingRequestsDeliveryScope]::DelegatesAndMe, $dgArray);


This script will add a delegate to the currently loged on user but this is something you may want to use impersonation for see http://msdn.microsoft.com/en-us/library/bb204095.aspx if you wanted to add a delegate to possiblly a large number of users or if you where doing this during mailbox provisioning. So this version of the script will use impersonation to access another users mailbox and add a delegate.
$mbtoDelegate = "user@domain.com"
$delegatetoAdd = "delegate@domain.com"


$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$sidbind = "LDAP://<SID=" + $windowsIdentity.user.Value.ToString() + ">"
$aceuser = [ADSI]$sidbind

$service.AutodiscoverUrl($aceuser.mail.ToString())
$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $mbtoDelegate);

$mbMailbox = new-object Microsoft.Exchange.WebServices.Data.Mailbox($mbtoDelegate)
$dgUser = new-object Microsoft.Exchange.WebServices.Data.DelegateUser($delegatetoAdd)
$dgUser.ViewPrivateItems = $false
$dgUser.ReceiveCopiesOfMeetingMessages = $false
$dgUser.Permissions.CalendarFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Editor
$dgUser.Permissions.InboxFolderPermissionLevel = [Microsoft.Exchange.WebServices.Data.DelegateFolderPermissionLevel]::Reviewer
$dgArray = new-object Microsoft.Exchange.WebServices.Data.DelegateUser[] 1
$dgArray[0] = $dgUser
$service.AddDelegates($mbMailbox, [Microsoft.Exchange.WebServices.Data.MeetingRequestsDeliveryScope]::DelegatesAndMe, $dgArray);

I've put a download of the two scripts here

35 comments:

Michael said...

Great stuff! Exactly what I was looking for... only problem is I can't get it going :-(.

The code itself appears to run ok, however I strike a problem when the last line runs. The CPU usage jumps up to 95-100% but doesn't appear to be doing anything. Have left it running like this for ~10mins before killing it off. This happened on two different Win2003 R2 SP2 servers, using both Powershell 1.0 and 2.0 and the 32bit EWS API.

Did you strike this problem? Can you tell me what you tested this on? ie OS, x64/x86, Powershell version etc. Hopefully if I match what you used, it'll work!

Am really hoping I can get this working. Have a large number of delegates to update... and aren't looking forward to doing it manually :-).

Thanks

Michael said...

Hi have partly resolved my problem above.

Should have mentioned that I was using the second script with the impersonation.

I tried the first without impersonation, which worked fine.

So have concluded this is an issue with impersonation - although I would have thought I'd get access denied or something (perhaps I would have if I left it running longer).

I tried quickly granting myself the impersonation rights but didn't seem to make any difference. May not have done it right.

At any rate - I'm happy with using the first script. Thanks very much Glen!

Darrin said...

Hello Glen,

I just tried the script you have here, and ran into an issue with the very last command. The error I got was:

Exception calling "AddDelegates" with "3" argument(s): "The Url property on ExchangeService object must be set."
At line:1 char:22
+ $service.AddDelegates( <<<< $mbMailbox, [Microsoft.Exchange.WebServices.Data.MeetingRequestsDeliveryScope]::DelegatesAndMe, $dgArr
ay);

Any suggestions? Thanks.

Glen said...

This means that autodiscovery of the currently logged on users hasn't worked this may mean the user you are logged on as doesn't have a exchange 2007 mailbox. You can manually set the Cas server by using the following line

$service.AutodiscoverUrl($aceuser.mail.ToString())
$uri=[system.URI] "https://servername/ews/exchange.asmx"

Cheers
Glen

Anonymous said...

Hi, this looks very promising and will help me out a lot. I've downloaded and installed EWS Managed API, but how do I use it?

The sample script doesn't look anything like what you can type in the Exchange Management Shell interface like other powershell commands.

I've reviewd the SDK and can not find any info on what all the permission levels are available for setting delegate rights.

Anyway you can provide a little more information on this? I don't do programming at all and this looks to be fairly involved. Was wondering if there was a more "powershell" way of doing this that was a little simpler. Thanks.

Brian Lane said...

Great post... I've been trying to do this sort of thing through EMS for a while now, particularly the permission levels...

I downloaded the sample code and had a little play, and it works great.

Due to the environment I'm in, we're trying to add MailEnabled groups as delegates. Any ideas on how that would be possible?

When running AddDelegates() with a group, it fails with "The delegate does not map to a user in the Active Directory" which is correct, as it is a group and not a user. The API doesn't seem to support adding a group, well not that I could see.

Any ideas/help would be appreciated :)

Glen said...

To use a group a delegate it has to be Mail Enabled Universal Security Group. If its a distrution group then it wont work also there is a bug if its been automatically converted from a UDG to USG One problem when this conversion happens is that it doesn't modify the msExchRecipientDisplayType which still remain at 1. EWS wont let you add a group unless the property value is 1073741833. If you have a group that has been converted to a Universal Security Group and that value of this property isn't set to 1073741833 then that usually the problem. What you can do is modify this property manually with ADSIedit. You should take great care when doing so and you should only every modify the property on a group that is already a Universal Security Group dont change this value for Global Groups.

Cheers
Glen

Brian Lane said...

Thanks for the reply Glen. The groups I'm using are created specifically as Universal Security Groups that are then mail enabled. I've double checked the msExchRecipientDisplayType and it is set to 1073741833.

I'm suspecting the API just doesn't handle groups?

Pete said...

Glen - this is an awesome post, thank you.

The second script is exactly what I need, except that I need to say

for each userX in X group
{
giveUserZdelegateAccessTo(userX)
[ie your script]
}

What would be the powershell version of this?

Thanks

Glen said...

Not sure i understand the question this is what this script does it is powershell

Cheers
Glen

Glen Mark Martin said...

I would love to use this to manage delegates on a resource mailbox. However, Exchange Impersonation fails on resource mailboxes since the underlying account is disabled. Is there any way to use the EWS Managed API to bind to resource mailboxes just using Full Access privs and no impersonation?

Glen said...

In the download there is a version that uses impersonation and one that just uses plain delegate access so if you use the other script for resource mailboxes it should work fine.

Cheers
Glen

PhuongNC said...

Hi Glen,
I tried your second script and got error at the last command:
Exception calling "AddDelegates" with "3" argument(s): "Request failed. The underlying connection was closed: Could not
establish trust relationship for the SSL/TLS secure channel."
At C:\SON\delegatefolder.ps1:27 char:22
+ $service.AddDelegates( <<<< $mbMailbox, [Microsoft.Exchange.WebServices.Data.MeetingRequestsDeliveryScope]::Delegates
AndSendInformationToMe, $dgArray);

How can I solve this problem?
Thanks

Oppergod said...

I also fail to add a group as a delegate. I also checked the msExchRecipientDisplayType property.

ErrorMessage : The delegate does not map to a user in the Active Directory.
"

KyoungSeok said...

I have also the same problem, delegate to group object.

ErrorDelegateNoUser

The gruop is Mail Enabled Universal Security Group.

Does the EWS really support delegating group?

Anonymous said...

Great article - just wish it worked for delegating to groups!

Blog Note said...

HI glen

Exchange 2010 is now available (even the Sp1 beta) with new features like native calendar delegation within OWA. But still no native "add/modify/remove delegates to a mailbox" in OWA.
Is your 2007 way of doing it using Powershell and the EWS Managed API still workin in 2010 ? do you think you could improve it regarding new 2010 features ?

thanks
fred

Glen said...

Nothing new but i will work fine on 2010 you should change the version in the service object to 2010 though.

Cheers
Glen

Carl Nelson said...

Great work! I appreciate your efforts with EWS.

I altered one of your lines, but it does not seem to work on an E2K10 mailbox:

$dgUser.ViewPrivateItems = $true

This does not result in the delegate being able to "see my private items", nor is that item checked off when looking at the delegate's permissions.

Do you think this is due to the different versions of EWS?

Thanks!

Anonymous said...

Can this code also be adjusted to list the delegates on a (or better all) users ?
All code I've found so far will not list the 'invalid' delegates (ghosts), which however do still show in Outlook Delegations tab

Thanks,
Rob

Carl Nelson said...

I fixed my problem....Or I didn't wait long enough for the change to take full effect. That's the trouble with doing multiple things at a time. If others have the problem described below, and a little patience makes no difference, reach out to me, and I'll share.

"Great work! I appreciate your efforts with EWS.

I altered one of your lines, but it does not seem to work on an E2K10 mailbox:

$dgUser.ViewPrivateItems = $true

This does not result in the delegate being able to "see my private items", nor is that item checked off when looking at the delegate's permissions.

Do you think this is due to the different versions of EWS?

Thanks!"

Glen said...

Are you using the Latest version the of the Managed API ? If you enable tracing eg

$service.traceenabled = $true

This will let you see what request are sent to and from the server can you see if this setting is being sent

Cheers
Glen

Glen said...

Also make sure you set the request version to 2010 are you running SP1 on your server ?

Carl Nelson said...

I downloaded EWS API 1.1 (I had been running 1.0). I had already set the service object to 2010. I'm not on
E2K10 SP1 yet. It turns out that the condition ($dgUser.ViewPrivateItems = $true not working) persists in an inconsistent fashion.

Carl Nelson said...

Breaking news...I discovered that a mailbox with the problem also had a problem with free/busy information, and delegate stuff wasn't working right to begin with. After rectifying this, the script worked as designed.

orla said...

Hello Glen, I must confess this is quality stuff here.
I get this error when I am trying to deligate to a mail-enabled universal security group " The delegate does not map to a user in the Active Directory."
Can you advise please
Ola

orla said...
This comment has been removed by the author.
Glen said...

You can't add a group as a delegate using EWS this is supported AFAIK.

Cheers
Glen

orla said...

This glen, this is my scenario, I want to deligate 10user mailbox to 10 user
$mbtoDelegate = 10 different users
$delegatetoAdd = 10 different users"

but the script adds only one deligate and not the entire 10 as deligate, any suggestions?

Kelly Salvatori said...

Not sure I understood your comment. Is it supported to assign a group as a delegate using the EWS API?

Glen said...

Last I tested it wasn't although in 2010 they do seem to have fixed the bug with ACLs for groups as long as they are Universal Security Groups

Cheers
Glen

Anonymous said...

Try to you your script but get some error messages like this:

Exception calling "AutodiscoverUrl" with "1" argument(s): "The Autodiscover service couldn't be located."
At C:\ps\adddelegatesimp.ps1:14 char:25
+ $service.AutodiscoverUrl <<<< ($aceuser.mail.ToString())
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Exception calling "AddDelegates" with "3" argument(s): "The Url property on the ExchangeService object must be set."
At C:\ps\adddelegatesimp.ps1:25 char:22
+ $service.AddDelegates <<<< ($mbMailbox, [Microsoft.Exchange.WebServices.Data.MeetingRequestsDeliveryScope]::Delegates
AndMe, $dgArray);
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

Can anyone help me to fix this issue?
Thanks!!!

Glen said...

That is permission related suggest you have a read of http://gsexdev.blogspot.com/2009/04/using-ews-managed-api-with-powershell.html which describe in more detail how to work around permission and other auto-discover issues.

Cheers
Glen

Jens said...

Hello Glen,
Thanks for your answer. I made some changes on my exchange 2007 test system and now your script is working fine ;-). But I have a other problem. I will set one user account to have read permissions on everyone’s calendar. When I use your script I can add the permissions to a mailbox, but if a user have a look on his outlook calendar permissions he will see the entry of the user I added and can delete it. Is it possible to add the calendar permissions in a "hidden" way?
Other question: If I use the script to add read permission to a mailbox and after that I delete this permission in outlook. When I call the script again, I got this message:
DelegateUser : Microsoft.Exchange.WebServices.Data.DelegateUser
Result : Error
ErrorCode : ErrorDelegateAlreadyExists
ErrorMessage : The user is already a delegate for the mailbox.
ErrorDetails : {}
ErrorProperties : {}
But the permissions for that user account will be not set any longer. What`s wrong?

Jens said...

Ok... I find out the answer for the first question, I had only to use UpdateDelegates instead of AddDelegates ;-). But the second question is still the same…