The Argyle MVP

Yet another Teams blog but this one by Masters & MVP's

Lync Server 2013

Using IIS Application Request Routing (ARR) as a TMG Replacement

So this won’t be shocking news but Microsoft has stopped selling Forefront Threat Management Gateway (TMG) and they really didn’t give us any good alternatives.  Officially, they tell you to use the Unified Access Gateway but anyone who uses it knows that 1) it’s a massive pain to setup 2) it’s really expensive 3) it breaks autodiscover and mobility.

So this leaves us to use third party hardware load balancers but I’m not much of a fan of doing firewall rules directly to the HLB.  The nice thing about the reverse proxy is that it served as a separation between the internet and production.  So I have found some articles on the web about IIS ARR with Lync but they all seem to be 1) not written for Lync 2013 2) don’t work with mobility 3) are too vague 4) assume that you are going to bind one service to one IP – which works find but sometimes people want to not take up a ton of IP’s on the internet to deploy one service.

So this will attempt to guide you through the configuration of ARR to work with Lync Server 2013.

UPDATE: I’ve been asked does this work with Lync Server 2010 and the answer is yes.  I haven’t spent as much time testing every option yet but it does appear to work fine.

Installation

First you need to install IIS on your Windows 2008 R2 or Windows 2012 Server.  I like to click the Application Server which will install pretty much everything you need in terms of .NET Framework.

The second item you need is ARR. This can be installed by downloading version 2.5 + hotfixes from Microsoft’s website or you can use the Web Installer (http://www.microsoft.com/web/downloads/platform.aspx).  This is how I typically install the product:

pic1

 

Search for ARR and select Application Request Routing 2.5 (which will select the hotfixes) and install search for rewrite and install URL Rewrite 2.0.

NOTE: I see that 3.0 Beta is now available.  Feel free to try it out – it worked for me but I’m going to production with ARR so I want to use non-beta software for now.

IIS Configuration

This isn’t meant to be an all encompassing lesson on how to use IIS so I’m going to assume you have some basics under your belt.  You need to make sure that your public certificate is installed on the IIS server.  Go to bindings of your default website and bind your SSL certificate to this server.  This certificate must have a private key associated with it.  Also, make sure that your internal root certificate authority is installed on this server as well.  My test server is not domain joined so I have to install it manually.

Ultimately, your website is going to contain the IP address you plan to listen on.  So this is very similar to the concept of a listener from the TMG world.  You have the same limitation in terms of certificates and IP’s in IIS as you do in TMG.  So if you have two certificates you plan to use on the reverse proxy you will need to bind two IP’s to your IIS server and create two websites.  This guide is going to assume a single website/single IP address for Lync, Exchange and Office Web Apps (OWAS) as I have a single certificate with all of those names on it.

ARR Configuration

NOTE: I had a section on how to enable the proxy within ARR here at first because I was under the impression it was needed for this to work.  Got word from a Lync friend that it wasn’t needed and indeed it was an extra step in the configuration that didn’t harm anything just wasn’t required for the configuration.  Thank Corrado!

Server Farms

Server farms are the targets of where we might send traffic to.  There isn’t a direct correlation to something in TMG but the big thing to know is that ARR does more than just a reverse proxy.  It is a load balancer and more.  So we need to create a farm for Lync.

In IIS Manager, Click on Server Farms and right-click and choose Create Server Farm.

pic5

 

Enter a name

pic6

Click Next

pic7

Enter the server IP address.  Click on advanced settings and change httpPort to 8080 and httpsPort to 4443.  If you are going to use the load balancing feature, add all of your front-end servers in the pool.  If you have a HLB, than this server IP should be going to your HLB.

pic8

 

You will get this message.  Click the Yes button to create a default rules.

pic9

Now you should see your new server farm.  Click on the Lync farm you just created and find Routing Rules.  Double click on it and disable SSL Offloading. There is no reason to offload on the ARR box as far as I can figure out.  If someone has a good reason to leave it on let me know.

pic10

Make sure to click Apply in the Action pane on the right when you are making changes in IIS.

Your farm is now configured and setup.  If you want you can create two more farms, one for Exchange and one for OWAS.

URL Rewrite Rules

Now it’s time to get to the meat and potatoes of the configuration.  We are now going to setup URL Rewrite (or really reroute rules).  This is very similar to the firewall rules that you had in TMG.  They don’t have all of the same features but covers what we need.

In IIS Manager, click on the Server

pic11

 

NOTE: This is URL Rewrite on the server.  Don’t go to the website.  There is a URL rewrite option there as well.

When you go into URL Rewrite you will find two default rules created for you.  The first is the SSL rule and the second is the HTTP rule.  Since I don’t want to use the HTTP rules I am simply disabling them.  You could delete them if you want I suppose as well.

pic12

 

Double click the ARR_Lync_Loadbalance_SSL rule and let’s understand what you see here:

pic13

 

In the Match URL is basically what we are going to make after the / in the URL.  So if our URL was www.domain.com/website the pattern would be /website for example.  You will see under Using you will have the option to use Regular Expressions.

The Conditions is a set of inputs that are required to match this rule.  Here we will we have HTTPS which basically means we must match SSL requests only.

pic14

 

This is a continuation of the above.  The Action section tells us what we should do if we match.  So here we will route to the Lync Server Farm.  This part is pretty straight forward and will be basically the same for all rules.  So let’s create our first Lync Rule.

Meeting/Dialin/External Web Services Rule

Here you have some options based on how you do your simple URLs.  If you do “Option A” where you URL would look like:

https://meet.domain.com and https://dialin.domain.com then you would need to use the below rule.

pic1

Change Using to: Regular Expression

Change Pattern to: (.*)

If you use Option B where your simple URLs might look like this:

https://meet.domain.com/ID/ and https://meet.domain.com/dialin/ your rule would look like this.

pic2

((?:^dialin|^id|^Abs|^autodiscover|^CertProv|^CollabContent|^Fonts|^GroupExpansion|^HybridConfig|^lwa|^mcx|^PassiveAuth|^PersistentChat|^Reach|^RequestHandlerExt|^RgsClients|^Scheduler|^Storage|^ucwa|^WebTicket).*)

NOTE: You will need to change dialin and id to whatever you use if using Option B for simple URL’s

NOTE: This is the list of all potential directories as of RTM of Lync 2013.  CU1 will most likely add some items so this may need to be edited.

Add to Conditions: {HTTP_HOST} on the pattern of (externalwebservices.domain.com|dialin.domain.com|meet.domain.com).

NOTE: You should NOT add LyncDiscover.domain.com to this rule.  The reason we don’t want to add this is because our regular expression doesn’t include the root of the website and lyncdiscover.domain.com/?sipuri= won’t match any of these rules.  If you wanted to, you could change your pattern to (.*) and add lyncdiscover.domain.com to the list. I don’t have a good reason to do it either way.  Whatever makes the most sense to you but I like to separate them out.

Lync Discover Rule

Now we create a rule for Lync Discover services.  Here is what I have created.  This rule is created as brand new and not one of the existing rules.

pic16

 

Here I am defining my pattern match as any request (.*) anything from lyncdiscover.thegaragelab.info URL.  Here I am not requiring HTTPS on this rule Lync Discover will use either HTTP or HTTPS.

OWAS Rule Setup

If you created your web server farm for OWAS you should have your default rules again.  So if I look at that rule:

pic17

 

This rule again is pretty straight forward.  We are matching anything intended to go to owas.domain.com.  The big difference is under Action, our Server Farm is now OWAS and not Lync.  You can do the same thing with Exchange or other services as well.  Here is my completed configuration:

pic18

 

Services Tested

I have tested the following services to make sure they work through ARR.

  • Meeting Join URL
  • Dial-In Website
  • Web Scheduler
  • Mobility

The only thing I haven’t tested thus far is the Lync MX (Metro/Win 8) client.  I’ll try to get remote of my lab and test that later this week.  My history thus far with the Lync MX client is a complete crap shoot – where mobility works but MX doesn’t.

Troubleshooting

So what are some common problems I’ve found while setting up ARR.

#1 – Make sure you can reach your websites from your ARR server.  If the ARR server can’t reach these websites it’s not going to work from the outside.

#2 – Certificates.  Ensure that your internal server has your internal CA root certificate.  If this box isn’t a domain joined server, it won’t be there.  If you did #1 you shouldn’t have this issue because you would have fixed it when the certificate error appeared.

#3 – Make sure your internal certificates have the right names.  For example on OWAS.  If your internal FQDN is owas.intdomain.com and your external FQDN is owas.domain.com, your internal certificate should have BOTH names on it.  Your internal users will look up based on the internal name and when your external people go to that server, they will proxy through ARR with the external name.  It doesn’t have to be a public certificate but it needs to have the name.

Hopefully that covers the most issues you might run into and this gets you on the right page for using ARR and Lync 2013.

UPDATE

I found that you will want to adjust the timeout on the IIS or you will see some timeouts with the new Lync 2013 Mobile Client.  On the Proxy Settings | Time-out – change from 30 seconds to 180 second.

 

Picture

 

40 COMMENTS

    • Yes. I have tested this with Lync 2010 as well and it works fine. You should modify the pattern to match for the Lync 2010 list of virtual directories but otherwise it’s the same.

  1. Great article. Though I found an error in your ARR_Lync_LoadBalance_SSL rule. In the matches screenshot, you have LyncDiscover in there. I would suggest to make sure the matches screenshot matches what you have in the text where you say to change pattern to:
    Change Pattern to: ((?:^dialin|^meet|^Abs|^autodiscover|^CertProv|^CollabContent|^Fonts|^GroupExpansion|^HybridConfig|^lwa|^mcx|^PassiveAuth|^PersistentChat|^Reach|^RequestHandlerExt|^RgsClients|^Scheduler|^Storage|^ucwa|^WebTicket).*)

    Again, excellent article. Just figured I’d point out the above to avoid any confusion a reader may have.

  2. Good article, I will try it today, but I have a few questions.

    In the beginning of the article, you compare it with TMG. Now many NICs do you have? Is it the same setup as TMG where one NIC is on the DMZ and another one on LAN? Can you tell me more about your setup?

    Thank you.

    • I have done it with both a single NIC in the DMZ which can route back to the necessary resources in the internal network and a two leg deployment like the edge server. Both work fine but I do recommend the two NICS.

        • A single NIC should work as that is what I use in my lab for everything. What error are you getting? My first place I troubleshoot always with this is, can I reach all of these websites from the IIS box itself via FQDN without any cert errors or anything else.

  3. Anything you have to do differently if you put it in the DMZ? How does the ARR server authenticate the user to Exchange, or does it simply pass the traffic? ARR is configured to listen for webmail.domain.com and autodiscover,domain.com so i should be able to test by putting in a host file, and point it to ARR, and it should pass me through. Am I missing something?

    • Scott – there is no authentication done on the IIS/ARR box. It’s strictly for reverse proxy of content from the internet to the back-end services. As for the DMZ, there is nothing you need to do different if you do a two-arm environment (one NIC in the DMZ and the second NIC in the internal/rotatable DMZ) – as long as the ARR box can connect to the web pages you are ok. That is why the test of making sure you can go to all of the websites from the box itself is important.

  4. It’s amazing to me how many people post different directions for this. I tested this one and it doesn’t work. If you add {HTTP_HOST} matches the pattern (lync.mydomain.com|meet.mydomain.com), it never matches the pattern! Even while you are adding it, you can test it. It doesn’t work!

    • Make sure you set it up for regular expression and not match. But this is a common and straight forward regex and does work. I often times I use (meet.*|dialin.*) for example to catch mutli-domain.

  5. This is PERFECT! I was able to implement this in under an hour for my Lync 2013 Enterprise deployment, and it works flawlessly after fight with TMG for days and giving up even thinking about UAG!
    My deployment is stretched across 2 sites with a VPN link between them. It contains an Edge server and 2008 R2 IIS box running AAG 3.0 beta (both non-domain joined) in the public-facing datacenter.
    I ended up stopping the Default Web Site and creating a new one, binding to the IP of the machine (versus All unassigned), and attaching my SSL cert to the site.
    I was able to leave the default AAR SSL rule it created alone, disabled the HTTP rule, and created a separate rule for lyncdiscover.
    I did have an issue where it would load the meet or dialin URL and give a 404 error trying to access the subpath (IE lwa/WebPages/LwaClient.aspx for the MEET url, and /Dialin/Conference.aspx for the DIALIN url). The IIS logs showed a 443, which was odd, and Failed Request Tracing got me nowhere. Turns out that, while I already had SSL loadbalancing disabled, I needed to reenable, apply, and disable. Once I did that, I’m not sure what magic it did, but everything started working.
    As a note on the proxy: I am doing 1800 seconds (I read on Dr Rez blog that mobility polls at 180 and 900 seconds) and the connection is pretty solid.

  6. Hello Im on a research to replace the TMG
    does AAR also work for all Exchange 2010/2013 Features

    autodiscover
    outlookanywhere
    ActiveSync

    happy to get any advise

    best Regards

    Manfred Preissner

    • Absolutely. Under the IIS root, modify Request Filtering and change the Maximum allowed content length to 2147483648. Leave the other two at 4096 and 2048.

      Thanks,

      Richard

  7. Amazing! This can be a particular of the most useful sites We’ve ever before appear over with this subject. Essentially Wonderful. I’m also an expert in this subject matter in order to comprehend your effort.

    • Marius – you just need to change ID to meet. Otherwise, everything else should be good.

    • Disregard – I was using server manager rather standalone iis I read somewhere there are issues which is why I did not see the other icon options

  8. I followed this all the way through, but when I’m testing internally (I edited my hosts file so i could test first before changing external DNS and everything else) against the reverse proxy all I get is the IIS default webpage. It’s as if the url rewrite isn’t working. Any ideas?

  9. Question…does the cert used on the ARR server for external URL have to be from a public CA? I am trying to test it before spending the money for the public cert but doesn’t seem to be working using cert created on internal CA. Internal CA is trusted by mobile devices…Meet and Dialin work fine. Even external url works for ABS and other tests. But mobility just keeps getting “unhandled alert type 300…bad gateway”. And Lync Connectivity Analyzer fails for mobility tests. But if it is not working with internal CA, shouldn’t that be the same result with a public cert?

    • Should work fine with an internal certificate if it’s trusted. For mobility, just make sure everything for IIS ARR is patched. I’ve even re-patched it when I thought it was right and suddenly it fixed it.

      • I cant get my mobility working, i’ve setup my link like above and lyncdiscover works but my lync sip uri not. My fault is:

        [29-3-2015 14:35:01] Created log file
        [29-3-2015 19:41:40] [DEBUG] Logging test parameters:
        [29-3-2015 19:41:40] [DEBUG] SIP Uri: cbrinkhoff@mooiland.nl
        [29-3-2015 19:41:40] [DEBUG] User Name: wmcbrinkhoff
        [29-3-2015 19:41:40] [DEBUG] Discovery Type: Manual Discovery
        [29-3-2015 19:41:40] [DEBUG] Server FQDN: lyncdiscover.mooiland.nl
        [29-3-2015 19:41:40] [DEBUG] Network access: NetworkAccessExternal
        [29-3-2015 19:41:40] [DEBUG] Selected client: ApplicationLyncMobile2013
        [29-3-2015 19:41:40] [SUBHEADING] Starting manual Lync server discovery
        [29-3-2015 19:41:40] [INFO] Please wait; this test may take several minutes to complete…
        [29-3-2015 19:41:40] [SUBHEADING] Starting server discovery for secure (HTTPS) channel
        [29-3-2015 19:41:40] [INFO] Server discovery started for https://lyncdiscover.mooiland.nl/Autodiscover/AutodiscoverService.svc/root
        [29-3-2015 19:41:40] [DEBUG] Sending HTTP request to https://lyncdiscover.mooiland.nl/Autodiscover/AutodiscoverService.svc/root?sipuri=cbrinkhoff@mooiland.nl
        [29-3-2015 19:41:40] [DEBUG] Cookie found in autodiscover response: StatusCode: 200, ReasonPhrase: ‘OK’, Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
        {
        Pragma: no-cache
        X-MS-Server-Fqdn: MLD-LYFE01.wm.local
        X-Content-Type-Options: nosniff
        Cache-Control: no-cache
        Server: Microsoft-IIS/8.5
        X-AspNet-Version: 4.0.30319
        X-Powered-By: ASP.NET
        X-Powered-By: ARR/2.5
        Date: Sun, 29 Mar 2015 17:42:10 GMT
        Content-Length: 1020
        Content-Type: application/vnd.microsoft.rtc.autodiscover+xml; v=1
        Expires: -1
        }
        [29-3-2015 19:41:40] [DEBUG] Parsing the response for URL https://lyncdiscover.mooiland.nl/Autodiscover/AutodiscoverService.svc/root?sipuri=cbrinkhoff@mooiland.nl. Full response:
        [29-3-2015 19:41:40] [DEBUG] Autodiscover URL https://lyncdiscover.mooiland.nl/Autodiscover/AutodiscoverService.svc/root?sipuri=cbrinkhoff@mooiland.nl redirected to https://lync.mooiland.nl/Autodiscover/AutodiscoverService.svc/root/user?originalDomain=mooiland.nl
        [29-3-2015 19:41:40] [DEBUG] Sending HTTP request to https://lync.mooiland.nl/Autodiscover/AutodiscoverService.svc/root/user?originalDomain=mooiland.nl?sipuri=cbrinkhoff@mooiland.nl
        [29-3-2015 19:41:40] [DEBUG] Cookie found in autodiscover response: StatusCode: 500, ReasonPhrase: ‘Internal Server Error’, Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
        {
        Date: Sun, 29 Mar 2015 17:42:10 GMT
        Server: Microsoft-IIS/8.5
        Content-Length: 75
        Content-Type: text/html
        }
        [29-3-2015 19:41:40] [DEBUG] Autodiscover: SendRequest(): the URL https://lync.mooiland.nl/Autodiscover/AutodiscoverService.svc/root/user?originalDomain=mooiland.nl?sipuri=cbrinkhoff@mooiland.nl couldn’t be connected. Complete HTTP headers:rn Date: Sun, 29 Mar 2015 17:42:10 GMT
        Server: Microsoft-IIS/8.5

        [29-3-2015 19:41:40] [ERROR] Couldn’t connect to URL https://lync.mooiland.nl/Autodiscover/AutodiscoverService.svc/root/user?originalDomain=mooiland.nl?sipuri=cbrinkhoff@mooiland.nl
        [29-3-2015 19:41:40] [ERROR] Reason: Internal server error (HTTP status code 500)
        [29-3-2015 19:41:40] [DEBUG] System.Exception: Exception of type ‘System.Exception’ was thrown.
        at Microsoft.LyncServer.WebServices.AutoDiscoverManager.TerminateAD(String mesg)
        at Microsoft.LyncServer.WebServices.AutoDiscoverManager.d__d.MoveNext()
        — End of stack trace from previous location where exception was thrown —
        at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
        at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
        at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
        at Microsoft.LyncServer.WebServices.AutoDiscoverManager.d__3.MoveNext()
        — End of stack trace from previous location where exception was thrown —
        at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
        at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
        at Microsoft.LyncServer.WebServices.AutoDiscoverManager.d__16.MoveNext()
        — End of stack trace from previous location where exception was thrown —
        at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
        at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
        at Microsoft.LyncServer.WebServices.AutoDiscoverManager.d__3.MoveNext()
        — End of stack trace from previous location where exception was thrown —
        at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
        at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
        at Microsoft.LyncServer.WebServices.AutoDiscoverManager.d__0.MoveNext()
        — End of stack trace from previous location where exception was thrown —
        at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
        at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
        at LyncConnectivityAnalyzerCore.Utilities.d__3e.MoveNext()
        [29-3-2015 19:41:40] [INFO] Total server discovery time: 0,3 seconds
        [29-3-2015 19:41:40] [SUMMARY_ERROR] Server discovery failed for secured channel against https://lyncdiscover.mooiland.nl/Autodiscover/AutodiscoverService.svc/root
        [29-3-2015 19:41:40] [INFO] Server discovery ended for https://lyncdiscover.mooiland.nl/Autodiscover/AutodiscoverService.svc/root
        [29-3-2015 19:41:40] [DEBUG] None, AutoInternalDNSFail, AutoExternalDNSFail, AutoInternalSecureD, AutoInternalUnsecureD, AutoExternalSecureD, AutoExternalUnsecureD, ManualSecureD, ManualUnsecureD, AuthBrokerInternalLMXCheckGET, AuthBrokerInternalLMXCheckPOST, AuthBrokerExternalLMXCheckGET, AuthBrokerExternalLMXCheckPOST, MobilityMCXInternalLMXCheckGET, MobilityMCXInternalLMXCheckPOST, MobilityMCXExternalLMXCheckGET, MobilityMCXExternalLMXCheckPOST, LMXSIPServerInternalDNS, LMXSIPServerExternalDNS, MobilityUCWAInternalCheckPOST, MobilityUCWAExternalCheckPOST
        [29-3-2015 19:41:40] [SUMMARY]
        [29-3-2015 19:41:40] [SUMMARY_ERROR] Server discovery failed using lyncdiscover.mooiland.nl. Please verify the server requirements at http://go.microsoft.com/fwlink/?LinkId=278998
        [29-3-2015 19:41:40] [SUMMARY_ERROR]
        Microsoft Lync Connectivity Analyzer cannot analyze deployment readiness until a discovery test has completed successfully.

  10. One last question (hopefully!). Several times Microsoft makes mention that “A prerequisite for the Lync mobility component is that the Front End pool internal web FQDN must be distinct from the Front End pool external web FQDN.” I am using Standard edition server so the internal web FQDN defaults to the internal pool name. Given that there is a distinct external web services FQDN defined, isn’t this requirement met? Or do you think I should still define an Internal Web Services FQDN that is not the pool name?

    Thanks!

    • You are fine. The reason they need to be different is that you should have an internal DNS record for your external web FQDN pointing to the public IP address of your external web services.

  11. hello
    can you help me
    i try to configure IIS ARR for external acces to exchange server 2013
    i have ise this link :
    http://blogs.iis.net/erez/archive/2013/11/27/installing-arr-manually-without-webpi.aspx

    http://www.microsoft.com/web/downloads/platform.aspx

    http://blogs.technet.com/b/exchange/archive/2013/07/19/reverse-proxy-for-exchange-server-2013-using-iis-arr-part-1.aspx

    http://y0av.me/2013/07/22/lync2013_iisarr/

    but impossible to pass healt check

    i have this error :

    result : failed
    détails : cannot connect to the server. The HTTP status code is 0 and the error code is 80072F8F

    can you help me?

LEAVE A RESPONSE

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