Month: March 2018

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 ( '', '' )

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 = []::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