Friday, August 29, 2014

Certificate requirements for internal Lync servers

Lync is quiet strict in certificate validation. If you assign a non compatible certificate to Lync it will run into serious issues.
This is most likely happen if you are using dedicated certificate for each Lync service.
Especially the Lync WebServiceInternal certificate cannot be requested correctly, neither with Lync Wizard nor with the Request-CsCertificate command.

Here the problem is that both methods are requesting a certificate with a Subject Name of the Internal Web Services rather than the POOL FQDN.

Lync BUG:
The remote certificate is invalid according to the validation procedure. reason="The web ticket is invalid." ;faultcode="wsse:InvalidSecurityToken",Replace=false

In both, the TechNet and Help File the correct certificate is described. Therefore you need a valide process of requesting the correct certificate.

If you have a consolidated certificate for all services, this is issue is not present, because the Subject Name responds to the POOL FQDN.

Here I post a SNOOPER Tracing of what's happen with the wrong certificate:

You can simple test this by running the Test-CsAddressbookService command-let

Further Information:
Internal Certificate Deployment in Lync 2013 - How to and planning

Download Script: Requesting internal Lync Server Certificates


SUPPORT TEST Result:

The test has successfully demonstrated the issue we expected:
The Test failed with 401 unauthorized error


The Snooper Analysis has shown the following errors:
TL_ERROR(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.924.00352017 (WebInfrastructure,RemoteCertificateResolver.ResolveCertificate:remotecertficateresolver.cs(82))
(00000000030603DB)AuthenticationException. Remote certificate is not valid. <hostName, lyncfe01.customer.com> <port,443> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.


TL_INFO(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.924.00352018 (WebInfrastructure,WebTicketRemoteSecurityTokenStore.EnsureIssuerSecurityToken:webticketremotesecuritytokenstore.cs(315))
(000000000289B908)Unable to ensure we have the SecurityToken from issuer lyncfe01.customer.com, port 443.

TL_VERBOSE(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.925.0035201e (WebInfrastructure,WebTicketRemoteSecurityTokenStore.GetSigningTokenByThumbprint:webticketremotesecuritytokenstore.cs(84))
(000000000289B908)Did not find <thumbprint, 12DF9402A814C3C5F099D3E1974959F5037FA8B8> in dictionary.

TL_INFO(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.925.0035201f (WebInfrastructure,WebTicketKeyStore.GetSigningTokenByThumbprint:webticketkeystore.cs(188))
Token not found in remote token store. <thumbprint, 12DF9402A814C3C5F099D3E1974959F5037FA8B8>

TL_ERROR(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.925.00352020 (WebInfrastructure,OCSAuthModule.BeginAuthenticateUser:iismodule.cs(827))[2147532902]
Exception: System.IdentityModel.Tokens.SecurityTokenException: Unable to resolve SecurityKeyIdentifier found in the SamlAssertion signature. The SamlAssertion signature can not be validated for the Issuer https://lyncfe01.customer.com/367030ae-f2fb-5c02-be25-fe905fabf83c.
   at System.IdentityModel.Tokens.SamlAssertion.ReadSignature(XmlDictionaryReader reader, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver, SamlSerializer samlSerializer)   at System.IdentityModel.Tokens.SamlAssertion.ReadXml(XmlDictionaryReader reader, SamlSerializer samlSerializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)   at System.IdentityModel.Tokens.SamlSerializer.LoadAssertion(XmlDictionaryReader reader, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)   at System.IdentityModel.Tokens.SamlSerializer.ReadToken(XmlReader reader, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)   at System.ServiceModel.Security.WSSecurityJan2004.SamlTokenEntry.ReadTokenCore(XmlDictionaryReader reader, SecurityTokenResolver tokenResolver)   at System.ServiceModel.Security.WSSecurityTokenSerializer.ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)   at Microsoft.Rtc.Internal.WebServicesAuthFramework.OCSWebTicketCredentials.ExtractInstance(HttpRequest request)   at Microsoft.Rtc.Internal.WebServicesAuthFramework.OCSAuthModule.BeginAuthenticateUser(Object sender, EventArgs e, AsyncCallback cb, Object state)

 
TL_ERROR(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.925.00352021 (WebInfrastructure,OCSAuthModule.BeginAuthenticateUser:iismodule.cs(940))[2147532902]
Exception: Microsoft.Rtc.Internal.WebServicesAuthFramework.WebTicketHttpHeaderException: Exception of type 'Microsoft.Rtc.Internal.WebServicesAuthFramework.WebTicketHttpHeaderException' was thrown.
   at Microsoft.Rtc.Internal.WebServicesAuthFramework.OCSAuthModule.BeginAuthenticateUser(Object sender, EventArgs e, AsyncCallback cb, Object state)
 

TL_INFO(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.925.00352023 (WebInfrastructure,OCSAuthModule.SetHttpResponseOnException:iismodule.cs(1572))[2147532902]
Set response for exception: Microsoft.Rtc.Internal.WebServicesAuthFramework.WebTicketHttpHeaderException: Exception of type 'Microsoft.Rtc.Internal.WebServicesAuthFramework.WebTicketHttpHeaderException' was thrown.
   at Microsoft.Rtc.Internal.WebServicesAuthFramework.OCSAuthModule.BeginAuthenticateUser(Object sender, EventArgs e, AsyncCallback cb, Object state)
 

TL_ERROR(TF_COMPONENT) [0]98CC.054C::08/26/2014-09:21:30.925.00352024 (WebInfrastructure,OCSAuthModule.AddMsDiagnosticsHeader:iismodule.cs(1714))[2147532902]
Add Ms-diagnostics header <errorId,28032> <reason,The web ticket is invalid.>

TL_INFO(TF_IIS) [0]98CC.054C::08/26/2014-09:21:30.925.00352026 (WebInfrastructure,CAuthHelperGlobalModule::DumpEvent:SslNgMod.cpp(16))[2147532902]
Event: 3a2a4e84-4c21-4981-ae10-3fda0d9b0f83:0x0:56:GENERAL_SET_RESPONSE_HEADER, httpContext: 000000BFCEDFFE70, items: ContextId=null,HeaderName=X-Ms-diagnostics,HeaderValue=28032;source="lyncfe01.customer.com";reason="The web ticket is invalid.";faultcode="wsse:InvalidSecurityToken",Replace=false

TL_INFO(TF_IIS) [0]98CC.054C::08/26/2014-09:21:30.926.0035202a (WebInfrastructure,CAuthHelperGlobalModule::DumpEvent:SslNgMod.cpp(16))[2147532902]
Event: 3a2a4e84-4c21-4981-ae10-3fda0d9b0f83:0x0:56:GENERAL_SET_RESPONSE_HEADER, httpContext: 000000BFCEDFFE70, items: ContextId=null,HeaderName=X-MS-WebTicketURL,HeaderValue=https://lyncfe01.customer.com/WebTicket/WebTicketService.svc,Replace=false

TL_INFO(TF_IIS) [0]98CC.054C::08/26/2014-09:21:30.926.0035202d (WebInfrastructure,CAuthHelperGlobalModule::DumpEvent:SslNgMod.cpp(16))[2147532902]
Event: 3a2a4e84-4c21-4981-ae10-3fda0d9b0f83:0x0:56:GENERAL_SET_RESPONSE_HEADER, httpContext: 000000BFCEDFFE70, items: ContextId=null,HeaderName=X-MS-WebTicketSupported,HeaderValue=cwt,saml,Replace=false

If you wish requesting correct certificate, please copy and use my script:


#****************************************************
#*      (c) Thomas Poett, Microsoft MVP Lync        *
#*      contact: thomas.poett@live.de               *
#*                                                  *
#*  Frontend and Director Pool Certificate Request  *
#*           Version 1.2, 25.09.2014                *
#*                                                  *
#*      Certificate Template must be WebServer      *
#*      You need to know your internal CA Name      *
#*     or write script request into a local file    *
#*     Run Script for each required Certificate     *
#*                                                  *
#*    included are Scheduler + ucupdates-r2 FQDN    *
#*                                                  *
#*                                                  *
#* Not for Edge Server certificates                 *
#*                                                  *
#* BUG Warning:                                     *
#* Lync Wizard and Request-Certificate for Internal *
#* Web Service request a certificate with a wrong   *
#* Certificate Subject Name.                        *
#* This script addresses this issue correctly!      *
#****************************************************

#Collect necessary user input
$CAOnline = Read-Host 'Do you request your certificate online (Y/N)'
if ($CAOnline -eq 'Y')
    {
  $CA = Read-Host 'Certificate Authority (Format FQDN\CA)'
    }
   else
    {
  $Folder = Read-Host 'Folder e.g C:\CERTS (this folder must exist)'
    }

$Organization = Read-Host 'ORGANIZATION'
$City = Read-Host 'CITY'
$State = Read-Host 'STATE'
$Country = Read-Host 'COUNTRY (2 characters, e.g. DE)'
$ComputerName = $env:ComputerName+"."+$env:userdnsdomain

#Lync PS command gaining topology information
$SIP_ALL = Get-CsSipDomain | Select-Object -ExpandProperty Identity
$SIPString = ""
foreach($tempUrl in $SIP_ALL)
    {
 $SIPString = $SIPString + $tempUrl+","
    }

$SIPString = $SIPString.Substring(0,$SIPString.Length-1)
$DefaultSIPDomain = Get-CsSipDomain | where {$_.IsDefault -eq $true} | Select-Object -ExpandProperty Name

#Choose Certificate Type
$CertType = Read-Host 'Please provide the following number for the Certificate Service: 1= Default, 2= WebServicesInternal, 3= WebServiceExternal, 4= Consolidated, 5=WebServicesInternal+External'

  if ($CertType -eq 1)
      {
   Write-Host "Certificate is DEFAULT"
      $AutoLogin = Read-Host 'Auto Login Server for SIP? Y/N'
      if ($AutoLogin -eq "Y") 
          {
          #SIP.<SIPDomain> for AutoLogin
          $SANString =""
          foreach($tempUrl in $SIPString)
              {
              $SANString = $SANString + "SIP."+$tempUrl +","
              }
          $SANString = $SANString.Substring(0,$SANString.Length-1)
          Write-Host "Your SAN:"+$SANString
          if ($CAOnline -eq 'Y')
              {
     $CERT = Request-CsCertificate -New -Type Default -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync Default" -Template WebServer -AllSipDomain -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
              Set-CsCertificate -Type Default -Thumbprint $CERT.Thumbprint
              }
    else
              {
     $CAOnlineName = $Folder + '\' + $ComputerName + '-Default.cer'
     $CERT = Request-CsCertificate -New -Type Default -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync Default" -Template WebServer -AllSipDomain -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
     }
          }
      else
       {
       #SIP.<SIPDomain> for NON AutoLogin
       Write-Host "Your SAN:" $SANString
    if ($CAOnline -eq 'Y')
           {
        $CERT = Request-CsCertificate -New -Type Default -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync Default" -Template WebServer -AllSipDomain -City $City -Country $Country -Organization $Organization -State $State
           Set-CsCertificate -Type Default -Thumbprint $CERT.Thumbprint
           }
          else
           {
           $CAOnlineName = $Folder + '\' + $ComputerName + '-Default.cer'
           $CERT = Request-CsCertificate -New -Type Default -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync Default" -Template WebServer -AllSipDomain -City $City -Country $Country -Organization $Organization -State $State
           }
        }
      }
   
 elseif ($CertType -eq 2)
      {
   Write-Host "Certificate is WebServicesInternal"
      #Lync PS command gaining topology information
      $SURL_ALL = Get-CsSimpleUrlConfiguration | Select-Object -ExpandProperty SimpleUrl | Select-Object -ExpandProperty ActiveUrl
      $SANString = ""
      foreach($tempUrl in $SURL_ALL)
          {
          $SANString = $SANString + $tempUrl.replace("https://","") +","
          }
#     $surlString = $SANString.Substring(0,$SANString.Length-1)
      $SANString = $SANString + "scheduler."+$DefaultSIPDomain +"," + ",ucupdates-r2."+$DefaultSIPDomain
           
      #LYNCDISCOVERINTERNAL
      foreach($tempUrl in $SIPString)
          {
          $SANString = $SANString + ",LyncDiscoverInternal."+$tempUrl
          }
          Write-Host "Your SAN:" + $SANString
          if ($CAOnline -eq 'Y')
              {
     #The type of DEFAULT is correct, the internal WebServiceFQDN must have a Subject Name (SN) of the Pool!! This is the described bug
     $CERT = Request-CsCertificate -New -Type Default -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync WebServices Internal" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
              Set-CsCertificate -Type WebServicesInternal -Thumbprint $CERT.Thumbprint
              }
    else
              {
     $CAOnlineName = $Folder + '\' + $ComputerName + '-WebServicesInternal.cer'
     $CERT = Request-CsCertificate -New -Type Default -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync WebServices Internal" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
           }
      }
     
     elseif ($CertType -eq 3)
          {
          Write-Host "Certificate is WebServicesExternal"
          #Lync PS command gaining topology information
          $SURL_ALL = Get-CsSimpleUrlConfiguration | Select-Object -ExpandProperty SimpleUrl | where {$_.Component -ne "Cscp"} | Select-Object -ExpandProperty ActiveUrl
          $SANString = ""
          foreach($tempUrl in $SURL_ALL)
              {
              $SANString = $SANString + $tempUrl.replace("https://","") +","
              }

          #Get External Web Service FQDN
          $Pool =  Get-CsPool | where {$_.Computers -eq $ComputerName}
          $WebServer = "WebServer:"+$Pool.Identity
          $ExtWebSrvURL = Get-CsService -Identity $WebServer | Select-Object -ExpandProperty ExternalFqdn
          $SANString = $SANString + $ExtWebSrvURL
           
          #LYNCDISCOVER
          foreach($tempUrl in $SIPString)
              {
              $SANString = $SANString + ",LyncDiscover."+$tempUrl
              }
          Write-Host "Your SAN:" + $SANString           
          if ($CAOnline -eq 'Y')
              {
              $CERT = Request-CsCertificate -New -Type WebServicesExternal -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync WebServices External" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
              Set-CsCertificate -Type WebServicesExternal -Thumbprint $CERT.Thumbprint
              }
    else
              {
     $CAOnlineName = $Folder + '\' + $ComputerName + '-WebServicesExternal.cer'
     $CERT = Request-CsCertificate -New -Type WebServicesExternal -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync WebServices External" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
              }         
          }

     elseif ($CertType -eq 4)
          {
          Write-Host "Certificate is Consolidated"
           
          #Lync PS command gaining topology information
          $SURL_ALL = Get-CsSimpleUrlConfiguration | Select-Object -ExpandProperty SimpleUrl | Select-Object -ExpandProperty ActiveUrl
          $SANString = ""
          foreach($tempUrl in $SURL_ALL)
              {
              $SANString = $SANString + $tempUrl.replace("https://","") +","
              }
#         $surlString = $SANString.Substring(0,$SANString.Length-1)
          $SANString = $SANString + "scheduler."+$DefaultSIPDomain + ",ucupdates-r2."+$DefaultSIPDomain
           
          #LYNCDISCOVER and LYNCDISCOVERINTERNAL
          foreach($tempUrl in $SIPString)
              {
              $SANString = $SANString + ",LyncDiscoverInternal."+$tempUrl + ",LyncDiscover."+$tempUrl
              }
           
          #Get External Web Service FQDN
          $Pool =  Get-CsPool | where {$_.Computers -eq $ComputerName}
          $WebServer = "WebServer:"+$Pool.Identity
          $ExtWebSrvURL = Get-CsService -Identity $WebServer | Select-Object -ExpandProperty ExternalFqdn
          $SANString = $SANString + "," + $ExtWebSrvURL
                      
          $AutoLogin = Read-Host 'Auto Login Server for SIP? Y/N'
          if ($AutoLogin -eq "Y") 
             {
             #SIP.<SIPDomain> for AutoLogin
             foreach($tempUrl in $SIPString)
                 {
              $SANString = $SANString + ",SIP."+$tempUrl
                 }
             Write-Host "Your SAN:" + $SANString               
             if ($CAOnline -eq 'Y')
                 {
     $CERT = Request-CsCertificate -New -Type Default,WebServicesInternal,WebServicesExternal -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync Consolidated" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
                 Set-CsCertificate -Type Default,WebServicesInternal,WebServicesExternal -Thumbprint $CERT.Thumbprint
                 }
    else
                 {
     $CAOnlineName = $Folder + '\' + $ComputerName + '-Consolidated.cer'
     $CERT = Request-CsCertificate -New -Type Default,WebServicesInternal,WebServicesExternal -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync Consolidated" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
                 }
             }
            else
             {
             Write-Host "Your SAN:" + $SANString
          if ($CAOnline -eq 'Y')
                 {
        $CERT = Request-CsCertificate -New -Type Default,WebServicesInternal,WebServicesExternal -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync Consolidated" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
                 Set-CsCertificate -Type Default,WebServicesInternal,WebServicesExternal -Thumbprint $CERT.Thumbprint
                 }
    else
                 {
     $CAOnlineName = $Folder + '\' + $ComputerName + '-Consolidated.cer'
     $CERT = Request-CsCertificate -New -Type Default,WebServicesInternal,WebServicesExternal -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync Consolidated" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
                 }
              }       
          }
    

     elseif ($CertType -eq 5)
          {
          Write-Host "Certificate is WebServicesInternal+External"
           
          #Lync PS command gaining topology information
          $SURL_ALL = Get-CsSimpleUrlConfiguration | Select-Object -ExpandProperty SimpleUrl | Select-Object -ExpandProperty ActiveUrl
          $SANString = ""
          foreach($tempUrl in $SURL_ALL)
              {
              $SANString = $SANString + $tempUrl.replace("https://","") +","
              }
#         $surlString = $SANString.Substring(0,$SANString.Length-1)
          $SANString = $SANString + "scheduler."+$DefaultSIPDomain + ",ucupdates-r2."+$DefaultSIPDomain
           
          #LYNCDISCOVER and LYNCDISCOVERINTERNAL
          foreach($tempUrl in $SIPString)
              {
              $SANString = $SANString + ",LyncDiscoverInternal."+$tempUrl + ",LyncDiscover."+$tempUrl
              }
           
          #Get External Web Service FQDN
          $Pool =  Get-CsPool | where {$_.Computers -eq $ComputerName}
          $WebServer = "WebServer:"+$Pool.Identity
          $ExtWebSrvURL = Get-CsService -Identity $WebServer | Select-Object -ExpandProperty ExternalFqdn
          $SANString = $SANString + "," + $ExtWebSrvURL
           
          Write-Host "Your SAN:" + $SANString 
    if ($CAOnline -eq 'Y')
              {  
              $CERT = Request-CsCertificate -New -Type WebServicesInternal,WebServicesExternal -ComputerFqdn $ComputerName -CA $CA -FriendlyName "Lync Consolidated" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
              Set-CsCertificate -Type WebServicesInternal,WebServicesExternal -Thumbprint $CERT.Thumbprint
              }
    else
              {
     $CAOnlineName = $Folder + '\' + $ComputerName + '-WebServicesInternalExternal.cer'
     $CERT = Request-CsCertificate -New -Type WebServicesInternal,WebServicesExternal -ComputerFqdn $ComputerName -Output $CAOnlineName -FriendlyName "Lync Consolidated" -Template WebServer -DomainName $SANString -City $City -Country $Country -Organization $Organization -State $State
              }
          }

    else {"Your input is not valid! - EXIT"}
if ($CAOnline -eq 'Y')
    {
    Write-Host "Your certificate as requested is assigned (as fare you didn't saw any related issues)."
    }
   else
    {
    Write-Host "Your Certificate Request is stored here: " $CAOnlineName
    Write-Host "Please order the Certificate and assign it manually to the Lync Services"
    }

if ($CAOnline -eq 'Y')
    {
 $RestartLyncService = Read-Host 'You need to restart your Lync Services? Y/N'
 if ($RestartLyncService -eq "Y") 
        {
  Stop-CsWindowsService
  Start-CsWindowsService
  }
    else {Write-Host {"Please RESTART your Lync Servises (Stop-CsWindowsService and Start-CsWindowsService."}}}

# ++++ END of SCRIPT ++++





Tuesday, August 26, 2014

Lync Edge Server or Pool Server have uncommon call drops (30sec) and other IM issues

I came across a funny problem.
I customer complained if a federated call was initiated the call always dropped after exactly 31 seconds.
What we could figure out was this error message.

SIP/2.0 504 Server time-out
ms-client-diagnostics: 52085;reason="Dialog does not exist"

So I continued the analysis and traced the calls. With the wonderful tool SNOOPER, I was able to get a see the "Call Flow Windows", which is really a very helpful visualization of the exact package flow.
I saw now the SIP Session was initiated correctly.

BUT
I figured out, the PRACK message was not acknowledge, so the ACK 200/OK was missing. Even the message was send correctly the target host. "This is a kind of early media." The Voice stream is established, but need to be reconfirmed in case some port/ parameter should be changes/ optimized.
Since the ACK is missing, truly the Lync Server must think the call has ended and actively dropped it.

This all is happened on the EDGE Server of the affected customer.
I really struggled, as I also analyzed the HOSTS file on the EDGE Server.

So far ones I run the IPCONFIG /DISPLAYDNS command and saw something strange. After some DNS entries I have seen wired characters. Especially behind the Frontend FQDNs.

SOLUTION:
The HOSTS File was not written proper and contain invisible characters behind the FQDNs. So Lync EDGE Server was not able finding the Frontend Servers via DNS and therefore the PRACK request from the Frontend could not be ACK acknowledged.  



Lync 2013 Reverse Proxy Solution with IIS ARR (Application Request Routing) - Instllation and Consulting guide

Before we start with the Lync 2013 Reverse Proxy solution design and setup guide. I want to keep some supportability statement in mind.

By today, the date of writing this blog, Microsoft has to supported solutions.
1. Microsoft TMG (Thread Management Gateway) - if the TMG was purchased before the EOL date.
2. Microsoft IIS ARR (Internet Information Server Application Request Routing)

A third solution, the Microsoft Web Application Proxy introduced with Windows Server 2012 R2 is not jet supported. This is due to the reason that WAP has a problem with multiple SIP Domains, meaning here: I cannot handle requests other than for the primary SIP domain, especially the MEET simple URL.

The configuration guide here runs through the entire process from the ISS ARR Installation and setup. The guide concentrates on Windows Server 2012 R2. Therefore we need to understand the installation process for IIS ARR first. ARR cannot simply installed by download the MSI package, rather than using the Web Installer. If you are going to use the Standalone installer, you need to distribute it.

(Typical Lync Reverse Proxy Design with IIS ARR)

Since this we are focusing on Windows Server 2012 R2, the IIS ARR Version described here is: Version 3.0

Download Links:
Web Platform Installer: http://www.microsoft.com/web/gallery/install.aspx?appid=ARRv3_0
Installer Package: http://www.microsoft.com/en-us/download/details.aspx?id=39715

Install Application Request Routing 3.0:

Before we install ARR, we need to have the following prerequisites installed for IIS.

Therefore we install with this command (WIN 2012 +  2012 R2)

Import-Module ServerManager
Add-WindowsFeature Web-Static-Content,Web-Default-Doc,Web-Dir-Browsing,Web-Http-Errors,Web-Net-Ext,Web-Http-Logging,Web-Request-Monitor,Web-Http-Tracing,Web-Filtering,Web-Stat-Compression,Web-Mgmt-Console,NET-Framework-Core,AS-Web-Support,NET-Non-HTTP-Activ,NET-HTTP-Activation,Web-Server


Comment:
You might find the RSAT-Web-Server and the NET-Win-CFAC Feature added, this is a setup on Windows Server 2008 with .NET 3.5.1
Windows Communication Foundation Activation ComponentsWindows Communication Foundation Activation ComponentsNET-Win-CFAC


Fully automated Installation:
If you want a installation without all this manual feature setup, simply download the Microsoft Web Platform Installer (PI): download here

 
If you should have the ARR Installer, it looks this.
 
If you try using the Installer, you might run into an error: Web Farm Framework is a requisite for installing Application Request Routing. (PI is installing Web Farm Framework automatically)

 
Us the PI and start over again:
 
The PI provides several options and make sure you select ARR version 3.0:
 
 
 

Once the installation has ended, you might restart the server and please ensure the Windows 2012 R2 server is fully patched/ updated.

Next we start configuring ARR according to the need.
In our case here, we have multiple SIP domains. As in Lync recommended, you will have multiple MEET pages, a single DIALIN page and the Lync Web Service, as well as the LYNCDISCOVER URLs. It is necessary regarding Lync Mobility Service, that your internal Deployment allows the internal Mobile device to connect the mobility service via the IIS ARR.
This is not part of this article, but keep in mind the mobility service URL is related to the external Web Service FQDN.

Configuration of IIS ARR (Application Request Routing):

First Step is creating the Server Farm. this is not related to a IIS Farm of Servers, e.g. which you might create for HA/ redundancy.
A Farm is related to the URL you are going to publish.

Since Lync simple URL publishing does not require any SSL Offloading if you have the External Web Site in Lync assigned with a Public Certificate, you do not need a certificate installed on the IIS.
Most likely you have assigned a private certificate from your internal Certificate Authority, you have to assign the IIS ARR an public certificate and reencrypt the traffic for internal use.
Be aware of two point here:
1. this is called SSL Offloading and requires some extra CPU load on your server
2. IIS must not be "domain joined" therefore you need to have the internal Certificate authority Root Certificates assigned as TRUSTED !

Also you must be aware of the TCP Port redirection.
for HTTP request redirect 80 -> 8080
for HTTPS request redirect 443 -> 4443

Lync internal IIS has two web site identified, Lync internal and external Web Services. The web sites are assigned with different ports, while the external service interact with 8080 and 4443.

You have to repeat all the following steps for each simple URL!


 Identify the simple URL here:


Ensure you have set the internal TCP Ports (8080 and 4443):
Remember, you must specify the HTTP port too. If you don't want to expose HTTP to the Internet, you have to restrict this on you fronting Firewall.


Set MEMORY CACHE DURATION to: 60 seconds:

HTTP version is: PASS THROUGH and Time-Out is set to 200 seconds:

Routing: is defined as use URL Rewrite to inspect incoming requests:

Choose your public certificate for this service:

Repeat this steps for all Simple URLs.


Finally assure all URLs are defined and have the correct settings:


Next step should be a Test. I prefer the Dialin web page, since I doesn't require an immediate login. If you can see this page, try also if you can login. This will ensure you that the Web Ticket Service in Lync is working correctly too.


Last but not least, you can validate all URL Requites if you click to the IIS Root and click Rewrite:


 So far this is the entire configuration for Lync 2013 and Lync 2010 too. If you need to publish other service e.g. Exchange, you might choose similar settings. In Exchange you might want to use a pre-authentication, which Lync 2013 does not require.

Happy IIS ARR setup ;)