The attribute on AD computer objects netbootGuid is very important to allowing some deployment options (RIS and WDS) to locate the computer object the hardware belongs to. Unfortunately certain activities, such as replacing the system board, swapping in new hardware but using a pre-existing computer name, or creating a new VM using an existing VHD can invalidate the accuracy of the netbootGuid attribute stored in AD.
I wrote the script below (more precisely cobbled together since my VBScript is very rusty) to automatically check to make sure the netbootGuid is correct and update it if it isn't. This script can be deployed as a startup script via Group Policies to ensure every time the computer boots, the netbootGuid will be updated if needed. However, in order for this to work, the ACLs on the computer objects must be changed to allow SELF to write to the netbootGuid property.
References (from whence I cobbled):
Note: I'm not entirely fond of having to write out to a temp file to turn the GUID into a byte array. But it works. I'm open to feedback on alternate methods as to how I can rewrite the function baConvertGuidToByteArray without writing out to the temporary file. Thanks in advance.
Note: Updated code 1/6/2014 based on comment below.
<VBScript>
'http://support.microsoft.com/kb/302467 'The sample uses WMI to return the UUID on the system. 'If a UUID can not be found on the system it returns all F's. 'What RIS does in this case is it uses a zero'd out version of the MAC 'address of the NIC the machine is booting off of. 'This sample will return the value required to set the 'netbootGUID attribute Option Explicit Call UpdateNetbootGuid(guidGetUUID, szGetDn) Function guidGetUUID Dim SystemSet, SystemItem, NetworkAdapterSet, NetworkAdapter Set SystemSet = GetObject("winmgmts:").InstancesOf ("Win32_ComputerSystemProduct") For Each SystemItem In SystemSet If SystemItem.UUID = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" Then Set NetworkAdapterSet = GetObject("winmgmts:").InstancesOf ("Win32_NetworkAdapter") For Each NetworkAdapter In NetworkAdapterSet If NetworkAdapter.AdapterType = "Ethernet 802.3" And NetworkAdapter.Description <> "Packet Scheduler Miniport" Then guidGetUUID = "00000000-0000-0000-0000-" & Replace(NetworkAdapter.MACAddress, ":", "") End If Next Else guidGetUUID = SystemItem.UUID End If Next End Function Function szGetDN Dim objSysInfo Set objSysInfo = CreateObject("ADSystemInfo") 'Set DN to upper Case szGetDN = UCase(objSysInfo.ComputerName) End Function Sub UpdateNetbootGuid(guidUUID, szComputerDn) Dim oComputer 'Get parentcontainer Set oComputer = GetObject("LDAP://" & szComputerDn) If ByteArrayToGuid(oComputer.netbootGuid) <> guidUUID Then oComputer.Put "netbootGuid", baConvertGuidToByteArray(guidUUID) oComputer.SetInfo End If 'Clean up Set oComputer = Nothing End Sub Function ByteArrayToGuid(arrbytOctet) If Not IsEmpty(arrbytOctet) Then ByteArrayToGuid = _ Right("0" & Hex(AscB(MidB(arrbytOctet, 4, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 3, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 2, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 1, 1))), 2) & _ "-" & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 6, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 5, 1))), 2) & _ "-" & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 8, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 7, 1))), 2) & _ "-" & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 9, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 10, 1))), 2) & _ "-" & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 11, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 12, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 13, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 14, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 15, 1))), 2) & _ Right("0" & Hex(AscB(MidB(arrbytOctet, 16, 1))), 2) End If End Function Function baConvertGuidToByteArray(ByVal strHexString) Dim fso, stream, temp, ts, n, szScrubbedString Set fso = CreateObject ("scripting.filesystemobject") Set stream = CreateObject ("adodb.stream") Const TemporaryFolder = 2 temp = fso.GetSpecialFolder(TemporaryFolder) & fso.gettempname () Set ts = fso.createtextfile (temp) szScrubbedString = Replace(strHexString, "-", "") ts.write Chr("&h" & Mid(szScrubbedString, 7, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 5, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 3, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 1, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 11, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 9, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 15, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 13, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 17, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 19, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 21, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 23, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 25, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 27, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 29, 2)) ts.write Chr("&h" & Mid(szScrubbedString, 31, 2)) ts.close stream.type = 1 stream.open stream.loadfromfile temp baConvertGuidToByteArray = stream.read stream.close fso.deletefile temp Set stream = Nothing Set fso = Nothing End Function
</VBScript>
Great job with this script, it's just about perfect for my environment. One slight issue I didn't notice until I put it into a startup script is that you reference the current user's domain, not the computer's, so when run from a non-domain account (i.e. SYSTEM as it would in a startup script or the local administrator in an MDT task sequence) it will fail to get the computer's distinguished name. Replacing this one function seems to do the trick for me, feel free to update your post with it: Function szGetDN Set objSysInfo = CreateObject("ADSystemInfo") szGetDN = objSysInfo.ComputerName End Function
(This commenting system seems to have removed the line breaks from my code, but it should still be pretty clear what I'm doing)
Thank you for your feedback, you are correct that was an oversight from my testing in the console. I keep this running in my lab all the time due to frequent rebuilds of the lab computers. It seems to have worked fine for my single domain environment. I've updated the script in the article to reflect what you shared.