Lync Meetings and Transport Neutral Encapsulation Format

One of the small things that make Lync Meetings so simple to join is that Outlook will recognize the meeting and wil give you a small button on the Outlook reminder that lets you join the meeting without even opening the calendar. This is also the same functionality that clickable from the calendar interface on the mobile and desktop clients and makes the meeting joinable from a Lync Room System.

Some might have noticed though that when the Lync meeting invites come from an external organization, none of the clients will actually recognize the meeting as a Lync meeting. For most of the clients this is not a big problem, because the link in the invite will still be clickable, but for the Lync Room System this will actually render the meeting unjoinable.

The method that is beeing used in the meeting invites to identify a calendar object as a Lync meeting is called Transport Neutral Encapsulation Format, or TNEF. TNEF is basically an attachment format that is used by Outlook and Exchange in different situations additional formatting is needed, like voting and meeting invites.

The global settings for sending TNEF to remote domains is default set to false. This means that when sending Lync Meeting invites out of the organization, the TNEF attachment is stripped off and the recieving party does not get the extra data which in turn makes the Lync clients at the recieving party not recognize the meeting as a Lync meeting.

To resolve this, the sending party needs to enable sending of TNEF attachments to the recieving party. This is done via the RemoteDomain settings, and can be turned on for i.e contoso.com like this:

New-RemoteDomain -DomainName contoso.com -Name Contoso
Set-RemoteDomain -Identity Contoso -TNEFEnabled $true

It is also possible to set TNEF on for all remote domains, but be careful with this as TNEF can cause issues if the recieving end does not use Exchange.

Bug in KB 2863908

There seems to be a bug in the Lync update KB 2863908 that breaks integration between Outlook 2010 and Lync 2013.

If you search for a local outlook contact in lync, it will not display a phone number  on the contact card unless you remove the email address from the contact. If an email address is present on the contact, the lync client will not display any phone numbers for the contact.

As far as I have seen it only occurs when you have Lync 2013 and Outlook 2010.

Removing KB 2863908 resolves the issue.

There are also a couple of threads on the technet forums about this:

http://social.technet.microsoft.com/Forums/en-US/4317ec0e-3287-427d-b875-5b9c93997139/lync-client-2013-does-not-get-contact-phone-numbers-from-outlook-2010?forum=lyncprofile

http://social.technet.microsoft.com/Forums/lync/en-US/130ff6ff-54aa-42a2-86c5-69761eec446a/lync-2013-client-does-not-show-outlook-contact-numbers?forum=ocsclients

Script: Find-CsLineUri

Often I need to find a certain LineUri in Lync. LineUris are most of the time configured on users, so a simple

Get-CsUser -filter {LineUri -like "tel:<e164number>*"}

will give you the result you are looking for. But a user is not the only object in Lync that can have a LineUri, and if you in addition don’t know what object has the LineUri you might need to search through all the objects. This script does just that. Sort of an opposite to Ståle Hansens List-UnusedNumbers.ps1.

The script will also list all objects that have that LineUri, so it is useful in the situations where you have managed to get the same LineUri on two objects and are getting “SIP/2.0 485 Ambiguous” errors on calls. Normally, if you try to give an object a LineUri that is already configured it will throw a powershell error, but there are situations where it doesn’t pick up on it, especially when using the ;ext=1234 addition to the LineUri.

Lync reports a SIP 485 Ambiguous and a Diagnostic ID 4199 "Multiple users associated with the target phone number"

Lync reports a SIP 485 Ambiguous and a Diagnostic ID 4199 “Multiple users associated with the target phone number”

Download Find-CSLineUri.zip or copy the sourcecode:

####################################################################################################
# Find-CsLineUri.ps1
#
# Lists all objects with a given LineUri
#
#
# Passing parameters:
# .\Find-CsLineUri.ps1 +4712345678
#
# Written by Tom-Inge Larsen (http://www.codesalot.com)
#
####################################################################################################
param($getlineuri)

Function ListContents
{
    Param($Heading,$list)

    Write-Host $Heading
    Write-Host "--------------------------------------------------"
    Write-Host

    foreach ($object in $list) {
       $object
    }
}

clear-host

write-debug $getlineuri

$getlineuri = "tel:" + $getlineuri + "*"

Write-debug $getlineuri

$csusers=Get-CsUser -Filter {LineURI -like $getlineuri} | Select-Object SipAddress,LineURI | out-string -stream
$csuserspl=Get-CsUser -Filter {PrivateLine -like $getlineuri} | Select-Object SipAddress,PrivateLine | out-string -stream
$csanalogs=Get-CsAnalogDevice -Filter {LineURI -like $getlineuri} | Select-Object SipAddress,LineURI | out-string -stream
$cscaps=Get-CsCommonAreaPhone -Filter {LineURI -like $getlineuri} | Select-Object SipAddress,LineURI | out-string -stream
$csums=Get-CsExUmContact -Filter {LineURI -like $getlineuri} | Select-Object SipAddress,LineURI | out-string -stream
$csdialins=Get-CsDialInConferencingAccessNumber -Filter {LineURI -like $getlineuri} | Select-Object PrimaryUri,LineURI | out-string -stream
$cstrusteds=Get-CsTrustedApplicationEndpoint -Filter {LineURI -like $getlineuri} | Select-Object SipAddress,LineURI | out-string -stream
$csrgses=Get-CsRgsWorkflow | Where-Object {$_.LineUri -like $getlineuri} | Select-Object PrimaryUri,LineURI | out-string -stream

if ($csusers -ne $null){ListContents -Heading "User" -list $csusers}
if ($csuserspl -ne $null){ListContents -Heading "Private Line" -list $csuserspl}
if ($csanalogs -ne $null){ListContents -Heading "Analog Device" -list $csanalogs}
if ($cscaps -ne $null){ListContents -Heading "Common Area Phone" -list $cscaps}
if ($csums -ne $null){ListContents -Heading "Exchange UM Contact" -list $csums}
if ($csdialins -ne $null){ListContents -Heading "Dial-in Conference Number" -list $csdialins}
if ($cstrusteds -ne $null){ListContents -Heading "Trusted Application Endpoint" -list $cstrusteds}
if ($csrgses -ne $null){ListContents -Heading "Response Group Workflow" -list $csrgses}

Making Office Web Apps Server private

After installing the WAC server following this or another guide, we also normally publish the WAC server through the same reverse proxy method as we do the other Lync web services – using the DNS name that we configured when we created the farm.

If you publish the WAC server this way without doing anything else it will work the same way for users on the internet as it does for the internal users and we might be content with that. But should we?

My colleague Marjus made this post a little while back on how to limit the access to the WAC services only to servers from your domain. If you don’t do this you’re basically publishing the WAC server as a public service that anyone can add to topology builder and use as the WAC server in their own Lync environment.

So, if you want to keep your WAC server for yourself, remember to add all domains where you host servers that should be able to use the WAC server to the allow list by using the New-OfficeWebAppsHost Cmdlet. The wildcard * is assumed on all domains in the allow list, so subdomains are supported automatically. You only need to add the server domain(s), not necessarily the same as the SIP domain(s).

New-OfficeWebAppsHost -Domain "contoso.com"

Powerpoint presentations not working externally

I just had a problem with powerpoint presentations in Lync 2013 that behaved strangly.

All internal users could share and view powerpoints as they should, but all external users and guests could not. It behaved the same way in Lync 2013 clients as in the web app. It would just show “connecting” or “Waiting for the presentation to begin” before failing with a message that the network had gone down or the server was busy. There were no errors logged on the WAC server and no failures recorded on the monitoring database. I could also reach the https://wacserver.contoso.com/hosting/discovery through the TMG rule. Really weird.

After a bit of googling I found a forum post on technet where someone referenced a setting on the HTTP filter called “Verify Normalization”. The setting is found on the “Traffic” tab on the rule, like this:

Verify Normalization

Unticking this box solved the issue.

The rule is explained here, but it is basically a security mechanism that blocks URLs containing % sign if they are double encoded in the URL, although they can end up blocking legitimate traffic as well which is the case here. I do not know if this is a bug in WAC/OWAS or if it is by design though. Removing “Verify Normalization” from the rule will solve the issue in any case.

The URL the clients were accessing looked something like this, and contains a lot of url encoded characters.

https://lync-app.domain.no/m/Presenter.aspx?a=0&e=true&WopiSrc=https%3A%2F%2Flync-fe1.domain.no%2FDataCollabWeb%2Fwopi%2Ffiles%2F9-1-2C3E7AD&access_token=AAMFEOV5-KFUGNRfrclZBRMAVS8GEC3yB9HdElYrtUpQ8UkEs2KBEOV5-KFUGNRfrclZBRMAVS-CAr65gyAPlwe3U3NwCIa-PNE_Q7U3BPbx_s-yipQjZy9FKfOs04YIc2BBOb2J0AgIDURhdGFDb2xsYWJXZWI&<fs=FULLSCREEN&><rec=RECORDING&><thm=THEME_ID&><ui=UI_LLCC&><rs=DC_LLCC&&gt

Garbled but readable text on the Store page when installing Windows 8.1 Preview

Help! I’m trying to install Windows 8.1 preview, but the store page looks like someone is hacking me! Like this:

Screenshot (8)

Fear not, this is called Pseudolocalization and happens because Store is not yet available in your language locale. This locale is tied to your Microsoft Account profile, not your OS language.

Handling SimpleURLs and Reverse Proxy during migration

I’ve thought about writing this post for a while, as the topic isn’t really covered very well in the Lync 2013 migration documentation. The issue is also only relevant when migrating from Lync 2010 to Lync 2013.

The scenario is this: You are doing a migration from Lync 2010 to Lync 2013. You are following the migration steps provided in the migration documentation, and have planned and prepared your migration, deployed a pilot pool and moved some pilot users to Lync 2013. Internally everything is working as it should,  all modalities are working and conferencing works via the meeting join page using simple urls regardless of which server the user is on.

Now, most of the deployments I do are not large enough that it is needed to use more than one FE pool and for external access one Edge pool and one reverse Proxy. Because of this, most of the conferencing going on is involving external participants. This means that modalities and meeting join needs to be working for the pilots in external scenarios as well. The next step in the migration documentation covers some of this with the deployment of the Edge server. This will handle the peer to peer modalities, but what about the simple URLs?

If you try to reuse the existing reverse proxy rules, you will end up in one of these situations:

  1. You keep the rule as it is, pointing toward the Lync 2010 pool
    If you do this, simple URLs and meeting join will continue working for meetings created by the Lync 2010 users. But if someone external tries to join a meeting created by one of the pilot Lync 2013 users, they will be met with a page saying “Sorry, something went wrong, and we can’t get you into the meeting.” and if you click on “more info” you will get en error saying “Error:Invalid Conference organizer or Conference ID” like this:
    meetingjoinerror
    The Lync 2013 pilot users will also not be able to log in via the mobile client.
  2. You change the rule, pointing it toward the new Lync 2013 pool
    If you do this, everyting will work as it should for the Lync 2013 users. But if someone tries to join a meeting created by one of the Lync 2010 users, they will be met with an ordinary IIS “404 not found” page
    404

Both of these solutions are not really wanted in a normal migration, so what do we do then? The solution is to handle it as a normal multiple pool deployment. This is as it turns out not really well documented in the TechNet library, but on the Request and Configure a Certificate for Your Reverse HTTP Proxy page there is a note saying this:

If your internal deployment consists of more than one Standard Edition server or Front End pool, you must configure web publishing rules for each external web farm FQDN and you will either need a certificate and web listener for each, or you must obtain a certificate whose subject alternative name contains the names used by all of the pools, assign it to a web listener, and share it among multiple web publishing rules.

This means that you will need to deploy a new reverse proxy publishing rule for the new Lync 2013 Pool with a different DNS name for the external services. The rule will also need to use a certificate that contains all the simple URL names in addition to the new name for the external web services and lyncdiscover. This will in most cases create a need for a new public certificate for this new rule.

By doing this Lync is able to redirect the traffic to the webservices between the reverse proxy rules as it does on the inside, and all functionality should be available for both existing Lync 2010 users and the Lync 2013 pilot users.