• About me…

ConfigMgr.nl

VMware, Automation and more

  • About me…

SCCM – Duplicate Device Records

06-06-2017 ConfigMgr Powershell SCCM Script 4 Comments

A few days back I was investigating duplicate device records in SCCM. Basically, there are two major reasons for duplicate device records:

  1. Reinstalling a device
  2. Active Directory delta discovery

For the first reason SCCM has some built-in solutions. In the Hierarchy Settings there’s an option for conflicting client records. This option looks at the hardware ID’s to detect duplicates and gives you the option to automatically resolve the conflict or to do it manually.

There’s an old Microsoft article that describes how SCCM (SMS) handles duplicate ID’s and how you can find them.

If you look at the image you can see that in my test environment the option to manually resolve conflicts is selected. One way of resolving the conflicts manually is to create a Status Filter Rule.

Event 2642:

Configuration Manager has detected a record that might be conflicting with the following client record in the site database: <GUID> . Possible cause: <GUID> has been imaged, recovered from backup, or migrated to a new computer. Solution: In the Configuration Manager console, under Computer Management, in the Conflicting Records node, right click <GUID> and then choose one of the following options: Merge to match the conflicting record with the existing record, New to create a new client record, or Block to block this record from being a client.

BTW the location in SCCM 1702 is Monitoring, System Status, Conflicting Records node.

MergeConflictingRecords.ps1

####################################################################################
#                                                                                  #
# Configuration                                                                    #
#                                                                                  #
####################################################################################

# SCCM Servername
Set-Variable serverName "servername" -Option Constant

# test = $true: shows information about conflicting records without merging them.
# test = $false: shows information about conflicting records and merges them.
Set-Variable test $false -Option Constant

####################################################################################
#                                                                                  #
# Functions                                                                        #
#                                                                                  #
####################################################################################

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    Split-Path $Invocation.MyCommand.Path
}

function Log
{
    param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $message
    ,
        [Parameter(Mandatory=$true)]
        [ValidateSet("Info","Debug","Warn","Error")]
        [String]
        $type
    ,
        [Parameter(Mandatory=$true)]
        [ValidateSet("Console","LogFile","Both")]
        [String]
        $outputMode
    )
    
    $dateTimeString = Get-Date -Format "yyyy-MM-dd HH:mm:sszz"
    $output = ($dateTimeString + "`t" + $type.ToUpper() + "`t" + $message)
    
    if ($outputMode -eq "Console" -OR $outputMode -eq "Both")
    {
        Write-Host $output
    }
    
    if ($outputMode -eq "LogFile" -OR $outputMode -eq "Both")
    {
        try
        {
            Add-Content $logFile -Value $output -ErrorAction Stop
        }
        catch
        {
			Log ("Failed to write to log file: """ + $logFile + """.") -OutputMode Console -Type Error
        	Log ("[" + $_.Exception.GetType().FullName + "] " + $_.Exception.Message) -OutputMode Console -Type Error
        }
    }
}

function GetSCCMSiteCode
{
    try
    {    
        $sccmProvider = Get-WMIObject -ComputerName $serverName -Namespace "root\SMS" -Class "SMS_ProviderLocation"
        $sccmProvider | foreach-object{if ($_.ProviderForLocalSite -eq $true){$siteCode=$_.sitecode}}
    }
    catch
    {
        Log ("[" + $_.Exception.GetType().FullName + "] " + $_.Exception.Message) -OutputMode Both -Type Error
        exit 1
    }
    
    if (!$siteCode)
    {
        Log ("Failed to determine site code.") -OutputMode Both -Type Error
        exit 1
    }
    
    return $siteCode
}

function MergeRecords
{
	param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $SMSID
	)
    
    $pendingRegClass = [WmiClass]("\\$serverName\ROOT\SMS\Site_" + $sccmSiteCode + ":SMS_PendingRegistrationRecord")
    
    $inParams = $pendingRegClass.PSBase.GetMethodParameters("ResolvePendingRegistrationRecord")
    $inParams.SMSID = $SMSID
    $inParams.Action = 1 # Action 1 = Merge
	
    Log ("Merging records...") -OutputMode Both -Type Info
	
	try
    {
		$result = $pendingRegClass.PSBase.InvokeMethod("ResolvePendingRegistrationRecord", $inParams, $Null)
		if ($result["returnValue"] -eq "0")
		{
			Log ("Successfully merged records. Return value: " + $result["returnValue"]) -OutputMode Both -Type Info
		}
		else
		{
			Log ("Failed to merge records! Return value: " + $result["returnValue"]) -OutputMode Both -Type Error
		}
	}
    catch
    {
        Log ("Failed to merge records!") -OutputMode Both -Type Error
        Log ("[" + $_.Exception.GetType().FullName + "] " + $_.Exception.Message) -OutputMode Both -Type Error
    }
}

####################################################################################
#                                                                                  #
# Main Code                                                                        #
#                                                                                  #
####################################################################################

# Logfile
Set-Variable logFile ((Get-ScriptDirectory) + "\MergeConflictingRecords.log") -Option Constant -ErrorAction SilentlyContinue

# Determine SCCM Site Code
Set-Variable sccmSiteCode (GetSCCMSiteCode) -Option Constant -ErrorAction SilentlyContinue

# Get conflicting records
$pendingRegistrations = Get-WmiObject -class "SMS_PendingRegistrationRecord" -namespace ("root\sms\site_" + $sccmSiteCode) -impersonation 3 -computername $serverName

# Merge conflicting records
if ($pendingRegistrations)
{
    Log ("Found conflicting records.") -OutputMode Both -Type Info
    
    foreach ($item in $pendingRegistrations)
    {
        Log ("**************************************************************************************************************************************") -OutputMode Both -Type Info
        
        Log ("NetBIOS Name: " + $item.NetBiosName) -OutputMode Both -Type Info
        Log ("SMSID: " + $item.SMSID) -OutputMode Both -Type Info
        Log ("Conflicting SMSID: " + $item.ConflictSMSID) -OutputMode Both -Type Info
        Log ("Hardware ID: " + $item.HardwareID) -OutputMode Both -Type Info
        
		if (!$test)
    	{
        	MergeRecords -SMSID $item.SMSID
		}
        
        Log ("**************************************************************************************************************************************") -OutputMode Both -Type Info
    }
}
else
{
    Log ("No conflicting records found.") -OutputMode Both -Type Info
}

However, for the second reason there is no built-in solution. So I had to create one.

The first step was getting some background information about the issue. There’s a Microsoft article about the issue.

As you can see the article is old (2011) and it’s not using powershell. So I rewrote the script from VB to powershell. One other change is that the VB script kicks of a full AD discovery but I will use the regular scheduled delta discovery method. For this to work the AD object must be changed. I found one blog that uses the same approach but uses an EXE file for this.  I decided to incorporate this into the powershell script.

DeleteDuplicateRecords.ps1

<# .SYNOPSIS Deletes duplicate device records .DESCRIPTION Use this script if you need to delete duplicate device records because of a timing issue between OSD and AD delta discovery .PARAMETER SiteServer Site server name with SMS Provider installed .PARAMETER MachineName Name of the duplicate device .EXAMPLE .\DeleteDuplicateRecords.ps1 -SiteServer "SERVER" -MachineName "MACHINE" Deletes duplicate records of the device with the name "MACHINE" .NOTES Script name: DeleteDuplicateRecords.ps1 Author: Jeroen Buren DateCreated: 02-06-2017 #>

[CmdletBinding(SupportsShouldProcess=$true)]

param(
    [parameter(Mandatory=$true,HelpMessage="Site server where the SMS Provider is installed")]
    [ValidateScript({Test-Connection -ComputerName $_ -Count 1 -Quiet})]
    [string]$SiteServer,
    [parameter(Mandatory=$true,HelpMessage="Name of the device")]
    [string]$MachineName
)

####################################################################################
#                                                                                  #
# Functions                                                                        #
#                                                                                  #
####################################################################################

function Get-ScriptDirectory {
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    Split-Path $Invocation.MyCommand.Path
}

function Log {
    param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $message
    ,
        [Parameter(Mandatory=$true)]
        [ValidateSet("Info","Debug","Warn","Error")]
        [String]
        $type
    ,
        [Parameter(Mandatory=$true)]
        [ValidateSet("Console","LogFile","Both")]
        [String]
        $outputMode
    )
    
    $dateTimeString = Get-Date -Format "yyyy-MM-dd HH:mm:sszz"
    $output = ($dateTimeString + " " + $type.ToUpper() + " " + $message)
    
    if ($outputMode -eq "Console" -OR $outputMode -eq "Both")
    {
        Write-Host $output
    }
    
    if ($outputMode -eq "LogFile" -OR $outputMode -eq "Both")
    {
        try
        {
            Add-Content $logFile -Value $output -ErrorAction Stop
        }
        catch
        {
			Log ("Failed to write to log file: """ + $logFile + """.") -OutputMode Console -Type Error
        	Log ("[" + $_.Exception.GetType().FullName + "] " + $_.Exception.Message) -OutputMode Console -Type Error
        }
    }
}

function GetSCCMSiteCode {
    try
    {    
        $sccmProvider = Get-WMIObject -ComputerName $SiteServer -Namespace "root\SMS" -Class "SMS_ProviderLocation"
        $sccmProvider | foreach-object{if ($_.ProviderForLocalSite -eq $true){$siteCode=$_.sitecode}}
    }
    catch
    {
        Log ("[" + $_.Exception.GetType().FullName + "] " + $_.Exception.Message) -OutputMode Both -Type Error
        exit 1
    }
    
    if (!$siteCode)
    {
        Log ("Failed to determine site code.") -OutputMode Both -Type Error
        exit 1
    }
    
    return $siteCode
}

####################################################################################
#                                                                                  #
# Main Code                                                                        #
#                                                                                  #
####################################################################################

# Logfile
Set-Variable logFile ((Get-ScriptDirectory) + "\DeleteDuplicateRecords.log") -Option Constant -ErrorAction SilentlyContinue

# Determine SCCM Site Code
Set-Variable sccmSiteCode (GetSCCMSiteCode) -Option Constant -ErrorAction SilentlyContinue

# If you give the computer a new machine name, you need to query for the new machine name
$StatusMessageQuery = "select RecordID from SMS_StatMsg where MessageID = 11171 and MachineName = '" + $MachineName + "' order by RecordID desc"
$StatusMessages = gwmi -Query $StatusMessageQuery -Namespace ("root\sms\site_" + $sccmSiteCode) -ComputerName $SiteServer

If ($StatusMessages.Count -lt 1) {
    Log ("No Status Message with ID = 11171 and MachineName = $MachineName found. Exiting...") -OutputMode LogFile -Type Info
    #Exit   
}
Else {
    $RecordID = $StatusMessages[0].RecordID
    Log ("Status Message RecordID = $RecordID") -outputMode LogFile -type Info
    $StatusMessageAttributeQuery = "select AttributeValue from SMS_StatMsgAttributes where RecordID = '" + $RecordID + "' and AttributeID = 408"
    $StatusMessageAttributes = gwmi -Query $StatusMessageAttributeQuery -Namespace ("root\sms\site_" + $sccmSiteCode) -ComputerName $SiteServer
    If ($StatusMessageAttributes.Count -lt 1) {
        Log ("No Status Message Attribute with AttributeID = 408 and RecordID = $RecordID found. Exiting...") -outputMode LogFile -type Info
    }
    Else {
        $GUID = $StatusMessageAttributes[0].AttributeValue
        Log ("SMS Client GUID = $GUID") -outputMode LogFile -type Info
        $MachineNameQuery = "select NetbiosName from SMS_R_System where SMSUniqueIdentifier = '" + $GUID + "'"
        $MachineNames = gwmi -Query $MachineNameQuery -Namespace ("root\sms\site_" + $sccmSiteCode) -ComputerName $SiteServer
        If ($MachineNames.Count -lt 1) {
            Log ("No Systems with SMSGUID = $GUID found. Using the Machine Name in the status message.") -outputMode LogFile -type Info
        }
        Else {
            $MachineName = $MachineNames[0].NetbiosName
            Log ("New MachineName = $MachineName" ) -outputMode LogFile -type Info
        }
    }
}

# Find the system with the specific machine name.

$duplicateRecordsQuery = "select * from SMS_R_System where NetBIOSName = '" + $MachineName + "'"
$duplicateRecords = gwmi -Query $duplicateRecordsQuery -Namespace ("root\sms\site_" + $sccmSiteCode) -ComputerName $SiteServer

If ($duplicateRecords.Count -lt 1) {
    Log ("Didn't find a duplicate record for the machine, exiting...") -outputMode LogFile -type Info
}
Else {
    # Delete if the Client, Client Type, Hardware ID, SMBIOSGUID, SMSUniqueIdentifier is null
    Foreach ($item in $duplicateRecords) {
        $Active = $item.Active
        $Client = $item.Client
        $ClientType = $item.ClientType
        $HardwareID = $item.HardwareID
        $ResourceId = $item.ResourceId
        $SMBIOSGUID = $item.SMBIOSGUID
        $SMSUniqueIdentifier = $item.SMSUniqueIdentifier
        If (($Active -eq $null) -and ($Client -eq $null) -and ($ClientType -eq $null) -and ($HardwareID -eq $null) -and ($SMBIOSGUID -eq $null) -and ($SMSUniqueIdentifier -eq $null)) {
            Log ("Delete this one: ResourceId = $ResourceId") -outputMode LogFile -type Info
            # Delete Record when there's duplicate and it's active/SMBIOSGUID, etc is null
            $item.Delete()
            Log ("Deleted item: ResourceId = $ResourceId") -outputMode LogFile -type Info
        }
    }
}

# Modify AD computer object so delta discovery will pick this up.
Get-ADComputer -Identity $MachineName | Set-ADComputer -Replace @{adminDescription="Touched by SCCM - $dateTimeString"}
clientDuplicatesccmSystem Center

Powershell - Get SCCM Client info from WMI

Frequent, continuous releases coming for System Center

4 thoughts on “SCCM – Duplicate Device Records”

  1. Christoph
    19-12-2019 at 11:35

    Howto use second scrpit ?

    DeleteDuplicateRecords.ps1 -SiteServer “SERVER” -MachineName “MACHINE”

    I get errors …

    Reply
    • admin
      19-12-2019 at 21:40

      And what is the error?

      Reply
  2. Rick Gates
    21-04-2020 at 20:38

    Is there a way to modify the second script to detect duplicate computers and delete the older ones duplicate records without having to pass in the computer names?

    Reply
  3. Jeroen Buren
    21-04-2020 at 21:09

    It’s been a while but I think you could enclose the script in a foreach where you would feed it all computer names…

    Reply
Leave a Reply to Jeroen Buren Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Recent Posts

  • Testing Script Runtime Service for vSphere – part 2
  • Reporting on your Windows Server backup
  • Testing Script Runtime Service for vSphere – part 1
  • Using ADFS with vCenter 7
  • Network Port Diagram vSphere
  • Joining ESXi hosts to AD using Authentication Proxy in vCenter 7 (updated 04-12-2020)
  • Windows Server 2019 customization issue
  • Packer and WinRM – mystery resolved
  • Using LDAPS with vCenter and AD
  • Backup your homelab… for free!

Archives

Categories

AnyLinq (1) Azure (2) ConfigMgr (8) DIY (4) PowerCli (9) Powershell (10) SCCM (9) Script (12) Solutions (16) System Center (3) VMware (31) vRealize Automation (1) vRealize Orchestrator (1)

Jeroen BurenFollow

Jeroen Buren
jeroen_burenJeroen Buren@jeroen_buren·
23 Feb

Eerlijk gezegd ben ik niet zo opstandig maar dit kan je toch niet volhouden? Gooi wat mij betreft de winkels, sportscholen en restaurants maar open! Ik weet wel op wie ik niet ga stemmen... #persconferentie #klaarmetRutte

Reply on Twitter 1364303792364269568Retweet on Twitter 1364303792364269568Like on Twitter 13643037923642695682Twitter 1364303792364269568
jeroen_burenJeroen Buren@jeroen_buren·
22 Feb

Installed vSphere with K8S using this awesome script (https://github.com/lamw/vghetto-vsphere-with-kubernetes-external-nsxt-automated-lab-deployment) from @lamw. The only thing I could not get running was the yelb demo app... Could it be the NSX-T 3.1 Limited Export edition? #vExpert #NSX

Reply on Twitter 1363884748205142020Retweet on Twitter 1363884748205142020Like on Twitter 1363884748205142020Twitter 1363884748205142020
Retweet on TwitterJeroen Buren Retweeted
Annemiek73Annemiek Meijer@Annemiek73·
16 Jan

Running man kan altijd! Ook tijdens #VVAL2021 @VriendvanAmstel #RunningMan #VVALS

Reply on Twitter 1350537030879608833Retweet on Twitter 13505370308796088332Like on Twitter 135053703087960883326Twitter 1350537030879608833
Retweet on TwitterJeroen Buren Retweeted
PowerCLIVMware PowerCLI@PowerCLI·
11 Jan

Do check out the SRS roadmap here. Feel free to influence the roadmap by submitting your requests. https://github.com/vmware/script-runtime-service-for-vsphere/projects/2 https://twitter.com/PowerCLI/status/1337370157551796225

VMware PowerCLI@PowerCLI

Introduction to Script Runtime Service (SRS) for vSphere https://blogs.vmware.com/PowerCLI/2020/12/introduction-to-script-runtime-service-srs-for-vsphere.html

Reply on Twitter 1348606798492618752Retweet on Twitter 13486067984926187524Like on Twitter 13486067984926187528Twitter 1348606798492618752
jeroen_burenJeroen Buren@jeroen_buren·
7 Jan

New blogpost about Script Runtime Service for vSphere.
https://configmgr.nl/2021/01/07/testing-script-runtime-service-for-vsphere-part-1/

#VMware #vSphere #SRS #PowerCLI

Reply on Twitter 1347191971002118144Retweet on Twitter 1347191971002118144Like on Twitter 1347191971002118144Twitter 1347191971002118144
Load More...
Proudly powered by WordPress | Theme: Doo by ThemeVS.