Tuesday, October 10, 2006

Change GUID using VBS

Thanks to http://mystuff.clarke.co.nz/wsname.asp and http://myitforum.com/cs2/blogs/socal/archive/2006/04/03/19585.aspx

Instead of booting up the PC and finding out what the GUID was and then entering this into ADUC I wanted to be able to load the OS from RIS and then run a VBS program that would get the list of computers from the OU, retrieve the GUID from each of the computers, update the GUID in ADUC computer account, rename the computer to what we wanted and then reboot it. And during the RIS process the computer has loaded all the relevant programs from Active Directory using Group Policy.

The script I managed to put together is below. The bits I have added probably aren't very efficient but thanks to the two sites above.

Option Explicit

'**************************************************************************­*****
' Initialize the variables
Dim Com, Conn, Rs ' for ADO
Dim ADsEncodeBinaryData, strQuery, objRootDSE, strNamingContext
Dim newArr, strGUID
Dim objSysInfo
Dim strComputerDN
Dim objComputer
Dim boolWFMCapable
Dim str_WBEM_UUID
Dim szMac
Dim SystemSet
Dim szAdapterDescription
Dim SystemItem
Dim NetworkAdapterSet
Dim NetworkAdapter
Dim iMacCount
Dim cmdline
Dim oComp
Dim retval
Dim pGUID
Dim cnvt
Dim mymsg
Dim colNewComputers, objNewComputer, strComputer, strNewComputerName
Dim q, fso, objTextFile, strNextLine, arrComputerNameList

q = 0
Set fso=CreateObject("Scripting.FileSystemObject")
Const ForReading = 1
Set objTextFile = fso.OpenTextFile("c:\NewComputerName.txt", ForReading)
Do Until objTextFile.AtEndOfStream
strNextLine = objTextFile.Readline
arrComputerNameList = Split(strNextLine , ",")
Loop



Set colNewComputers = GetObject("LDAP://OU=OrgUnit,DC=domain,DC=com,DC=au")
For Each objNewComputer in colNewComputers
strComputer = objNewComputer.CN
strNewComputerName = arrComputerNameList(q)

'**************************************************************************­*****
' Connect to Active Directory and get the Root
'Set objRootDSE = GetObject ("LDAP://RootDSE")
'strNamingContext = objRootDSE.Get ("defaultNamingContext")
'Set objRootDSE = Nothing

'**************************************************************************­*****
' Connect to Active Directory and get the local computer name
' and get the computer object for processing
'Set objSysInfo = CreateObject("ADSystemInfo")
'strComputerDN = objSysInfo.ComputerName
Set objComputer = GetObject("LDAP://CN=" & strComputer & ",OU=OrgUnit,DC=domain,DC=com,DC=au")

'**************************************************************************­*****
' Connect to WBEM and try to retreive the UUID
Set SystemSet = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2").InstancesOf("Win32_ComputerSystemProduct")

For Each SystemItem In SystemSet
str_WBEM_UUID = SystemItem.UUID
If str_WBEM_UUID = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" Then
mymsg = "No system UUID could be found. This system does " & _
"not appear to support Intel's Wired For Management " & _
"specification. This script will now try and retrive " & _
"a MAC based UUID."
boolWFMCapable = 0
Else
mymsg = "Successfully retrieved a system UUID: " & str_WBEM_UUID & "."
boolWFMCapable = 1
End If
Next
wscript.echo mymsg

'**************************************************************************­*****
' Get a MAC based UUID if not available from above
If boolWFMCapable = 0 Then
Set NetworkAdapterSet = GetObject("winmgmts:").InstancesOf("Win32_NetworkAdapter")

iMACCount = 0
For Each NetworkAdapter In NetworkAdapterSet
If NetworkAdapter.AdapterType = "Ethernet 802.3" Then
szAdapterDescription = NetworkAdapter.Description
If NetworkAdapter.Description <> "Packet Scheduler Miniport" Then
szMAC = NetworkAdapter.MACAddress
str_WBEM_UUID = "00000000-0000-0000-0000-"&Replace(szMAC, ":", "")
mymsg = "Based on the network adapter with the description """ & _
szAdapterDescription & """, your system's UUID would " & _
"be " & str_WBEM_UUID & ". "
End If
End If
iMACCount = iMACCount + 1
Next
wscript.echo mymsg
End If
wscript.echo ""

'**************************************************************************­*****
' Display the guid
mymsg = "str_WBEM_UUID = " & str_WBEM_UUID
wscript.echo mymsg

'**************************************************************************­*****
' Pack the guid and display it again
str_WBEM_UUID = PackGUID(str_WBEM_UUID)
mymsg = "str_WBEM_UUID = " & str_WBEM_UUID
wscript.echo mymsg

'**************************************************************************­*****
' Convert the packed GUID to a Byte Array
Call ConvertHexStringToByteArray (str_WBEM_UUID, newArr)

'**************************************************************************­*****
' and write the Byte Array value into the Active Directory.
objComputer.Put "netbootGUID", newArr
objComputer.SetInfo


'********************************************************************************
' Change computer name on domain
Dim wbemLocator, wbemNameSpace, wbemProcess, wbemObjectSet, intProcessID, intReturnCode, strCommandLine, strAdminUser, strAdminPassword

strAdminUser = "domain\administrator"
strAdminPassword = "password"

Set wbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set wbemNameSpace = wbemLocator.ConnectServer(strComputer, "root\cimv2", strAdminUser, strAdminPassword)
Set wbemProcess = wbemNameSpace.Get("Win32_Process")
Set wbemObjectSet = wbemNameSpace.InstancesOf("Win32_Process")

'Set fso=CreateObject("Scripting.FileSystemObject")
fso.CopyFile "c:\wsname.exe", "\\" & strComputer & "\c$\windows\wsname.exe"

strCommandLine = "c:\windows\wsname.exe /N:" & strNewComputerName & " /rcid /user:domain\administrator /pass:password" '/REBOOT"

intReturnCode = wbemProcess.Create(strCommandLine, Null, Null, intProcessID)

If intReturnCode = 0 Then
Wscript.Echo "Command was started with a process ID of " & intProcessID & "."
Else
Select Case intReturnCode
Case 1 Wscript.Echo "Error: The request is not supported"
Case 2 Wscript.Echo "Error: The user did not have the necessary access"
Case 3 Wscript.Echo "Error: The service cannot be stopped because other services that are running are dependent on it"
Case 8 Wscript.Echo "Error: Interactive Process"
Case 9 Wscript.Echo "Error: The directory path to the service executable file was not found"
Case 21 Wscript.Echo "Error: Invalid parameters have been passed to the service"
Case Else Wscript.Echo "Error: Command could not be started due to error " & intReturnCode
End Select
End If

Set wbemLocator = Nothing
Set wbemNameSpace = Nothing
Set wbemProcess = Nothing
Set wbemObjectSet = Nothing

'*******************************************************************************
' Reboot Computer
wscript.echo "Rebooting Computer"
Dim strShutDown, strShutDownSwitch, objShell
strShutDown = "shutdown /r /t 60 /m \\" & strComputer
strShutDownSwitch = " /c " &amp;amp;amp;amp; Chr(34) & "Renamed and changed GUID " & Chr(34) & " /f"
Set objShell = CreateObject("WScript.Shell")
objShell.Run strShutDown & strShutDownSwitch


'********************************************************************************
q = q + 1
'Call Debug
Next
Set objComputer = Nothing

'**************************************************************************­*****
'**************************************************************************­*****
' the rest of this is available for debugging purposes if needed
Sub Debug ()
ADsEncodeBinaryData = myADsEncodeBinaryData (objComputer.netbootGUID)
wscript.echo "ADsEncodeBinaryData = " & ADsEncodeBinaryData
strGUID = ConvertOctetToHexStr (objComputer.netbootGUID)
wscript.echo "strGUID = " & strGUID
wscript.echo "objComputer.netbootGUID Type = " & Typename(objComputer.netbootGUID)
wscript.echo "objComputer.netbootGUID Size = " & LenB(objComputer.netbootGUID)

Call InitializeADSI

strQuery = ";" & _
"(netbootGUID=" & ADsEncodeBinaryData & ");" & _
"name,distinguishedName;" & _
"subtree"

wscript.echo strquery

Call DoLDAPQuery (strQuery, Rs)

wscript.echo "recordcount = " & rs.recordcount

While not rs.eof
wscript.echo "name = " & rs.fields ("name")
wscript.echo "dn = " & rs.fields ("distinguishedname")

rs.MoveNext
Wend

Call ConvertHexStringToByteArray (strGUID, newArr)
wscript.echo "newArr Type = " & Typename (newArr)
wscript.echo "newArr Size = " & LenB (newArr)

If strGUID = ConvertOctetToHexStr (newArr) Then
wscript.echo "Same!"
Else
wscript.echo "Different!"
End If

Call FinishLDAPQuery (rs)

Call DoneWithADSI

End Sub

'**************************************************************************­*****
'**************************************************************************­*****
' Converts an array of Bytes to a "\" separated string
Function myADsEncodeBinaryData (arrByte)
Dim str, s, i
WScript.Echo ""

str = ConvertOctetToHexStr (arrByte)
WScript.Echo "Length = " & len(str) & " '" & str & "'"

s = ""
For i = 1 to Len (str) Step 2
s = s & "\" & Mid (str, i, 2)
Next
WScript.Echo s

myADsEncodeBinaryData = s
End Function

'**************************************************************************­*****
'**************************************************************************­*****
' Function to convert OctetString (byte array) to Hex string.
' Code from Richard Mueller, a MS MVP in Scripting and ADSI
Function ConvertOctetToHexStr (arrbytOctet)
Dim k

ConvertOctetToHexStr = ""

For k = 1 To Lenb (arrbytOctet)
ConvertOctetToHexStr = ConvertOctetToHexStr _
& Right("0" & Hex(Ascb(Midb(arrbytOctet, k, 1))), 2)
Next
End Function

'**************************************************************************­*****
'**************************************************************************­*****
' This is an elegant way to convert a hex string to a Byte
' array. Typename(pByteArray) will return Byte(). pByteArray
' should be a null variant upon entry. strHexString should be
' an ASCII string containing nothing but hex characters, e.g.,
' FD70C1BC2206240B828F7AE31FEB55BE
' Code from Michael Harris, a MS MVP in Scripting
Sub ConvertHexStringToByteArray (ByVal strHexString, ByRef pByteArray)
WScript.echo ""
WScript.echo "ConvertHexStringToByteArray (" & strHexString & ")"
Dim fso, stream, temp, ts, n
Set fso = CreateObject ("scripting.filesystemobject")
Set stream = CreateObject ("adodb.stream")

temp = fso.gettempname ()
Set ts = fso.createtextfile (temp)

For n = 1 To (Len (strHexString) - 1) step 2
ts.write Chr ("&h" & Mid (strHexString, n, 2))
Next

ts.close

stream.type = 1
stream.open
stream.loadfromfile temp

pByteArray = stream.read

stream.close
fso.deletefile temp

Set stream = Nothing
Set fso = Nothing
End Sub

'**************************************************************************­*****
'**************************************************************************­*****
Sub InitializeADSI
Set Com = WScript.CreateObject ("ADODB.Command")
Set Conn = WScript.CreateObject ("ADODB.Connection")

' Open the connection.
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
End Sub

'**************************************************************************­*****
'**************************************************************************­*****
Sub DoneWithADSI
Conn.Close

Set Com = Nothing
Set Conn = Nothing
End Sub

'**************************************************************************­*****
'**************************************************************************­*****
Sub DoLDAPQuery (strLDAPQuery, resultSet)
Com.ActiveConnection = Conn
Com.CommandText = strLDAPQuery
Set resultSet = Com.Execute
End Sub

'**************************************************************************­*****
'**************************************************************************­*****
Sub FinishLDAPQuery (resultSet)
resultSet.Close
Set resultSet = Nothing
End Sub

'**************************************************************************­*****
'**************************************************************************­*****
' To convert a GUID like {E09B48B5-E141-427A-AB0C-D3605127224A} to
' the packed (commonly used in the registry) format like
' 5B84B90E141EA724BAC03D06157222A4
Function PackGUID(guid)
PackGUID = ""

' Remove brackets if they exist
Dim temp
temp = Replace( guid, "{", "")
temp = Replace( temp, "}", "")

' split the guid on the hyphen
Dim part
part = Split(temp,"-")

' build the packed version of the guid
Dim pack
pack = ""
Dim i, j
' reversing the sequence of the first 3 parts from the left
For i = 0 To 2 'UBound(part)
For j = Len(part(i))-1 To 1 Step -2
pack = pack & Mid(part(i),j,2)
Next
Next
For i = 3 To 4
For j = 1 to Len(part(i)) Step 2
pack = pack & Mid(part(i),j,2)
Next
Next

PackGUID = pack
End Function

Friday, September 08, 2006

Prompting to start EXE file at each logon

We recently purchased Trend NeatSuite software.

The OfficeScan client software, no matter how installed, added an entry to the RUN registry entry for All Users.

Everytime a user logged on, a message was displayed stating that the user had to press OK to continue as the program wasn't signed. You could also uncheck a box so as not to be prompted again.

I didn't want to bother my users with this message and didn't want everyone calling to ask what to do etc.

There is a GP setting that can be set so this message isn't displayed. The trouble is, is that it affects all EXE files.

The entry is \User Configuration\Administrative Templates\Windows Components\Attachment Manager\Inclusion list for moderate risk file types... and in there enter '.EXE'

I know this isn't best practice but it did solve our problem.