1. CAS - stateless goodness!
This article is solely dealing with the Exchange CAS array (client access services), more precisely with Outlook connectivity. A client has multiple ways to access his/her mailbox on an Exchange server. They can use the very well-known POP3 or IMAP4 protocols that virtually any email client can handle, or using Microsoft-proprietary protocols like EAS (Exchange Active sync) with mobile devices or MAPI (more precisely MAPI over HTTP starting with Exchange 2013), as well as Outlook Anywhere (for legacy devices that can only support RPC over HTTP).
The three latter are better suited for MS clients as they make the client experience more seamless, ideally faster, snappier. When they are not behaving this way that usually boils down to configuration issues with Exchange or in the network-stack.
In this article we focus on these three, as they are the main client-connection protocols and they all use the HTTPS network protocol. That means when it comes to load-balancing and setting our reverse proxy up we only need to worry about port 443/tcp. Of course, if needed ports for POP3 (110 and 995/tcp) and IMAP4 (143 and 993/tcp) can also be set up following the guide below.
After installation is done, by default the Exchange server is set up with virtual directories for the different kind of services that clients can access. These virtual directories refer to the individual server names in their addresses, making it differ on each server in the organization. For instance, OWA, the Web-based user interface is listening on https://MB1.alwayshotcafe.com/owa on server MB1. MB2 has the same service configured on https://MB2.alwayshotcafe.com/owa
Get Virtual Directories
Function GetVirtDirs { Get-MailboxServer | %{ Get-ActiveSyncVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-WebServicesVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-MapiVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-EcpVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-OwaVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-OabVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-PowerShellVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl Get-AutodiscoverVirtualDirectory -Server $_.Name | Select Server, Name, InternalUrl, ExternalUrl } }
The issue with this is when a client access server goes down, all the clients who are connected to the server will have their Outlook frozen causing a long downtime. Assuming of course that the databases are taken care of by configured DAG.
Luckily starting with Exchange 2013 the CAS connections are stateless connections, so all we need to do is to configure the virtual directories with a designated namespace for the client connections.
In our example we pick 'outlook.alwayshotcafe.com' for the CAS. This will match on all the member servers, so when a client connects through a layer 4 or layer 7 load balancer, it doesn't matter which server gets that user query. Remember, CAS connections are stateless so layer 4 load balancers will just do the job, although a layer 7 solution is better as it remembers a client and directs it to the same member each time, making the queries much faster.
Here we use HAProxy on our pfSense firewall for load balancing (setup guide comes here). Let's see how it all comes together.
2. Configure the unified namespace for client access
We'll use outlook.alwayshotcafe.com for client access. First, we configure the load balancer with a dedicated IP that listens for connection requests, then forwards them to available CAS servers. The IP in our case is 10.0.1.250, we add an new A record to our domain zone file with this IP as 'outlook'.
The next step is setting the virtual directories on the servers with the new FQDN
# Setting the name of the unified cas address $cas = 'outlook' ForEach ($server in (Get-MailboxServer).Name) { # Getting virual direcory addresses $eas = (Get-ActiveSyncVirtualDirectory -Server $server).InternalUrl.AbsoluteUri $ecp = (Get-EcpVirtualDirectory -Server $server).InternalUrl.AbsoluteUri $mapi = (Get-MapiVirtualDirectory -Server $server).InternalUrl.AbsoluteUri $oab = (Get-OabVirtualDirectory -Server $server).InternalUrl.AbsoluteUri $owa = (Get-OwaVirtualDirectory -Server $server).InternalUrl.AbsoluteUri $ews = (Get-WebServicesVirtualDirectory -Server $server).InternalUrl.AbsoluteUri # Uniformizing virtual directory addresses (same namespace) $easUni = $eas -replace ($server, $cas) $ecpUni = $ecp -replace ($server, $cas) $mapiUni = $mapi -replace ($server, $cas) $oabUni = $oab -replace ($server, $cas) $owaUni = $owa -replace ($server, $cas) $ewsUni = $ews -replace ($server, $cas) # Setting new names, external address as well Get-ActiveSyncVirtualDirectory -Server $server | Set-ActiveSyncVirtualDirectory -InternalUrl $easUni -ExternalUrl $easUni Get-EcpVirtualDirectory -Server $server | Set-EcpVirtualDirectory -InternalUrl $ecpUni -ExternalUrl $ecpUni Get-MapiVirtualDirectory -Server $server | Set-MapiVirtualDirectory -InternalUrl $mapiUni -ExternalUrl $mapiUni Get-OabVirtualDirectory -Server $server | Set-OabVirtualDirectory -InternalUrl $oabUni -ExternalUrl $oabUni Get-OwaVirtualDirectory -Server $server | Set-OwaVirtualDirectory -InternalUrl $owaUni -ExternalUrl $owaUni Get-WebServicesVirtualDirectory -Server $server | Set-WebServicesVirtualDirectory -InternalUrl $ewsUni -ExternalUrl $ewsUni }
3. Verify the virtual directories are set
We run the GetVirtDirs function (see step 1) to verify they are all set with the outlook.alwayshotcafe.com FQDN
From this point in time all client access connections are received by our load balancer, then forwarded to an available Mailbox server.
We can verify this on a client too:
Comments