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}

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.

ACE warnings when publishing topology

Now and then when publishing the topology, I’ve gotten some warnings that just states one or more of these:

Warning: Ace DOMAIN\RTCUniversalGlobalReadOnlyGroup; Allow; ReadProperty; None; None

and also later in the log

Warning: One or more group access control entries (ACEs) are not ready.

This means that not all ACEs are ready after the forest prep, for whatever reason. Just run a

Enable-CsAdForest

which should reset the permissions, and you should be fine.

http://technet.microsoft.com/en-us/library/gg425713(v=ocs.15).aspx

Presence issues with the calendar integration in CU2?

I just had a case where the users experienced that presence in the Lync client did not update based on the calendar information unless they restarted the client completely. Relogging did not help.

The weird thing was that on the users contact card they would be listed as busy, but their presence was still available.

After quite a bit of troubleshooting and some help from colleagues, I ended up removing CU2 (the April 2011 update, gives version .275 to the client). This removed the problem completely. So, if you are having presence issues with Lync, try removing CU2 for now.

This might be a bug?

Federate with Lync Online

To federate with Lync Online/Office 365, run:

 New-CSHostingProvider –identity LyncOnline –ProxyFqdn sipfed.online.lync.com –Enabled $True

Enable-CSTopology
 

Here’s how to do it using GUI: http://techietom.co.uk/blog/2011/04/how-to-enable-office365-lync-online-to-federate-with-lync-on-prem/

Disable AD disabled CS users powershell script

I’ve made a script to disable AD disabled users from Lync. The script pulls all AD disabled users and checks if they are disabled for Lync as well. If not, they will be. Optional output to screen and/or file.

Download Disable-AdDisabledCsUsers.zip or copy the sourcecode:

 #####################################################################################
 # Disable-AdDisabledCsUsers.ps1
 #
 # Pulls all AD disabled users from AD and disables them for Lync as well
 #
 # Can optionally write logs to file or screen using -verbose and/or -logFile inputs
 #
 # eg.
 #
 # .Disable-AdDisabledCsUsers.ps1 -verbose $true -logFile "c:logfile.log"
 #
 #
 #
 # Written by Tom-Inge Larsen (codesalot.com)
 #
 ####################################################################################
 param($verbose,$logFile)
Import-Module active*</code></code>if ($logFile -ne "") {
 $logoutput = [System.IO.StreamWriter] $logFile
 $logoutput.WriteLine("AD disabled users that was Lync disabled:")
 }

$disabledADusers = Search-ADAccount -AccountDisabled -UsersOnly | Select-Object userprincipalname

$disabledADusers | foreach-object {
 $identity = $_.userprincipalname
 $csuser = Get-CsUser -Identity $identity -ErrorAction SilentlyContinue | Select-Object Enabled

if ($csuser.enabled -eq $true) {
 Disable-CsUser -Identity $identity
 if ($verbose -eq $true) {
 Write-Host "AD disabled user" $identity "is now disabled for Lync as well"
 }
 if ($logFile -ne "") {
 $logoutput.WriteLine($identity)
 }
 }
 }

if ($logFile -ne "") {
 $logoutput.close()
 if ($verbose -eq $true) {
 Write-Host $logFile "written."
 }
 }

Lync 2010 and Exchange Web Services/autoconfigure

I’ve recently been upgrading an OCS 2007 R2 environment to Lync 2010 where the users have SMTP adresses in several SMTP domains, but they only have one SIP domain. All users have an SMTP address at least in the SIP domain, but many of them have their primary SMTP address in different domains from the SIP domain.

What happened when we migrated the first couple of users to the new Lync FE pool and the new client, some of them lost their calendar integration and recent calls list. It was early apparent that these users were the ones that didn’t have their primary SMTP address in the SIP domain, so at least we were able to find a common denominator.

Taking up the configuration information on the client showed “EWS not deployed” and the fields for EWS Internal and External URL were empty. Testing autoconfigure in Outlook did not return any errors. Of course google is my friend, so I found this post at confusedamused that listed a couple of things to check. None of these solved it for me, but check them first in any case.

When more googling didn’t result in any more possible solutions, I started looking at wireshark traces, and noticed that the Lync client was actually looking up DNS autoconfigure for the users primary SIP domain, not the SMTP domain. As it turned out, autoconfigure was set up to use SCP in all the other domains than the SMTP domain that was equal to the SIP domain, so the Lync client failed to get autoconfigure config for the other domains. I dont know why this was not a problem in R2, so theres probably been some kind of change to how the Lync client handles EWS configuration. Adding the autoconfigure host to HOSTS on the client machine I saw that the Lync client was getting “401 Unauthorized” messages back from the EWS server. I didn’t notice at first, but it was trying HTTP, not HTTPS which had failed a couple of packets earlier, because EWS didn’t have a SAN name in the correct domains.

The solution then was to either add autoconfigure names for the other domains to SAN on the EWS IIS certificate, or adding a SRV record pointing to the Exchange CAS for autoconfigure in the other domains. Hey presto!