Lync Response Groups / Update Agents from Distribution Lists / Synchronisation Process continuously crashes.

We have a large installation supporting 21,000 users with 260 Response groups and about 1200 agents. To enable response group managers to update the agent membership of their response groups, we use distribution lists.

The Synchronisation Task which updates the Agents table in the rgsconfig database from distribution lists crashes continuously under some circumstances which has the result that changes which the users make to the distribution lists may have no effect. If they are unlucky, their distribution list is not processed before the crash point.

The problem senario is this:  A person who is an agent in one or more response groups changes their sip address. In our case, the sip address contains a representation of the user's name and so for example a woman marries and changes her name, then she may request a new sip address.

The synchronisation task which updates the Agents table does not find the new sip address and attempts to add it to the table together with other attributes such as the SID of the User object. However, the old row still exists in the Agents table for the users old sip address. But because the User Object SID has not changed and the Table is indexed on the SID Column, it throws an error. Because this error is not handled appropriately, the synchronisation task breaks at this point.

 

Here is an event log extract ...

======================

Data synchronization from Active Directory failed.

The following exception was thrown during the synchronization process:
Exception: System.Data.SqlClient.SqlException - Cannot insert duplicate key row in object 'dbo.Agents' with unique index 'IX_Agents_UserSid'.
The statement has been terminated.
Inner Exception: ~
The next synchronization attempt will be in 480 minutes.
Cause: Unhandled exception.
Resolution:
Verify the connectivity with the domain controller or if the Active Directory schema was properly applied and all user attributes are present.

======================

 

The work around (until Microsoft fixes this problem) is to delete the agent's old entry from the agents table. The problem is finding which agent 's entry is causing the problem. In OCS it was possible by setting up a trace using the logging tool on all the RGS components and by examing the trace at the point of the crash, it was possible to find which agent was being added and use the SQL Studio to delete the entry.  Not so with Lync. There is a bug which prevents a user viewing the captured traces (etl files) for Response Group components. Microsoft Support have responded quickly in analysing thes files for us but we need to react very quickly when this happens because telephony is mission critical for us. I have developed a  powershell procedure for finding entries in the agents table which will cause the synchronisation task to go on to crash. It works by going through the agents table (actually a modified csv dump of the agents table) and getting the matching entries from the AD using the user object's SID. It then reports if the SIP address in the agents table is different to the SIP Address in the AD for a given user object.

Here is the code in case it helps anyone ..

====================
#
# Scan CSV file extract from RGS Agent Table
# 
# To use this script, Prepare Agent Table Extract (Copy to Excel, 
#  delete display name column, save to csv, replace all ';' with ',' )
#
# 
# Get AD User object based on SID in Agent Table
# Compare SIP Address in Agent Table with that in Userobject.
# Report Mismatches
#
# Douglas Miles 10.06.2011
#


# change this ...
$agentFile = 'C:\Users\SCIS-TBEMIFR1\Desktop\agentTableDump.txt'


Function Get-ADUser
{
	# bits from http://www.wisesoft.co.uk/scripts/

	Param($Container, $filter)

	$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
	$root = [ADSI] "LDAP://$($dom.Name)"

	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher 
	$objSearcher.SearchRoot = $root
	$objSearcher.PageSize = 1000
	$objSearcher.Filter = $Filter

	$objSearcher.FindAll()
}



Function Do-All ( $isDebug )
{

	begin
	{

	}

	process
	{
		$in = $_

		
		#convert SID from format "0x01050000000000051500000085E77E2F2AD0375C43170A32E4030800"
        # to \01\05\00....  format for the LDAP Query...
 
		$sidSep = ''
		for( $i = 2; $i -lt $in.UserSID.Length ; $i = $i + 2 )
		{
   			$sidSep += '\' + $in.UserSID.substring($i, 2 ) 
   			
 		}
		# $sidSep + ' - ' + $_.SIPAddress
		

		$uobj = Get-adUser -filter "(objectSid=$sidSep)" 

		$uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
		
		if ( $uoSIP.tolower() -ne $in.SIPAddress.tolower() )
		{
			"Unmatched SIPAddress AgentTable: " + [string] $in.SIPAddress + " ADUserObject:$uoSIP "
		}
		
	}

	end
	{

	}

}

Import-Csv $agentFile | Do-All( $false ) 
====================

 

If anyone wants to modify this code to directly read from the Agent table instead of a local csv dump of it, please post your code.

Another interesting point about updating agents from distribution lists is that in OCS after a certain patch level, the xml file OcsAppServerHost.exe.config included a section where the user could set the update frequency (default 8 hours) see http://support.microsoft.com/kb/975893 . In Lync, the xml code fragment is missing from OcsAppServerHost.exe.config but you can cut and paste it (carefully because the target file now contains other code !!) from the Knowledge base article or here is the relevant part with an example of a 30 Minute refresh cycle ...

  <appSettings>
    <add key="AdUpdateInterval" value="30" />
  </appSettings>

This works after the response group service is restarted.

 

 

 

 

 

 

 

 

 

 

June 13th, 2011 1:22pm

Additional Information:

Microsoft support has now delivered a patch for the Logging Tool which should solve the problem of not being able to trace RGS components under Lync. This is in fact a replacement for the file "default.tmx" which resides in "...\program files\common files\microsofr lync server 2010\tracing. This will be available generally in a future patch. In the meantime, we have been asked not redistribute this patch.

The Synchronisation Task Crash problem is still under investigation.

 

Free Windows Admin Tool Kit Click here and download it now
June 19th, 2011 3:28pm

We have got a confirmation that this issue  will not be fixed until wave 15 so here is our fix. It is set up as a scheduled task and "repairs" the SIP addresses which would otherwise cause the "Update from Distribution List" process to crash.

#
# Identify and correct bad SIP Addresses in  RGS Agent Table
# 
# 
# Get AD User object based on SID in Agent Table
# Compare SIP Address in Agent Table with that in Userobject.
# Report and correct Mismatches
# Fixed to allow mx 3 changes per run
#
# Adjust SQL Parameters to suit.
#
# Douglas Miles 10.06.2011
# 
# Version 01.09.2011 Update database and prepare for calling from scheduled task.
#

set-psdebug -strict


$SqlServer = "sg000501c-v2,14330"  # <<<<< change this sample formats   "sg000501c-v2,14330" or  "sg000802\rtc"  <<<<< 

$log = "D:\Scripts\RGS_Agent_Table_Repair\log\RGS_AgentTableBadSipAddress.txt"
$SqlCatalog =  "rgsconfig"
$DataSet = New-Object System.Data.DataSet
$global:changeCount = 0


Function Get-AgentTable
{
	# Get Agent Table fron SQL Server
	# http://myitforum.com/cs2/blogs/dhite/archive/2007/06/03/powershell-mini-sql-query-analyzer.aspx
	#

	$SqlQuery = "select UserSid, SipAddress from dbo.Agents"


 	$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
	$SqlConnection.ConnectionString = "Server = $SqlServer; Database = $SqlCatalog; Integrated Security = True"


	$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
	$SqlCmd.CommandText = $SqlQuery
	$SqlCmd.Connection = $SqlConnection

	$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
	$SqlAdapter.SelectCommand = $SqlCmd

	# $DataSet = New-Object System.Data.DataSet
	$SqlAdapter.Fill($DataSet)
	$SqlConnection.Close()
	
}


Function Correct-AgentSip
{
        Param (  $sid , $newSip )

#
#  called by e.g. Correct-AgentSip 0x0105000000000005150000000D7A5A33C6BB507A07E53B2BEC720100 'sip:john.smith@swisscom.com'
#

	$SqlQuery = "update [rgsconfig].[dbo].[Agents] `n set SipAddress=`'$newSip`' `n Where UserSid = $sid "


 	$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
	$SqlConnection.ConnectionString = "Server = $SqlServer; Database = $SqlCatalog; Integrated Security = True"


	$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
	$SqlCmd.CommandText = $SqlQuery
	$SqlCmd.Connection = $SqlConnection

	$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
	$SqlAdapter.SelectCommand = $SqlCmd

	$DataSetX = New-Object System.Data.DataSet
	$SqlAdapter.Fill($DataSetX)
	$SqlConnection.Close()
	
}



Function Get-ADUser
{
	# bits from http://www.wisesoft.co.uk/scripts/

	Param($Container, $filter)

	$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
	$root = [ADSI] "LDAP://$($dom.Name)"

	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher 
	$objSearcher.SearchRoot = $root
	$objSearcher.PageSize = 1000
	$objSearcher.Filter = $Filter

	$objSearcher.FindAll()
}



Function Do-All ( $isDebug )
{

	begin
	{

	}

	process
	{
		$in = $_

		
		#convert SID from format "0x0105000000000005150000000D7A5A33C6BB507A07E53B2BC6590400"
       		# to \01\05\00\00\00\00\00\05\15\00\00\00\0d\7a\5a\33\c6\bb\50\7a\07\e5\3b\2b\c6\59\04\00  format for the LDAP Query...
 

		$sidSep = '' 
		$in.UserSID  | foreach-object { $sidSep += $("\{0:x2}" -f $_) }

		
		#$sidSep + ' - ' + $in.SIPAddress
		

		$uobj = Get-adUser -filter "(objectSid=$sidSep)" 

		$uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
		
		if ( $uoSIP.tolower() -ne $in.SIPAddress.tolower() -and $global:changeCount -le 3 )
		{
			
			"Unmatched SIPAddress AgentTable: " + [string] $in.SIPAddress + " ADUserObject:$uoSIP "  >> $log
			$global:changeCount++ 
                        

			# convert SID so it can be used to query the DB
			$inSid = ''
                        $in.UserSID  | foreach-object { $inSid += $("{0:x2}" -f $_) }
                        $inSid = '0x' + $inSid.toUpper()

                        "Correct-AgentSip $inSid  $uoSIP"   >> $log
			 Correct-AgentSip  $inSid  $uoSIP    >> $log
		}
		
	}

	end
	{

	}

}

#
#  main
#

get-date -uformat "%Y.%m.%d %T" >> $log

Get-AgentTable

$DataSet.Tables[0]  | Do-All( $false ) 

"Entries changed this run = $global:changeCount " >> $log


 

September 2nd, 2011 5:15pm

Thanks for the precise and detailed explanation. I will add this entry to my ever-growing knowledge base, not to offer this feature to any customer :)
Free Windows Admin Tool Kit Click here and download it now
September 15th, 2011 9:51pm

Just to say this script has just helped us massively. We are changing sip domain name and having to wait for the deletions was just taking to long. Thanks!
August 22nd, 2012 7:33pm

We have got a confirmation that this issue  will not be fixed until wave 15 so here is our fix. It is set up as a scheduled task and "repairs" the SIP addresses which would otherwise cause the "Update from Distribution List" process to crash.

#
# Identify and correct bad SIP Addresses in  RGS Agent Table
# 
# 
# Get AD User object based on SID in Agent Table
# Compare SIP Address in Agent Table with that in Userobject.
# Report and correct Mismatches
# Fixed to allow mx 3 changes per run
#
# Adjust SQL Parameters to suit.
#
# Douglas Miles 10.06.2011
# 
# Version 01.09.2011 Update database and prepare for calling from scheduled task.
#

set-psdebug -strict


$SqlServer = "sg000501c-v2,14330"  # <<<<< change this sample formats   "sg000501c-v2,14330" or  "sg000802\rtc"  <<<<< 

$log = "D:\Scripts\RGS_Agent_Table_Repair\log\RGS_AgentTableBadSipAddress.txt"
$SqlCatalog =  "rgsconfig"
$DataSet = New-Object System.Data.DataSet
$global:changeCount = 0


Function Get-AgentTable
{
	# Get Agent Table fron SQL Server
	# http://myitforum.com/cs2/blogs/dhite/archive/2007/06/03/powershell-mini-sql-query-analyzer.aspx
	#

	$SqlQuery = "select UserSid, SipAddress from dbo.Agents"


 	$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
	$SqlConnection.ConnectionString = "Server = $SqlServer; Database = $SqlCatalog; Integrated Security = True"


	$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
	$SqlCmd.CommandText = $SqlQuery
	$SqlCmd.Connection = $SqlConnection

	$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
	$SqlAdapter.SelectCommand = $SqlCmd

	# $DataSet = New-Object System.Data.DataSet
	$SqlAdapter.Fill($DataSet)
	$SqlConnection.Close()
	
}


Function Correct-AgentSip
{
        Param (  $sid , $newSip )

#
#  called by e.g. Correct-AgentSip 0x0105000000000005150000000D7A5A33C6BB507A07E53B2BEC720100 'sip:john.smith@swisscom.com'
#

	$SqlQuery = "update [rgsconfig].[dbo].[Agents] `n set SipAddress=`'$newSip`' `n Where UserSid = $sid "


 	$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
	$SqlConnection.ConnectionString = "Server = $SqlServer; Database = $SqlCatalog; Integrated Security = True"


	$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
	$SqlCmd.CommandText = $SqlQuery
	$SqlCmd.Connection = $SqlConnection

	$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
	$SqlAdapter.SelectCommand = $SqlCmd

	$DataSetX = New-Object System.Data.DataSet
	$SqlAdapter.Fill($DataSetX)
	$SqlConnection.Close()
	
}



Function Get-ADUser
{
	# bits from http://www.wisesoft.co.uk/scripts/

	Param($Container, $filter)

	$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
	$root = [ADSI] "LDAP://$($dom.Name)"

	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher 
	$objSearcher.SearchRoot = $root
	$objSearcher.PageSize = 1000
	$objSearcher.Filter = $Filter

	$objSearcher.FindAll()
}



Function Do-All ( $isDebug )
{

	begin
	{

	}

	process
	{
		$in = $_

		
		#convert SID from format "0x0105000000000005150000000D7A5A33C6BB507A07E53B2BC6590400"
       		# to \01\05\00\00\00\00\00\05\15\00\00\00\0d\7a\5a\33\c6\bb\50\7a\07\e5\3b\2b\c6\59\04\00  format for the LDAP Query...
 

		$sidSep = '' 
		$in.UserSID  | foreach-object { $sidSep += $("\{0:x2}" -f $_) }

		
		#$sidSep + ' - ' + $in.SIPAddress
		

		$uobj = Get-adUser -filter "(objectSid=$sidSep)" 

		$uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
		
		if ( $uoSIP.tolower() -ne $in.SIPAddress.tolower() -and $global:changeCount -le 3 )
		{
			
			"Unmatched SIPAddress AgentTable: " + [string] $in.SIPAddress + " ADUserObject:$uoSIP "  >> $log
			$global:changeCount++ 
                        

			# convert SID so it can be used to query the DB
			$inSid = ''
                        $in.UserSID  | foreach-object { $inSid += $("{0:x2}" -f $_) }
                        $inSid = '0x' + $inSid.toUpper()

                        "Correct-AgentSip $inSid  $uoSIP"   >> $log
			 Correct-AgentSip  $inSid  $uoSIP    >> $log
		}
		
	}

	end
	{

	}

}

#
#  main
#

get-date -uformat "%Y.%m.%d %T" >> $log

Get-AgentTable

$DataSet.Tables[0]  | Do-All( $false ) 

"Entries changed this run = $global:changeCount " >> $log


 

When I try to run this command, I receive the following error:

128
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Exception calling "Fill" with "1" argument(s): "Cannot insert duplicate key row in object 'dbo.Agents' with unique index 'IX_Agents_SipAddress'. The duplicate key value is 
(sip:user1@domain.com).
The statement has been terminated."
At C:\Users\administrator\Desktop\RGC\RGS.ps1:78 char:2
+     $SqlAdapter.Fill($DataSetX)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SqlException
 
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Exception calling "Fill" with "1" argument(s): "Cannot insert duplicate key row in object 'dbo.Agents' with unique index 'IX_Agents_SipAddress'. The duplicate key value is 
(sip:user2@domain.com).
The statement has been terminated."
At C:\Users\administrator\Desktop\RGC\RGS.ps1:78 char:2
+     $SqlAdapter.Fill($DataSetX)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SqlException
 
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Exception calling "Fill" with "1" argument(s): "Cannot insert duplicate key row in object 'dbo.Agents' with unique index 'IX_Agents_SipAddress'. The duplicate key value is 
(sip:user3@domain.com).
The statement has been terminated."
At C:\Users\administrator\Desktop\RGC\RGS.ps1:78 char:2
+     $SqlAdapter.Fill($DataSetX)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SqlException
 
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Exception calling "Fill" with "1" argument(s): "Cannot insert duplicate key row in object 'dbo.Agents' with unique index 'IX_Agents_SipAddress'. The duplicate key value is 
(sip:user4@domain.com).
The statement has been terminated."
At C:\Users\administrator\Desktop\RGC\RGS.ps1:78 char:2
+     $SqlAdapter.Fill($DataSetX)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SqlException
 
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
You cannot call a method on a null-valued expression.
At C:\Users\administrator\Desktop\RGC\RGS.ps1:130 char:3
+         $uoSIP = [string] $uobj.getdirectoryentry().'msrtcsip-primaryuseraddress'
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 
Can someone help me with this?

Thank you.

Free Windows Admin Tool Kit Click here and download it now
February 19th, 2014 10:46am

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics