Category: Lync / Skype for Business 2015

Unified Contacts Store (UCS) Deep Dive

I decided to make a bit of a deep dive inside the UCS (Unified Contact Store) in Lync/Skype for Business.

There are two options to store your contacts – in the Lync/SfB SQL store or in the Exchange mailbox.

For me the SQL store is the less problematic one, migrating users between different pools and Office 365 is less likely to cause a problems.

Option 1: Store in Lync/SfB SQL

SELECT UserAtHost, UpdateTime, InsertTime, ExUmEnabled, UcsMode, UcsMigrationAttemptCount, LastUcsMigrationAttempt FROM [rtc].[dbo].[Resource] as r
    INNER JOIN [rtc].[dbo].[ResourceDirectory] as d on (d.ResourceId = r.ResourceId)
    INNER JOIN [rtc].[dbo].[PresenceHomedResource] as h on (h.ResourceId = d.ResourceId)
--- Optional where clause to check specific users
--- WHERE r.UserAtHost IN ( 'user@domain.com', 'user2@domain.com' )

Option 2: Unified Contact Store in Exchange

MrMAPI.exe -ChildFolders -Folder "IPM_SUBTREE\Contacts"

MrMAPI.exe -Folder "IPM_SUBTREE\Contacts\{A9E2BC46-B3A0-4243-B315-60D991004455}"

Second-hop PowerShell remoting with Skype for Business/Lync cmdlets

I have faced a huge issue trying to run mutliple commands via remote PowerShell on different Front End servers, but apparently if the cmdlet needs to access resources from the Back End database – the command fails with this:

Active Directory error "-2147016672" occurred while searching for domain controllers

which is a very frustrating error.

For example running Get-CsCertificate will work, as the information is saved locally on the Front End, however Test-CsDatabase -LocalService will fail with this error, because executing this from your machine (in my case in a different domain) first jumps on the Front End, then requires a token from the Front End’s AD to send to the 3rd server for authentication – the SQL Back End where the connection breaks.

I also experimented with Get-CsConnections.ps1 script, and got the same problem.

So I came up with a bit ugly, but working solution for this. It is simple – Scheduled tasks.

$trigger = New-ScheduledTaskTrigger -AtStartup
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument 'Import-Module SkypeForBusiness; Test-CsDatabase -LocalService | convertto-csv -notypeinformation | out-file C:\temp\db.txt' 
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\NETWORK SERVICE" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings -Principal $principal
Register-ScheduledTask Test-CsDatabaseOnce -InputObject $task
Start-ScheduledTask -TaskName Test-CsDatabaseOnce
do {
    $state = (Get-ScheduledTask -TaskName Test-CsDatabaseOnce).State
    Start-Sleep -Milliseconds 200
} while ($state -eq "Running")
Get-Content C:\temp\db.txt | select -skip 1
Unregister-ScheduledTask -TaskName Test-CsDatabaseOnce -Confirm:$false

Here I create a simple task with the Network Service account and the name Test-CsDatabaseOnce, then run it, wait for the task to complete, which exports my results in csv in C:\temp\db.txt, and finally I get my results and work with them further.

Did the same thing with Get-CsConnections.ps1, however before that I had to deploy the script locally on each Front End with the simple code below:

$local_file = "C:\temp\Get-CsConnections.ps1"
$content = [system.io.file]::ReadAllBytes($local_file)
$remote_file = "C:\temp\Get-CsConnections.ps1"
Invoke-Command -Session $session -ScriptBlock { Param($path) [system.IO.file]::WriteAllBytes($path,$using:content) } -ArgumentList $remote_file

Office 365 License Management for Skype for Business Online

So I had a customer recently that wanted to update licenses on multiple users but only for Skype for Business Online. As I was researching I found out that in the web panel there is no option to that in bulk. Also there is no easy option to do in the PowerShell too. So I had to come out with a simple and fast solution so that I could enable all users only for Skype for Business Online at once. Another special requirement was to ONLY enable the Skype for Business service and ONLY for users having the E3 license assigned.

As you might know, Office 365 License has multiple services in it, so you can have an E3 user with Skype enabled and another one with Skype disabled. Gets trickier because there is not option to just ‘enable’ or ‘disable’ a service for a user (as you have the option in the admin panel clicking with your mouse user after user…). The only provided way with PowerShell is to create license options, per license, specifying which services should be disabled, by default all remaining services are deemed as enabled.

First you need to get the account sku with the following command:

Get-MsolAccountSku

You will receive a table with fist collumn AccountSkuId, you need the text before the colon, so for example mytennant365:SOME_SERVICE.

I will explain how the script works first, then you can review it yourself below.

  1. I have a .csv file called files.csv with only 1 column, with the name WindowsEmailAddress, containing the SMTP addresses of the users that need to be updated
  2. Loop trough all users and and get the licenses assigned (you can have multiple licenses assigned to a user!)
  3. Loop trough all licenses and only filter the E3 one (ENTERPRISEPACK) and get all services with their status (being disabled, enabled, etc.)
  4. Create an empty array of disabled services (options)
  5. Loop trough all services and copy only the disabled plans to the new empty array with disabled options, but SKIP the Skype for Business Online one (MCOSTANDARD).
  6. Create a service license option with the disabled services (skipping the Skype for Business Online one)
  7. Assign the newly created license options to the user
$users = Import-Csv file.csv
$users | ForEach-Object {
	$upn = $_.WindowsEmailAddress;
	$l = (Get-MsolUser -UserPrincipalName $upn).Licenses;
	$l | ForEach-Object {
		if ($_.AccountSkuId -eq "mytennant365:ENTERPRISEPACK"){
			$p = $_.ServiceStatus;
			$DisabledOptions = @()
			$p | ForEach-Object {
				if ($_.ProvisioningStatus -eq "Disabled" -and $_.ServicePlan.ServiceName -ne "MCOSTANDARD"){
					$DisabledOptions += $_.ServicePlan.ServiceName
				}
			}
			$x = New-MsolLicenseOptions -AccountSkuId "mytennant365:ENTERPRISEPACK" -DisabledPlans $DisabledOptions
			Set-MsolUserLicense -UserPrincipalName $upn -LicenseOptions $x;
		}
	}
}

Lync / Skype for Business client contacts export/import tool

I had a lot of issues with UCS on some of my customers so I had to come out with a solution for exporting and importing the contacts of the user from their client.

I used the SDK to develop 2 small applications, export.exe and import.exe. When you run the export.exe you get a BuddyList.xml in the same folder (you should be able to have write permissions there).

Running import.exe must be done with BuddyList.xml in the same folder, or it will fail.

The scripts are based on this PowerShell implementation, but with some bonuses:

  1. Does not require
    • Deployment of .dll file
    • PowerShell to be installed
    • Unrestricted execution rights for PowerShell scripts
    • Administrator access
  2. Does not duplicate contacts
  3. Does not duplicate groups
  4. The script takes into consideration the type of groups that you have:
    • DistributionGroup
    • FavoriteContacts/FrequentContacts
    • Custom created groups
  5. For distribution groups a lookup is first done to add it as a DG, not a custom group

You can download them from my OneDrive here: https://1drv.ms/f/s!ApaF5HY0Od2UhC861WCU5rqiTzum

Keep in mind that downloading random .exe files on the internet is dangerous!

If you would like to get the source code, I can provide it to you, just contact me on my email.