Script to fix duplicate SMS/SCCM GUID’s

This post was written by stephen on October 3, 2008
Posted Under: Microsoft

I recently had an interesting issue come about with a grip of old school legacy machines scattered around several offices that contained identical disk images and SMS/SCCM agent GUID’s. The unintended consequences of this situation are computers improperly referenced in a collection plan. In addition machines were improperly re-imaged and applications inadvertently deployed. Our in-house solution was to create a script to parse a CSV (comma delimited) file including all machine names with duplicate GUID’s and run the following tasks:

Sponsors... Article continues below.


1) Make sure machine is pingable
2) Stop SMS/SCCM Agent
3) Delete  SMSCFG.ini
4) Start SMS/SCCM Agent to generate new SMSCFG.ini including GUID.
5) Report results to  c:\FixSMSGUID_Results.csv

NOTE: Be sure you have sufficient rights on the destination machines before running this script!

'------------- START COPY ----------------'

'###########################################################################
'## VB Script: FixSMSGUID.vbs ##############################################
'## Purpose: Delete SMS.ini and restart SMS service. #######################
'## Author: shnizep atnospam gmail.com #####################################
'## Created: 2008-04-22 ####################################################
'## Last Modified: 2008-09-30 ##############################################
'###########################################################################
'## Changelog

On Error Resume Next

Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20
Const ForReading = 1
Const strOutputFile = "c:\FixSMSGUID_Results.csv"
'****************************************************************************
' Obtain CSV list of file.
'****************************************************************************

'Set objArgs = WScript.Arguments
'If objArgs.Count <> 0 Then
'	Set str = WScript.Arguments.Item(1)
'	Set intMode = WScript.Arguments.Item(2)
'ElseIf objArgs.Count = 0 Then

'Via browse dialog box if cmd line arguments null

MsgBox "Select duplicate GUID CSV"
Set objDialog = Createobject("Useraccounts.Commondialog")
objDialog.Filter = "Duplicate GUID CSV|*.csv"
'objDialog.Flags = &H0200
objDialog.Filterindex = 1
objDialog.InitialDir = "C:\"
'objDialog.dialogTitle = "Select a file"
intResult = objDialog.Showopen
If(intResult = 0) Then
	Wscript.Echo "No duplicate GUID CSV file selected! Exiting.."
	Wscript.quit
Else
  strHostList = objDialog.FileName
End If

'****************************************************************************
'END CSV file.
'****************************************************************************

'****************************************************************************

Dim arrDupGUID(1)
Dim i, sleepDur, intSleep, strService, objService, errReturn, strMsg
intSleep = 10000
sleepDur = 0
intSleepRetry = 1000
strService = "ccmexec"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile _
	(strHostList, ForReading)
Wscript.echo "Removing duplicate GUID's...."
Wscript.echo 

'Create Fix SMS GUID status CSV
If objFSO.FileExists(strOutputFile) Then
	objFSO.DeleteFile(strOutputFile)
End If
Set objOutputFile = objFSO.CreateTextFile(strOutputFile, True)

'Loop through each line of file
Do Until objTextFile.AtEndOfStream
	strNextLine = objTextFile.Readline
	i = 0
	For Each strItem In CSVParse(strNextLine)
		arrDupGUID(i) = strItem
		i = i + 1
	Next

	strHost = arrDupGUID(0)
	strStatus = arrDupGUID(1)

	Wscript.Echo "Hostname: "& strHost 

	'Wscript.echo "Machines status: "& strStatus
	'If machines GUID has not already been fixed.
	If strStatus = "Done" Then
		Wscript.echo "Host already done, skipping: "& strHost
		strMsg = "Done"
		Wscript.echo
	Else
		If fctIsAlive(strHost) = False Then
			strMsg = "Ping timeout"
			Wscript.echo strMsg
			Wscript.echo
		Else
			If Len(strHost) = 0 Then
				Wscript.echo "Hostname string is null, check your IMPORT file!...Exiting..."
				Wscript.quit
			End If

			Set objWMIService = GetObject("winmgmts:" _
				& "{impersonationLevel=impersonate}!\\" & strHost)
			If Err.Number Then
				'Wscript.Echo Err.Number
				Wscript.Echo "Error: "& Err.Description
				'Wscript.Echo "Could not connect to the WMI Service"
				Wscript.Echo
				strMsg = Err.Description
			Else
				Set objService = objWMIService.Get("Win32_Service.Name='" & strService & "'")

				'Get Windows Directory
				Set objOS = objWMIService.ExecQuery("Select * from Win32_OperatingSystem")
				For Each colOS In objOS
					strWindowsDir = colOS.WindowsDirectory
				Next
				'Wscript.Echo "Windows Dir: "& strWindowsDir
				Set objIniDel = GetObject("winMgmts:CIM_DataFile.Name='" & strWindowsDir & "\SMSCFG.INI'")

				'##########################################################
				'Stop the service, delete SMSCFG.INI and start the service.
				'##########################################################

				'Stop SMS Host Agent Service
				Wscript.Echo "Stopping SMS Agent Host service"
				errReturn = objService.StopService()
				If errReturn <> 0 Then
					'Wscript.Echo "Error: " & errReturn
					strMsg = errReturn.Description
					Wscript.Echo strMsg
				End If
				Wscript.Echo "Waiting..."
				'Wscript.Echo "State: "& objService.State
				Wscript.Sleep intSleep
				strState = objService.State

				'Make sure service is stopped before continuing
				Do Until strState = "Stopped"
					Wscript.Echo "Service still stopping, waiting..."
					Wscript.Sleep intSleepRetry
					Set objState = objWMIService.Get("Win32_Service.Name='" & strService & "'")
					strState = objState.State
					sleepDur = sleepDur + 1
					If sleepDur = 30 Then
						strMsg = "Timeout Could not stop SMS Agent service"
						strState = "Stopped"
					End If
					'Wscript.Echo "State: "& strState
				Loop

				'Delete SMSCFG.ini file
				Wscript.Echo "Deleting SMSCFG.ini file"
				errReturn = objIniDel.delete
				If errReturn <> 0 Then
					'Wscript.Echo "Error: " & errReturn
					Wscript.Echo "Could not delete SMSCFG.ini file"
					strMsg = "Could not delete SMSCFG.ini file"
				End If

				'Start SMS Host Agent Service
				Wscript.Echo "Starting SMS Agent Host service"
				errReturn = objService.StartService()
				If errReturn <> 0 Then
					'Wscript.Echo "Error: " & errReturn
					Wscript.Echo "Service could not start"
				End If

				'Reset sleep duration
				sleepDur = 0

				'Make sure service is started before continuing
				Do Until strState = "Running"
					Wscript.Echo "Service still starting, waiting..."
					Wscript.Sleep intSleepRetry
					Set objState = objWMIService.Get("Win32_Service.Name='" & strService & "'")
					strState = objState.State
					'Wscript.Echo "State: "& strState
					sleepDur = sleepDur + 1
					If sleepDur = 30 Then
						strMsg = "Timeout Could not Start SMS Agent service"
						strState = "Running"
					End If
				Loop
				Wscript.echo "SMS Host Service is Running"
				'Wscript.echo "Done with: "& strHost
				If Len(StrMsg) = 0 Then
					strMsg = "Done"
				End If

				Wscript.echo
			End If 'END If cannot connect
		End If ' If cannot ping
	End If 'END If machines GUID has not already been fixed.
	Err.Clear
	objOutputFile.Writeline strHost & ","& strMsg
	StrMsg = ""
Loop 'END - Loop through line of file
'END - Reading CSV
'Close file for Writing
objOutputFile.Close
Wscript.quit

Function fctIsAlive(strHostOrIP)
	'Function to make sure machine is pingable.
	'Stolen from http://www.fpschultze.de/uploads/wmiping.vbs.txt
Dim objSh, objCmd, strCmd
	strCmd     = "%ComSpec% /C %SystemRoot%\system32\ping.exe -n 1 " & strHostOrIP & " | " _
			   & "%SystemRoot%\system32\find.exe /c /i " & Chr(34) & "ttl=" & Chr(34)
	Set objSh  = WScript.CreateObject("WScript.Shell")
	Set objCmd = objSh.Exec(strCmd)
	fctIsAlive = CBool(Trim(objCmd.StdOut.ReadAll))
	Set objCmd = Nothing
	Set objSh  = Nothing
End Function

Function CSVParse(ByVal strLine)
    ' Function to parse comma delimited line and return array
    ' of field values.
	' Stolen from http://www.rlmueller.net/Programs/ReadCSV.txt

    Dim arrFields
    Dim blnIgnore
    Dim intFieldCount
    Dim intCursor
    Dim intStart
    Dim strChar
    Dim strValue

    Const QUOTE = """"
    Const QUOTE2 = """"""

    ' Check for empty string and return empty array.
    If (Len(Trim(strLine)) = 0) then
        CSVParse = Array()
        Exit Function
    End If

    ' Initialize.
    blnIgnore = False
    intFieldCount = 0
    intStart = 1
    arrFields = Array()

    ' Add "," to delimit the last field.
    strLine = strLine & ","

    ' Walk the string.
    For intCursor = 1 To Len(strLine)
        ' Get a character.
        strChar = Mid(strLine, intCursor, 1)
        Select Case strChar
            Case QUOTE
                ' Toggle the ignore flag.
                blnIgnore = Not blnIgnore
            Case ","
                If Not blnIgnore Then
                    ' Add element to the array.
                    ReDim Preserve arrFields(intFieldCount)
                    ' Makes sure the "field" has a non-zero length.
                    If (intCursor - intStart > 0) Then
                        ' Extract the field value.
                        strValue = Mid(strLine, intStart, _
                            intCursor - intStart)
                        ' If it's a quoted string, use Mid to
                        ' remove outer quotes and replace inner
                        ' doubled quotes with single.
                        If (Left(strValue, 1) = QUOTE) Then
                            arrFields(intFieldCount) = _
                                Replace(Mid(strValue, 2, _
                                Len(strValue) - 2), QUOTE2, QUOTE)
                        Else
                            arrFields(intFieldCount) = strValue
                        End If
                    Else
                        ' An empty field is an empty array element.
                        arrFields(intFieldCount) = Empty
                    End If
                    ' increment for next field.
                    intFieldCount = intFieldCount + 1
                    intStart = intCursor + 1
                End If
        End Select
    Next
    ' Return the array.
    CSVParse = arrFields
End Function

'------------- END COPY ------------------'

Please note that an alternative solution might be found within a Microsoft Technet SMSANDMOM blog entry I recently happened upon.

MY NORMAL DISCLAIMER APPLIES: Although I have used this script with success in my environment, every environment varies. You run this script at your own risk blogmynog.com and associates are in no way responsible for damages incurred running any provided scripts on blogmynog.com or associated websites. Be sure to test this in a non-production environment first before using it in production.

  • Share/Bookmark

Reader Comments

Have you tried running cscript c:\script.vbs in the command prompt?

#1 
Written By stephen on October 16th, 2008 @ 8:23 pm

Add a Comment

required, use real name
required, will not be published
optional, your blog address