Managing Affinity for EWS Impersonation in Exchange 2013 and Exchange Online W15

Updated 7/25/2013: This post has been re-written. The guidance is the same but I felt I needed to clarify the problem scenario and solution as well as add some code. The guidance in this post relates only to using Exchange Impersonation with EWS in W15. Refer to my previous post to understand the techniques for managing affinity for EWS Subscriptions changed between W14 and W15.

    

Consider the following scenario…

You have an Exchange Web Services based application or service that uses a service account which has been granted the ApplicationImpersonation role and makes EWS requests with the ExchangeImpersonation SOAP header to access target mailboxes. As the frequency of basic EWS requests (GetItem, FindItem, GetFolder, etc.) made by the application or service to different mailboxes increases, the round trip times decrease or in some cases the requests fail with 500/503 HTTP error responses from Exchange 2013 or Exchange Online (W15).

To get the best throughput for EWS requests using impersonation and avoid 500/503 errors use the following solution…

A high scale EWS-based application/service that uses impersonation needs to manage affinity with the backend Exchange server by adding an HTTP header called "X-AnchorMailbox" to requests with the ExchangeImpersonation SOAP header. The values of the X-AnchorMailbox HTTP header and the ExchangeImpesonation SOAP header on the request should match.

In April, I wrote a post about EWS subscriptions in Exchange 2013 and Exchange Online (W15) which detailed the new rules for managing affinity. I concluded that post by saying…

"…[In W15] the only time affinity really should be managed by the client is subscription related requests like Subscribe, GetStreamingEvents, GetEvents, and Unsubscribe. Other requests like SyncFolderItems, FindItem, GetItem, etc. should not set X-PreferServerAffinity or return the X-BackEndOverrideCookie…"

The statement above is mostly correct for simple EWS applications that aren't using impersonation. For migration applications, services monitoring and reacting to changes across organization-wide mailboxes, etc. that use impersonation to access target mailboxes this statement is false - these applications and services should manage affinity for any request made with the ExchangeImpersonation SOAP header.

…Why? What's going on here?..

Like the subscription scenario, the need to manage affinity with impersonation goes back to changes in the CAS and MBX server roles between W14 and W15. As often explained, the CAS role is now just a smart proxy. Greg Taylor explained this most recently at TechEd. Specifically, slide #4 shares "the key to enlightenment" as follows…

When it comes to ApplicationImpersonation with EWS, we have to look at the assumption made in the Greg's second statement - "Each CAS determines the right end point for the traffic…" How does the CAS determine the right MBX server to send the traffic to? Well, for EWS at least, it is primarily based on the authenticating user. The CAS is not smart enough try and peel apart the EWS request

As we know about impersonation in EWS, the service account authenticates and the EWS request has a SOAP header which notes the mailbox to be impersonated. What this means is that in W15 the CAS server is going to send your request to the MBX server where the service account's mailbox lives. If the target mailbox the service account is trying to impersonate does not reside on the same MBX server then the request is proxied to the right MBX. If the EWS application is a high scale service, sending many requests at a time that's a lot of proxying for one MBX to do. There are at least two possible outcomes: 1) At best, you'll notice your throughput drop off due to the proxying overhead. 2) At worst, if the MBX gets overwhelmed by all the proxying, your requests might start failing with 500 or 503 HTTP errors.

The solution is to set an HTTP header called X-AnchorMailbox to the same value that you set the ExchangeImpersonation SOAP header's ConnectingSID element value. The EWS Managed API and EWS Java API will not do this automatically. However they both allow you to manually set X-AnchroMailbox using the HttpHeaders object which is a child of the ExchangeService object. You can use the HttpHeaders object's Add method to add the header name "X-AnchorMailbox" and set the value of that header to the same string used in ExchangeImpersonation. So in the example from this blog post, the value of X-AnchorMailbox would be "someone@contoso.com".

Here is an example of setting X-AnchorMailbox using the EWS Managed API, this is very similar to the EWS Java API…

  1: static void GetInbox_EWSAPI(string serviceSmtp, string servicePass, 
  2:     string targetSmtp)
  3: {
  4:     // NOTE: Even though we're using the Exchange2010 schema, we have 
  5:     // to set the HTTP headers. The HTTP header is related to request 
  6:     // routing at the CAS layer, outside the EWS schema
  7:     ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
  8:     service.Credentials = new NetworkCredential(serviceSmtp, servicePass);
  9:  
  10:     service.TraceEnabled = true;
  11:     service.TraceEnablePrettyPrinting = true;
  12:  
  13:     // Speed up the AutodiscoverUrl() method response time by skipping 
  14:     // a lookup for an SCP record in local AD since we know in this case 
  15:     // we're accessing O365
  16:     service.EnableScpLookup = false;
  17:           
  18:     // NOTE: Use Autodiscover to get the EWS URL for the *target* mailbox,
  19:     // not the service account. This recommendation is the same for W14 
  20:     // and W15
  21:     service.AutodiscoverUrl(targetSmtp, delegate { return true; });
  22:  
  23:     // NOTE: Both the SOAP header "ExchangeImpersonation" and the HTTP header
  24:     // "X-AnchorMailbox" are set to the SMTP of the target mailbox
  25:     service.ImpersonatedUserId = new ImpersonatedUserId(
  26:         ConnectingIdType.SmtpAddress, targetSmtp);
  27:  
  28:     service.HttpHeaders.Add("X-AnchorMailbox", targetSmtp);
  29:  
  30:     Folder inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
  31: }

You can see the effect of the X-AnchorMailbox  by testing this code against Exchange Online W15 and comparing the response headers returned with the GetFolder response – assuming that the service account and target account mailboxes are on different MBX servers. You’ll notice in this test that the value of X-DiagInfo differs when this header is used. This is response header indicates which MBX server received the request.