May, 2014

  • Testing Exchange EWS service accessibility from internal/external Lync clients

    Hi there,

     

    In this blog post, I’ll be talking about a powershell based tool which could be used to check Exchange EWS connectivity especially from external Lync clients where the client might not be able to access Exchange EWS and hence cannot display meetings and similar.

     

    The powershell tool uses Exchange EWS managed API to connect to EWS service, so you have to install the API to the test client from where you plan to run the script. The library could be downloaded at:

     

    http://www.microsoft.com/en-us/download/details.aspx?id=35371 Microsoft Exchange Web Services Managed API 2.0

     

    => Here is the full Powershell script source:

     

     

    #------------SCRIPT STARTS HERE-----------

     

     

     

            function TestExternalEWS () {

     

            param($emailaddress)

     

            Add-Type -Path 'C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll'

         

            write-host "=> Retrieving your credentials for EWS connectivity (Domain\User and password) ..." -ForegroundColor Green

            $autod = New-Object Microsoft.Exchange.WebServices.Autodiscover.AutodiscoverService

           

     

            $Credentials = (Get-Credential)

           

           

           

            $creds=New-Object System.Net.NetworkCredential($Credentials.UserName.ToString(),$Credentials.GetNetworkCredential().password.ToString())

     

            $autod.Credentials=$creds

            $autod.EnableScpLookup = $false

            $autod.RedirectionUrlValidationCallback = {$true}

            $autod.TraceEnabled = $TraceEnabled

           

            write-host "=> Retrieving external EWS URL via Autodiscover service for the given smtp address '$emailaddress' ..." -ForegroundColor Green

            Write-Host

           

           

                    try {

           

                    $response = $autod.GetUserSettings(

                          $emailaddress,

                          [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalMailboxServer,

                          [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEcpUrl,

                          [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEwsUrl,

                          [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalOABUrl,

                          [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalUMUrl,

                          [Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalWebClientUrls

                        )

     

     

                    $ExternalEwsUrl = $response.Settings[[Microsoft.Exchange.WebServices.Autodiscover.UserSettingName]::ExternalEwsUrl]

               

                     

                     if($ExternalEwsUrl -eq $NULL) {

               

                        write-host "=> Successfully contacted Exchange Autodiscover service but couldn't retrieve autodiscovery settings for the given smtp address '$emailaddress'" -ForegroundColor Red

                        write-host "Error code:" $response.errormessage

                        Write-Host 

                        return

                        }

                   

                        else {

                       

                        write-host "=> Successfully contacted Autodiscover service and retrieved the external EWS URL for the given smtp address '$emailaddress'" -ForegroundColor green

                        write-host  $externalEwsUrl -ForegroundColor Magenta

                        write-host

                       

                        }

                   

                   

                   

            

                    }

     

                    catch {

     

     

     

                    write-host "There was an error calling GetUserSettings() function, the error returned:" $_.exception.message -ForegroundColor Red

                    Write-Host

                    return

           

                    }

     

    # Got the EWS URL information, now testing EWS access

     

     

                    write-host "=> Now making a test call (retrieving given user's OOF settings) to Exchange Web Service to test external EWS connectivity against '$externalEwsUrl' ..." -ForegroundColor Green

                    Write-Host

           

                   $service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

                   $uri=[system.URI] $ExternalEwsUrl

                   $service.Url = $uri

           

                    try {

                       

                        $oofsettings = $service.GetUserOofSettings($emailaddress)

                        Write-host "=> Successfully retrieved OOF settings." -foreground Green

                        write-host

                        write-host "OOF state: " $oofsettings.State

                        write-host "External OOF reply:"

                        write-host "============================================================================================================================================="

                        write-host $oofsettings.externalreply

                        write-host "============================================================================================================================================="

           

                    }

     

                    catch {

     

                        write-host "There was an error calling GetUserOofSettings() function, the error returned:" $_.exception.message -ForegroundColor Red

                        Write-Host

                    }

           

     

                    }

     

     

                    if(($args[0] -eq $NULL))

     

                    {

     

                    Write-host "Please specify e-mail address to test"

                    Write-Host "Example: testlyncext.ps1 user-email@contoso.com"

                   

                    return

     

                    }

     

     

                   

                    $emailaddress = $args[0]

     

                    TestExternalEWS($emailaddress)

     

     

     

    #------------SCRIPT ENDS HERE-----------

     

     

    You can find a sample output below:

     

     

    Hope this helps

    Thanks,

    Murat

     

  • Testing Lync server 2013 external URLs from Lync clients (external web service, meet, office webapps, lyncdiscover and dialin)

    Hi there,

     

    In this blog post, I’ll be talking about another powershell based tool which could be used to check name resolution, TCP layer connectivity and SSL/TLS negotiation for various URLs that are accessible through your reverse proxy which are:

     

    - External web services URL

    - Meet URL

    - Office WebApps URL

    - Lyncdiscover URL

    - Dialin URL

     

    When the tool is run, you can see if the given URL is successfully resolved to a name. If name resolution succeeds, a TCP session is attempted to the target and if the TCP session successfully created an SSL/TLS negotiation is done with the target URL. And if that succeeds as well, the subject names and subject alternative names in the returned certificate are dumped by the tool. Also expiration time of each certificate is dumped.

     

    You’ll have to provide each URL manually (actually I was planning to read all such details from topology file removing the requirement of manual input but due to some issues I decided to enter the information manual for the time being).

     

    Such URL details could be easily obtained from your topology file:

     

    - External Web services FQDN:


     

    - Meet URL and Dialin URL:


     

    - Office WebApps URL:


     

    - Lyncdiscover URL:

    Lyncdiscover URL is built by adding “lyncdiscover” to the beginning of sip domain. Examples:

     

    lyncdiscover.contoso.com

    lyncdiscover.fabrikam.com

     

    => Here is the full Powershell script source:

     

     

    #------------SCRIPT STARTS HERE-----------

     

     

    # ResolveLyncNames() resolves various Lync names to IP addresses

     

    function ResolveLyncNames {

     

    param($LyncFqdn)

     

    try {

    $ipaddr   = [System.Net.Dns]::GetHostAddresses($LyncFqdn)

    Write-Host "Successfully resolved $LyncFqdn to $ipaddr" -ForegroundColor Green

    return 1

    }

     

    catch {

     

    $exception = New-Object system.net.sockets.socketexception

    $errorcode = $exception.ErrorCode

    write-host "Name resolution failed, error code:$errorcode" -foregroundcolor Red

    write-host "Error details: $exception" -foregroundcolor Red

    return 0

    }

     

     

     

    }

     

     

    # TLSConnectionCheck() establishes TLS session to remote host at the given TCP port

     

    function TLSConnectionCheck  {

     

    param ($remotehost,$remoteport)

     

    try {

     

    $TCPConnection = New-Object System.Net.Sockets.Tcpclient($remotehost, $remoteport)

    Write-Host "TCP connection has succeeded" -ForegroundColor Green

    $TCPStream     = $TCPConnection.GetStream()

     

     

    try {

     

    $SSLStream     = New-Object System.Net.Security.SslStream($TCPStream)

    Write-Host "SSL connection has succeeded" -ForegroundColor Green

     

    try {

    $SSLStream.AuthenticateAsClient($remotehost)

    Write-Host "SSL authentication has succeeded" -ForegroundColor Green

    }

    catch {

    Write-Host "There's a problem with SSL authentication to $remotehost" -ForegroundColor Red

    return

    }

     

    $certificate = $SSLStream.get_remotecertificate()

     

    $certificateX509 = New-Object system.security.cryptography.x509certificates.x509certificate2($certificate)

    $SANextensions = New-Object system.security.cryptography.x509certificates.x509Certificate2Collection($certificateX509)

    $SANextensions = $SANextensions.Extensions | Where-Object {$_.Oid.FriendlyName -eq "subject alternative name"}

     

    Write-Host "=> Remote host certificate details:"

     

    Write-Host "Issuer:          " $SSLStream.RemoteCertificate.Issuer -ForegroundColor Green

    Write-Host "Subject name:    " $SSLStream.RemoteCertificate.Subject -ForegroundColor Green

    Write-Host "Expiration time: " $SSLStream.RemoteCertificate.GetExpirationDateString() -ForegroundColor Green

    Write-Host "Serial number:   " $SSLStream.RemoteCertificate.GetSerialNumberString() -ForegroundColor Green

    Write-Host "=> Subject alternative names:          "

    $SANextensions.Format(1)

     

    Write-Host "Please make sure that Subject name and Subject alternative names in the certificate are compatible with the names given in http://technet.microsoft.com/en-us/library/gg429704.aspx" -BackgroundColor DarkCyan

    Write-Host "Wildcard names in SAN are supported for meet, dialin and Lyncdiscover URLs. Please see http://technet.microsoft.com/en-us/library/hh202161.aspx for more details" -BackgroundColor DarkCyan

     

    }

     

    catch {

     

    Write-Host "$remotehost doesn't support SSL connections at TCP port $remoteport" -foregroundcolor Red

     

    }

     

    }

     

    catch {

     

    $exception = New-Object system.net.sockets.socketexception

    $errorcode = $exception.ErrorCode

    write-host "TCP connection to $remotehost failed, error code:$errorcode" -foregroundcolor Red

    write-host "Error details: $exception" -foregroundcolor Red

     

     

    }

    }

     

     

     

    #

    # TESTING

    #

     

     

    write-host "Please enter your external Web services FQDN. This could be seen from your Lync topology. You can skip it by pressing Enter. Example: webext.contoso.com" -ForegroundColor Green

    $LyncExternalWebserviceFqdn = Read-Host

    write-host "Please enter your meet URL. This could be seen from your Lync topology. You can skip it by pressing Enter. Example: meet.contoso.com" -ForegroundColor Green

    $SimpleURL_meet             = Read-Host

    write-host "Please enter your Office WebApp server FQDN. This could be seen from your Lync topology. You can skip it by pressing Enter. Example: wac.contoso.com" -ForegroundColor Green

    $LyncWac                    = Read-Host

    write-host "Please enter your Lyncdiscover URL. You can skip it by pressing Enter. Example: lyncdiscover.contoso.com" -ForegroundColor Green

    $SimpleURl_lyncdiscover     = Read-Host

    write-host "Please enter your dialin URL. This could be seen from your Lync topology. You can skip it by pressing Enter. Example: dialin.contoso.com" -ForegroundColor Green

    $SimpleURL_dialin           = Read-Host

     

     

     

    # ==============================================================================================

    # Resolve and access external web service fqdn

    # ==============================================================================================

     

    Write-Host "================================================================================================================"

    Write-Host "TEST 1: External Web service FQDN name resolution and access tests" -ForegroundColor yellow

    Write-Host "================================================================================================================"

     

     

    $LyncExternalWebserviceFqdnport = 443

     

     

    If([string]::IsNullOrEmpty($LyncExternalWebserviceFqdn)) {

     

     Write-Host "External Web services URL is NULL. Bypassing External web services accessibility check"

     

    }

     

     

    else  {

     

     

    Write-Host "=> Attempting to resolve External Web Service fqdn ("$LyncExternalWebserviceFqdn" )"

    if(ResolveLyncNames($LyncExternalWebserviceFqdn)) {

     

    Write-Host "=> Attempting to connect to External Web Service fqdn ("$LyncExternalWebserviceFqdn" ) at TCP port $LyncExternalWebserviceFqdnport"

    TLSConnectionCheck $LyncExternalWebserviceFqdn $LyncExternalWebserviceFqdnport

    }

     

     

     

    # ==============================================================================================

    # Resolve and access Meet URL

    # ==============================================================================================

     

    Write-Host "================================================================================================================"

    Write-Host "TEST 2: Meet URL name resolution and access tests" -ForegroundColor yellow

    Write-Host "================================================================================================================"

     

     

    If([string]::IsNullOrEmpty($SimpleURL_meet)) {

     

     Write-Host "Meet URL is NULL. Bypassing Meet URL accessibility check"

     

    }

     

    else {

     

    $SimpleURL_meet_port = 443

     

    Write-Host "=> Attempting to resolve Meet URL ("$SimpleURL_meet" )"

    if(ResolveLyncNames($SimpleURL_meet)) {

     

    Write-Host "=> Attempting to connect to Meet URL ("$SimpleURL_meet" ) at TCP port $SimpleURL_meet_port"

    TLSConnectionCheck $SimpleURL_meet $SimpleURL_meet_port

    }

     

     

    }

     

     

    # ==============================================================================================

    # Resolve and access Office WebApps URL

    # ==============================================================================================

     

     

    Write-Host "================================================================================================================"

    Write-Host "TEST 3: Office WebApps server FQDN name resolution and access tests" -ForegroundColor yellow

    Write-Host "================================================================================================================"

     

     

    $LyncWacport = 443

     

     

    If([string]::IsNullOrEmpty($LyncWac)) {

     

     Write-Host "Office WebApps URL is NULL. Bypassing Office WebApps services accessibility check"

     

    }

     

     

    else  {

     

     

    Write-Host "=> Attempting to resolve Office WebApps server fqdn ("$LyncWac" )"

    if(ResolveLyncNames($LyncWac)) {

     

    Write-Host "=> Attempting to connect to resolve Office WebApps server fqdn ("$LyncWac" ) at TCP port $LyncWacport"

    TLSConnectionCheck $LyncWac $LyncWacport

    }

     

     

     

    # ==============================================================================================

    # Resolve and access Lyncdiscover URL

    # ==============================================================================================

     

    Write-Host "================================================================================================================"

    Write-Host "TEST 4: Lyncdiscover URL name resolution and access tests" -ForegroundColor yellow

    Write-Host "================================================================================================================"

     

     

    If([string]::IsNullOrEmpty($SimpleURl_lyncdiscover)) {

     

     Write-Host "Lyncdiscover URL is NULL. Bypassing Lyncdiscover URL accessibility check"

     

    }

     

    else {

     

     

    $SimpleURL_lyncdiscover_port = 443

     

    Write-Host "=> Attempting to resolve Admin URL ("$SimpleURl_lyncdiscover" )"

    if(ResolveLyncNames($SimpleURl_lyncdiscover)) {

     

    Write-Host "=> Attempting to connect to Admin URL ("$SimpleURl_lyncdiscover" ) at TCP port $SimpleURL_lyncdiscover_port"

    TLSConnectionCheck $SimpleURl_lyncdiscover $SimpleURL_lyncdiscover_port

    }

     

     

    }

     

     

    # ==============================================================================================

    # Resolve and access Dialin URL

    # ==============================================================================================

     

    Write-Host "================================================================================================================"

    Write-Host "TEST 5: Dialin URL name resolution and access tests" -ForegroundColor yellow

    Write-Host "================================================================================================================"

     

     

    If([string]::IsNullOrEmpty($SimpleURL_dialin)) {

     

     Write-Host "Dialin URL is NULL. Bypassing Dialin URL accessibility check"

     

    }

     

    else {

     

    $SimpleURL_dialin_port = 443

     

     

    Write-Host "=> Attempting to resolve Dialin URL ("$SimpleURL_dialin" )"

    if(ResolveLyncNames($SimpleURL_dialin)) {

     

    Write-Host "=> Attempting to connect to Dialin URL ("$SimpleURL_dialin" ) at TCP port $SimpleURL_dialin_port"

    TLSConnectionCheck $SimpleURL_dialin $SimpleURL_dialin_port

    }

     

    }

     

     

    #------------SCRIPT ENDS HERE-----------


    => You can also find a sample output below:



     

    Hope this helps

     

    Thanks,

    Murat