Prepare for Response Group Restore
When you try to restore the Response Group configuration file after a backup, the restore fails because the contact objects already exist in Active Directory. This script lists the Response Group contact objects in Active Directory and allows you to delete those objects before restoring.
To run the script below, do the following:
1. Copy the script and paste it into Notepad (or a Windows PowerShell editor) and save the file with the name Get-CsApplicationContact.ps1.
2. From a Microsoft Lync Front End server, open Lync Server Management Shell.
3. Navigate to the folder where the .ps1 file is located, and run the following command:
Import-Module .\Get-CsApplicationContact.ps1
4. Run the following command to retrieve the list of Response Group contact objects associated with a specific pool (pool01.contoso.com in the example below):
Get-CsApplicationContact –OwnerUrn "urn:application:Rgs" –Filter "(MSRTCSIP-ApplicationOptions=1)" –RegistrarPool "pool01.contoso.com"
The OwnerUrn parameter specifies that you want only contacts for the Response Group application. The Filter parameter is an LDAP filter that will select only contact objects that have been at some point tied to a Response Group workflow, and unselect the two contact objects (Presence Watcher and Announcement Service) that are automatically created with Response Group application. The RegistrarPool parameter allows you to select only contacts associated with a specific pool.
5. The preceding command should output all the properties of every contact object formerly associated with a Response Group workflow on the specified pool. If you are satisfied with that list, you can delete those contact objects by simply re-running the same command and adding the Delete parameter at the end, like this:
Get-CsApplicationContact –OwnerUrn "urn:application:Rgs" –Filter "(MSRTCSIP-ApplicationOptions=1)" –RegistrarPool "pool01.contoso.com" –Delete
You’ll receive a confirmation prompt to delete each contact object, so that you can skip some of them if necessary.
Get-CsApplicationContact Script
# Function that reads Service Assignments from Central Management database
Function ReadServiceAssignments()
{
$roleName = "UserServices"
$role = [Microsoft.Rtc.Management.Core.RoleName]::$roleName
# Create a Central Management database ServiceConsumer with that Role
$serviceConsumer = New-Object Microsoft.Rtc.Management.ServiceConsumer.ServiceConsumer $role
# We can't call directly the Read<T> method of ServiceConsumer
# because PowerShell can't call a generic method, so let's first retrieve it
# We can't call GetMethod("Read") either because there are two Read methods in that class
# so we need to get all methods and filter on the name and number of arguments
$readMethod = [Microsoft.Rtc.Management.ServiceConsumer.ServiceConsumer].GetMethods() | where {($_.Name -eq "Read") -and ($_.GetParameters().Count -eq 0)}
# Create a closed representation of it
$typedParameter = [type] "Microsoft.Rtc.Management.Config.Settings.ServiceAssignment.ServiceAssignments"
$newMethod = $readMethod.MakeGenericMethod($typedParameter)
$serviceAssignments = $newMethod.Invoke($serviceConsumer, $null)
return $serviceAssignments
}
Function GetPoolPolicy(
[string][Parameter(Mandatory=$true)]$registrarServiceId
)
{
$serviceAssignmentsList = ReadServiceAssignments
for ($i = 0; $i -lt $serviceAssignmentsList.ServiceAssignment.Count; $i++)
{
for ($j = 0; $j -lt $serviceAssignmentsList.ServiceAssignment[$i].Component.Count; $j++)
{
if ($serviceAssignmentsList.ServiceAssignment[$i].Component[$j].ServiceId -eq $registrarServiceId)
{
return $serviceAssignmentsList.ServiceAssignment[$i].TagId
}
}
}
}
# Function that returns the LDAP filter depending on the specified parameters
# FQDN filter is not taken into account here, it can be filtered only on the results at the end
Function CreateLDAPFilter(
[string]$identity = "",
[string]$ownerUrn = "",
[string]$customFilter = "",
[string]$poolPolicy = ""
)
{
$filter = ""
if ($identity -ne "")
{
$filter = "(MSRTCSIP-PrimaryUserAddress=$identity)"
}
if ($ownerUrn -ne "")
{
$ownerUrnFilter = "(MSRTCSIP-OwnerUrn=$ownerUrn)"
if ($filter -eq "")
{
$filter = $ownerUrnFilter
}
else
{
$filter = "(&" + $filter + $ownerUrnFilter + ")"
}
}
if ($customFilter -ne "")
{
if ($filter -eq "")
{
$filter = $customFilter
}
else
{
$filter = "(&" + $filter + $customFilter + ")"
}
}
if ($poolPolicy -ne "")
{
$poolPolicyFilter = "(MSRTCSIP-UserPolicies=0=$poolPolicy)"
if ($filter -eq "")
{
$filter = $poolPolicyFilter
}
else
{
$filter = "(&" + $filter + $poolPolicyFilter + ")"
}
}
return $filter
}
# Now the script itself
Function Get-CsApplicationContact(
[Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][string]$Identity = "",
[string]$OwnerUrn = "",
[string]$RegistrarPool = "",
[string]$Filter = "",
[Switch]$Delete
)
{
Write-Host
# If RegistrarPool is specified, find the Registrar ServiceId associated with the FQDN
# and then the PoolPolicy Service Assignment associated with it
if ($RegistrarPool -ne "")
{
$registrarServiceId = (Get-CsService -Registrar -PoolFqdn $RegistrarPool).ServiceId
Write-Host ("ServiceId of the Registrar service on pool " + $RegistrarPool + " = " + $registrarServiceId)
$poolPolicy = GetPoolPolicy -registrarServiceId $registrarServiceId
Write-Host ("Corresponding Pool Policy = " + $poolPolicy)
}
Write-Host
# Get the DN of the domain
$dirEntry = New-Object System.DirectoryServices.DirectoryEntry
$domainDN = $dirEntry.distinguishedName
# Create a DirectoryEntry to the path where the AppContacts are stored
$searchPath = "LDAP://CN=Application Contacts,CN=RTC Service,CN=Services,CN=Configuration," + $domainDN
$dirEntry = New-Object System.DirectoryServices.DirectoryEntry $searchPath
# Create a DirectorySearcher at that location
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $dirEntry
$objSearcher.PageSize = 1000
# Search only the one-level children of the Application Contacts node
$objSearcher.SearchScope = "OneLevel"
# Create an LDAP search filter
$strFilter = CreateLDAPFilter -identity $Identity -ownerUrn $OwnerUrn -customFilter $Filter -poolPolicy $poolPolicy
if ($strFilter -ne "")
{
$objSearcher.Filter = $strFilter
}
# Run the query
$colResults = $objSearcher.FindAll()
Write-Host "Contact objects found:"
Write-Host
foreach ($result in $colResults)
{
$objItem = $result.Properties
# Copy the object into a better-looking one and remove the extra {}
$formattedResult = New-Object HashTable
foreach ($propertyName in $objItem.PropertyNames)
{
$formattedResult.$propertyName = $objItem.$propertyName[0]
}
# Output the result with sorted properties and correct formatting
$formattedResult.GetEnumerator() | Sort-Object Key | Format-Table -AutoSize
}
if ($Delete)
{
foreach ($result in $colResults)
{
$objItem = $result.Properties
$sipAddressPropertyName = "msrtcsip-primaryuseraddress"
$sipAddress = $objItem.$sipAddressPropertyName[0]
$adspathPropertyName = "adspath"
$adspath = $objItem.$adspathPropertyName[0]
Remove-CsApplicationContact -SipAddress $sipAddress -ADSPath $adspath
}
}
}
Function Remove-CsApplicationContact(
[string][Parameter(Mandatory=$true)]$SipAddress,
[string][Parameter(Mandatory=$true)]$ADSPath
)
{
#$SipAddress
#$ADSPath
if ($psCmdlet.shouldContinue(("Are you sure you want to delete Contact Object with sip address: " + $SipAddress), ""))
{
$dirEntry = New-Object System.DirectoryServices.DirectoryEntry $ADSPath
#$dirEntry
$dirEntry.DeleteTree()
}
}