Archive for the ‘Unified Communications’ Category.

Adding external contacts to the #Lync addressbook

There are some scenarios where it would be nice to be able to add external contacts to the Lync addresse so that they are searchable for everyone in the organization. Or at least external in the sence that the contact has a sip domain that isn’t supported in the Lync topology.

One such scenario could be an integration with an internal Cisco Telepresence solution.

The way to solve this is to add a contact object to AD that has the msRTCSIP-PrimaryUserAddress attribute populated, and the contact should be added to the address book on the next synchronization pass. I’ve made a script to create this kind of object:

Note that displayName is not required, but if you don’t add it the contact will only display the sipadress in the Lync client. I guess it’s also possible to append other AD attributes to the user such as telephoneNumber.

I got a question about telephoneNumber as well, so I’ve added it as an optional parameter in the script.


#####################################################################################
# New-SipContact.ps1
#
# Creates a contact object in AD that will be included in Lync/OCS address books.
#
#
# Passing parameters:
# .\New-SipContact.ps1 -cn "John Spencer" -path "OU=SIPContacts,DC=contoso,DC=com" -sipaddress "john.spencer@litwareinc.com" -displayname "John Displayname Spencer"
#
# Written by Tom-Inge Larsen (http://www.codesalot.com)
#
#####################################################################################
param($cn,$OUpath,$sipAddress,$displayName="",$telephoneNumber="")

$fullpath= "LDAP://" + $OUpath
$SIPContactOU = [ADSI]$fullpath

$SIPContact = $SIPContactOU.create("contact", "cn=" + $cn)
$SIPContact.Put("Description","SIP Contact Object")
$SIPContact.Put("msRTCSIP-PrimaryUserAddress", "sip:" + $sipAddress)
if ($displayName -ne "") {
 $SIPContact.Put("displayName", $displayName)
}
$SIPContact.Put("msRTCSIP-PrimaryUserAddress", "sip:" + $sipAddress)
if ($telephoneNumber -ne "") {
 $SIPContact.Put("telephoneNumber", $telephoneNumber)
}
$SIPContact.setInfo()

I’ve also created one to bulk create contacts from a .csv file


#####################################################################################
# New-SipContactBulk.ps1
#
# Creates a contact object in AD that will be included in Lync/OCS address books.
#
#
# Passing parameters:
# .\New-SipContactBulk.ps1 -OUpath "OU=SIPContacts,DC=contoso,DC=com" -csv "c:\newcontacts.csv"
#
#
# Written by Tom-Inge Larsen (http://www.codesalot.com)
#
#####################################################################################
param($OUpath,$csv)

$fullpath= "LDAP://" + $OUpath
$SIPContactOU = [ADSI]$fullpath

$contacts = Import-Csv -path $csv -header "cn","displayName","sipaddress"

foreach ($contact in $contacts) {

$SIPContact = $SIPContactOU.create("contact", "cn=" + $contact.cn)
 $SIPContact.Put("Description","SIP Contact Object")
 $SIPContact.Put("msRTCSIP-PrimaryUserAddress", "sip:" + $contact.sipaddress)
 if ($contact.displayname -ne "") {
 $SIPContact.Put("displayName", $contact.displayName)
 }
 $SIPContact.setInfo()
}

The example csv file is here: newcontacts.csv

List static routing config

After adding a static route to Lync (for example when adding a CTP integration) you can use the following command to show the route:
Get-CsStaticRoutingConfiguration Service:Registrar:lspool01.contoso.com
Identity : Service:Registrar:lspool01.contoso.com
Route    : {MatchUri=video.contoso.com;MatchOnlyPhoneUri=False;Enabled=True;ReplaceHostInRequestUri=False}

This will list the static route, but it won’t show all the route details (specifically the route target which is semi often used in troubleshooting) as they are contained inside a Route object in the StaticRoutingConfiguration object. To list the details of the route, do:


$stroute = Get-CsStaticRoutingConfiguration Service:Registrar:lspool01.contoso.com
$stroute.Route

This will give you the content of the route.

Transport               : TransportChoice=Certificate=Microsoft.Rtc.Management.WritableConfig.Settings.SipProxy.UseDefaultCert;Fqdn=vcsc.contoso.com;Port=5061
MatchUri                : video.contoso.com
MatchOnlyPhoneUri       : False
Enabled                 : True
ReplaceHostInRequestUri : False

Playing with PS: Script: Start remote Lync management session

I wrote a script to start a remote Lync management shell session based on this post. This of course is a big bloated way to do it (it can be done as a twoliner), but I need the PS training :P

##########################################################################################################################
# New-RemoteCSPSsession.ps1
#
# Opens a remote session to a Lync Management Shell and imports all commands
#
# Uses current logged inn credentials, but can optionally supply other credentials.
#
# eg.
# Prompt for fefqdn:
# .\New-RemoteCSPSsession.ps1
#
# Use other credential:
# .\New-RemoteCSPSsession.ps1 -othercredential $true
#
# Ignore certificates on fe:
# .\New-RemoteCSPSsession.ps1 -notrust $true
#
# fefqdn can be passed in arguments as well:
# .\New-RemoteCSPSsession.ps1 -csfe lync-admin.contoso.local
#
# Written by Tom-Inge Larsen (www.codesalot.com), based on this blogpost:
# (http://blogs.technet.com/b/csps/archive/2010/06/16/qsremoteaccess.aspx)
# this script can also easily be run as a twoliner:
# (http://blog.powershell.no/2010/12/05/lync-server-2010-remote-administration/)
#
# $session = New-PSSession -ConnectionUri https://lync-admin.contoso.local/OcsPowershell -Credential (Get-Credential)
# Import-PSSession -Session $session
#
#
##########################################################################################################################
param($othercredential,$notrust,$csfe)

$env = get-host
$majorversion = $host.version.major

if ($majorversion -lt 2) {
 write-Host "You need to run at least Powershell 2.0 to run this script. http://support.microsoft.com/kb/968929"
} else {

if ($csfe -eq $null) {
 $csfe = Read-Host "Please enter the FQDN of the Lync Front End pool you want to connect to"
 }

 $connectionURI = "https://"+$csfe+"/OcsPowershell"
 $cmdstring = 'New-PSSession -ConnectionUri $connectionURI'

 if ($othercredential -eq $true) {
 $credential = Get-Credential
 $cmdstring += ' -Credential $credential'
 }
 if ($notrust -eq $true) {
 $sessionoption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
 $cmdstring += ' -SessionOption $sessionoption'
 }

 $session = & $executioncontext.invokecommand.NewScriptBlock($cmdstring)

 Import-PSSession $session
}

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/

What to do if users are enabled for LCS?

I just had a Lync installation where it turned out that about 5 years ago they had tried installing LCS 2005 in AD, an installation that had failed and the servers had been taken down without being disabled.

The users that had been enabled that time was therefore still enabled for LCS in AD, and first of all did not appear when trying to search for them in the “enable users” UI. Searching for them with “Legacy users” filter on showed them, but failed when trying to move them with the “Move users” script in BigFin. It is also not supported to move LCS enabled users to Lync. Disabling them in BigFin will also not work. The error message will say something about users not enabled for rich presence, which was added in 2007 R2.

What will work though is the cmdlet Disable-CsUser. Run the cmdlet on the affected users, and then enable them as a normal non-enabled user again after that. I guess that old buddy lists and settings from LCS will be removed by this as well, so it is not a recommended migration path though. In my case this was not important.

Script – New-CiscoTelepresenceIntegration.ps1

Script to enable routes from Lync to VCS Control:
######################################################################################################################################################################################
# New-CiscoTelepresenceIntegration.ps1
#
# Adds config in Lync 2010 for integration with Cisco Telepresence (Tandberg)
#
# Can optionally write logs to file or screen using -verbose and/or -logFile inputs
#
# eg.
# Clean Lync installation
# .\New-CiscoTelepresenceIntegration.ps1 -vcscfqdn vcsc011.contoso.com -lsfepool lspool01.contoso.com -CTPSipDomain video.contoso.com -logFile "c:\logfile.txt"
#
# Coexisting with OCS 2007 R2
# .\New-CiscoTelepresenceIntegration.ps1 -coexistence $true -r2pool r2pool01.contoso.com -lsfepool lspool01.contoso.com -CTPSipDomain video.contoso.com -logFile "c:\logfile.txt"
#
# Migration from OCS 2007 R2 to Lync
# .\New-CiscoTelepresenceIntegration.ps1 -hascoexisted $true -vcscfqdn vcsc011.contoso.com -lsfepool lspool01.contoso.com -CTPSipDomain video.contoso.com -logFile "c:\logfile.txt"
#
# Important:
# This will delete any existing static routes created ! Do not run the script with hascoexisted = $true if you have added manual routes other than OCS/Lync/CTP integration
#
# Written by Tom-Inge Larsen (<a href="http://www.codesalot.com">www.codesalot.com</a>), Peder Saether and Trond Egil Gjelsvik-Bakke
# Based on config made by Marjus Sirvinsks (marjuss.wordpress.cm)
#
#######################################################################################################################################################################################
param($logFile,$coexistence=$false,$hascoexisted,$CTPSipDomain,$lsfepool,$r2pool,$vcscfqdn)

if ($logFile -ne $null) {
 $a = "Steps made to enable integration with Cisco Telepresence: `n"
 Out-File -FilePath $logfile -InputObject $a
}

if ($lsfepool -eq $null) {
 $lsfepool = Read-Host "Please enter Lync Front End pool FQDN."
}

if ($CTPSipDomain -eq $null) {
 $CTPSipDomain = Read-Host "Please enter the SIP domain in the Cisco Telepresence environment."
}

if ($coexistence -eq $false) {
 #Change encryption level if SRTP option is not available for VCS
 $mediaconfiguration = get-csmediaconfiguration
 $requireencryption = ($mediaconfiguration.EncryptionLevel -eq "RequireEncryption")
 if ($requireencryption) {
 write-warning "This will set the media encryption level to Support Encryption. Are you sure you want to do this? (y/n)"
 $confirmation = Read-Host

 } else {
 $confirmation = 'y'
 }
 switch ($confirmation) {
 'y' {
 set-CsMediaConfiguration -EncryptionLevel supportencryption

 $registrarid = "service:registrar:"+$lsfepool
 $trustedappregistrar = "Registrar:"+$lsfepool

 if ($hascoexisted -eq $true) {
 Remove-CsStaticRoutingConfiguration -Identity $registrarid
 }

 if ($vcscfqdn -eq $null) {
 $vcscfqdn = Read-Host "Please enter the FQDN for the VCS Control"
 }

 #Establish trust
 $applicationpooladded = $true
 New-CsTrustedApplicationPool -Identity $vcscfqdn -Registrar $trustedappregistrar -site 1 -RequiresReplication $false -ThrottleAsServer $true -TreatAsAuthenticated $true -force

New-CsTrustedApplication -ApplicationID "CiscoTelepresenceDirectSIP" -TrustedApplicationPoolFqdn $vcscfqdn -Port 5061

 #Create static routes if needed

 if ($hascoexisted -eq $true) {
 New-CsRegistrarConfiguration -Identity $registrarid
 }

 New-CsStaticRoutingConfiguration -identity $registrarid

$route = New-CsStaticRoute -TLSRoute -destination $vcscfqdn -port 5061 -matchuri $CTPSipDomain -usedefaultcertificate $true

Set-CsStaticRoutingConfiguration -identity $registrarid -route @{Add=$route}

 Enable-CsTopology
 }
 'n' {
 Write-Warning "No change was made to the topology. Media Encryption Level must be set to Support Encryption"
 if ($logFile -ne $null) {
 $a = "No change has been made. `n"
 Out-File -FilePath $logfile -InputObject $a -Append
 }
 }
 }
}

else {

# If we coexist with R2, we might want to route all traffic via R2 FE, to possibly avoid
 # compromising security with deployments using TCP or if Lync is only intended as a
 # pilot.

 if ($r2pool -eq $null) {
 $r2pool = Read-Host "Please enter OCS 2007 R2 Front End pool FQDN."
 }

 $registrarid = "service:registrar:"+$lsfepool

 New-CsRegistrarConfiguration -Identity $registrarid
 New-CsStaticRoutingConfiguration -identity $registrarid

$route = New-CsStaticRoute -TLSRoute -destination $r2pool -port 5061 -matchuri $CTPSipDomain -usedefaultcertificate $true
 Set-CsStaticRoutingConfiguration -identity $registrarid -route @{Add=$route}

Enable-CsTopology
}

if ($logFile -ne $null) {

$a = "Route added: `n"
 Out-File -FilePath $logfile -InputObject $a -Append
 Get-CsStaticRoutingConfiguration $registrarid | Select-Object -ExpandProperty Route | Where-Object {$_.MatchUri -eq $CTPSipDomain} | Out-File -FilePath $logfile -Append
 if ($applicationpooladded -eq $true){
 $a = "`nTrusted Application Pool added:`n"
 Out-File -FilePath $logfile -InputObject $a -Append
 Get-CsTrustedApplicationPool $vcscfqdn | Out-File $logfile -append
 }
 $a = "`nRegistrar added:`n"
 Out-File -FilePath $logfile -InputObject $a -Append
 Get-CsStaticRoutingConfiguration $registrarid | Out-File $logFile -append

 if ($confirmation -eq 'y') {
 $a = "`nMedia encryption level was already set to or was set to Support Encryption.`n"
 Out-File -FilePath $logfile -InputObject $a -Append
 }

 Write-Host "Logfile: " $logFile "is written."
}

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.
#####################################################################################
# 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*

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!

Problems adding phone numbers to a conference call

I’ve recently had some problems with an OCS 2007 R2 installation where the users were complaining that they couldn’t add external phone numbers to a conference call. Internal numbers and SIP contacts were working fine.

I did some tracing, and noticed that the service provider gateway returned a 404 Not found when answering to the SIP Invite of the phone number. I checked the number, and it seemed correct.

But the FROM: field of the INVITE was populated with the users SIP URI not the users TEL URI, which the service provider didn’t handle. Normally a call through the mediation server will use the TEL URI, but turns out that it doesn’t in this case. There is AFAIK no way to handle this in OCS 2007 R2. (There might be in Lync, but I’ll need to lab that. The conference call behaves the same way in Lync, but you have the option to substitute the dialed from number on the route.)

This means that the service provider needs to handle this and substitute all unknown URI’s in the FROM: field with some known number, ex. the Dial In conference number or the main number of the organization, unless the service provider wants to maintain a database of the users and their corresponding TEL URI.

It might also be a solution to put some kind of gateway between the PSTN and OCS that does the conversion.

Has anyone else seen this?