Now that Active Directory can tell us what computer models we have (and how many we own), it is time to extend our inventory! Today, we are going to implement one shutdown that script with Group Policy. This script will query for our computer’s serial number (or service tag) and will also grab the unique ID associated with an attached monitor. It will then store both pieces of information in that computer’s Active Directory account. As a final result, you can look at any computer in your domain and see the information in the Description field. This makes it incredibly easy to find serial numbers in Active Directory!
So if you are tired of manually doing an inventory, let’s automate some stuff!
First, the script:
sn = GetSerialNumber arrMonitors = GetMonitorSerials strDescription = "Computer: " & sn For intMon = LBound(arrMonitors) To UBound(arrMonitors) strDescription = strDescription & " Monitor " & intMon + 1 & ": " & arrMonitors(intMon) Next 'MsgBox strDescription UpdateDescription(strDescription) Function GetSerialNumber strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colBIOS = objWMIService.ExecQuery _ ("Select * from Win32_BIOS") For each objBIOS In colBIOS GetSerialNumber = objBIOS.SerialNumber Next End Function Sub UpdateDescription(strDescription) Set objSysInfo = CreateObject("ADSystemInfo") On Error Resume Next Set objComputer = GetObject("LDAP://" & objSysInfo.ComputerName) objComputer.Description = strDescription objComputer.SetInfo End Sub Function GetMonitorSerials Const HKEY_LOCAL_MACHINE = &H80000002 strKey = "SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Connectivity" strComputer = "." strMS = "" Set objRegistry = GetObject("winmgmts:{impersonationLevel=Impersonate}!\\" & strComputer & "\root\default:StdRegProv") If objRegistry.EnumKey(HKEY_LOCAL_MACHINE, strKey, arrKeyNames) = 0 Then If IsNull(arrKeyNames) = False Then For Each strKeyName In arrKeyNames If strMS = "" Then strMS = strKeyName Else strMS = strMS & ";" & strKeyName End If Next End If End If GetMonitorSerials = Split(strMS, ";") End Function
This is a VB script that can be divided out into two distinct sections. The first part queries the BIOS (through WMI) for the serial number. If you are curious, you can do the same thing in the command prompt by typing WMIC BIOS GET SERIALNUMBER. If you haven’t played around with WMI, check out this series on mastering WMI.
It then writes the serial number to the description variable with a pretty little “Computer: ” prefix! The second part queries this registry key: HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Connectivity for the monitor information. Though this information is strictly a service tag, the unique ID is consistent across machines and serves us in our inventory.
Your Group Policy Object
You will want to setup your GPO in the same way that you setup the record model GPO. In fact, you can even have one central GPO named Active Directory Inventory and setup both scripts to run on shutdown. Because the computer account will write to it’s Active Directory account, you will need to give it an extra permission – the WRITE to Attribute permission. In the script above, we record our information in the description field. This is done because that field is always viewable in AD (whether you have Advanced Mode On or Off).
As a refresher, here is how you can delegate that permission:
Within Active Directory Users and Computers, right click on the OU (or OUs) containing your domain computers. Next, select Properties, then the Security Tab, and finally the Advanced button. Select Add. We are going to apply this permission to SELF (literally the object itself). For the Applies To button, select Descendant Computer objects. Scroll down the properties list until you come to Read/Write Description. Check both of these options and hit OK three times. SELF can now write to the ManagedBy attribute for the computers in the OUs that you selected.
How to Find Serial Numbers in Active Directory
After your machines start rebooting, they will populate their descriptions automatically! Your Active Directory Users and Computers console should start looking like this:
Two things should stand out as unique. First, the method of recording monitors will grab multiple monitors. As a note, I have only tested it with two monitors. Second, computers without a monitor tag in the description are almost always laptops. To export this information, you can create a custom query in Active Directory or you can use PowerShell (either the Get-ADComputer or Get-QADComputer cmdlet). If you save this to excel, you can use the Text-to-Columns features and separate out by the colon character.
If you haven’t set up User Account Tracking in AD, spend a few extra minutes in Active Directory to set that up! After implanting this, you’ll be able to see who uses what computers!
Special thanks to Pber for putting together the serial number portion of this script. Special thanks to RobSampson for putting together the monitor portion.
Hi Joseph,
I had sent one mail to you about this question.
Do you post one video in YouTube about this article?
I haven’t.
Hey,
Just curious, is there a reason why the description attribute in AD was used and not the serialNumber attribute?
I found that the description field was easier to use for techs in our department – there wasn’t a way for them to miss that data. 🙂
I had issues getting this to work in Windows 10 due to Shutdown scripts not running due to Fast Boot as the kernel goes into hibernation instead of shutting down completely unless you restart the computer. I didn’t want to turn off Fast Boot as frankly it’s a perk of Windows 10.
Instead I did an Immediate scheduled task in GPP running as NT AUTHORITY\SYSTEM which runs whether or not user is logged in and runs with highest privileges.
Nice workaround!!
Hi Joseph,
Very nice script but could you make one which types the Computer name, Computer serial and Monitor serial to a csv file located in a Network share? Put that script to a startup script and soon you’ll have one big csv which includes all the Computers in the AD.
Thanks in advance!
Is it possible to obtain the serial number of a monitor from a computer that is on the network using vba programming in excel?
I’ve never found a reliable way of doing this across every monitor type.
Running this on the network and works fine with desktops and monitors but does not report back on laptops. I assume this is because it does not have a separate monitor, I have tried running the script with a monitor connected but still no dice.
Would be happy with a script that just pulls the serial number if any one has one knocking about.
This should work for you:
sn = GetSerialNumber
strDescription = “Computer: ” & sn
‘MsgBox strDescription
UpdateDescription(strDescription)
Function GetSerialNumber
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:” _
& “{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2”)
Set colBIOS = objWMIService.ExecQuery _
(“Select * from Win32_BIOS”)
For each objBIOS In colBIOS
GetSerialNumber = objBIOS.SerialNumber
Next
End Function
Sub UpdateDescription(strDescription)
Set objSysInfo = CreateObject(“ADSystemInfo”)
On Error Resume Next
Set objComputer = GetObject(“LDAP://” & objSysInfo.ComputerName)
objComputer.Description = strDescription
objComputer.SetInfo
End Sub
Excellent Joseph thank you very much.
no problem!
Anyone else getting compiler errors using the above scripts? I’m unable to run them as they generate “invalid character” errors.
Recopy the script – there were some issues with the amp symbol.
Negative. Still getting an “invalid character” at line 1 char 1. I’m copying and pasting into Notepad, so I can’t imagine it’s a formatting issue.
The file was encoded as UTF-8. Change the encoding to ANSI
I’m trying to use the above script but am getting Microsoft Script Host compiler errors saying there are invalid characters. Here’s the script I’m using. The first location it’s erroring out is Line 3, Character 19, which as far as I can tell is an “o” so I’m not sure what’s going on.
sn = GetSerialNumber
arrMonitors = GetMonitorSerials
strDescription = “Computer: ” & sn
For intMon = LBound(arrMonitors) To UBound(arrMonitors)
strDescription = strDescription & ” Monitor ” & intMon + 1 & “: ” & arrMonitors(intMon)
Next
‘MsgBox strDescription
UpdateDescription(strDescription)
Function GetSerialNumber
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:” _
& “{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2″)
Set colBIOS = objWMIService.ExecQuery _
(“Select * from Win32_BIOS”)
For each objBIOS In colBIOS
GetSerialNumber = objBIOS.SerialNumber
Next
End Function
Sub UpdateDescription(strDescription)
Set objSysInfo = CreateObject(“ADSystemInfo”)
On Error Resume Next
Set objComputer = GetObject(“LDAP://” & objSysInfo.ComputerName)
objComputer.Description = strDescription
objComputer.SetInfo
End Sub
Function GetMonitorSerials
Const HKEY_LOCAL_MACHINE = &H80000002
strKey = “SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Connectivity”
strComputer = “.”
strMS = “”
Set objRegistry = GetObject(“winmgmts:{impersonationLevel=Impersonate}!\\” & strComputer & “\root\default:StdRegProv”)
If objRegistry.EnumKey(HKEY_LOCAL_MACHINE, strKey, arrKeyNames) = 0 Then
If IsNull(arrKeyNames) = False Then
For Each strKeyName In arrKeyNames
If strMS = “” Then
strMS = strKeyName
Else
strMS = strMS & “;” & strKeyName
End If
Next
End If
End If
GetMonitorSerials = Split(strMS, “;”)
End Function
I have a similar approach which records the first logged on First Name, Surname and then Computer Vendor, Model and then ending with the serial number.
Example:
John Dow – Dell Inc. Latitude E7440 – ABCD1234
This script is then applied as a User Configuration Group Policy which then runs at Logon. Normally after a computer has been built, the owner which the machine is intended for will be the first person that logs onto the computer.
I have also add some options in that it only updates the computer field if the AD Description field is blank (for new machines) or a pre-fix of a question mark ? if you want the Description field updated next time a user logs onto the computer. Additionally if any administrator accounts log onto this computer, which starts with a adm_ it will not update the computer field allowing administrators to log onto new machines and verify the build if they need to.
I then store the script on the Domain Controllers NETLOGON location so it is globally accessible.
VB Script is as follows and called Computer-Description.vbs
Set WshNetwork = WScript.CreateObject(“WScript.Network”)
Set objWMI = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2”)
‘ Get service tag and computer manufacturer
For Each objSMBIOS in objWMI.ExecQuery(“Select * from Win32_SystemEnclosure”)
serviceTag = replace(objSMBIOS.SerialNumber, “,”, “.”)
manufacturer = replace(objSMBIOS.Manufacturer, “,”, “.”)
Next
‘ Get computer model
For Each objComputer in objWMI.ExecQuery(“Select * from Win32_ComputerSystem”)
model = trim(replace(objComputer.Model, “,”, “.”))
Next
‘ Get computer object in AD
Set objSysInfo = CreateObject(“ADSystemInfo”)
Set objComputer = GetObject(“LDAP://” & objSysInfo.ComputerName)
Set objIADsUser = GetObject(“WinNT://” & WshNetwork.UserDomain & “/” & WshNetwork.UserName & “,user”)
‘ Build up description field data and save into computer object if current description blank
‘ We also only want to update computers with a description that starts with an question mark (?)
newDescription = objIADsUser.FullName & ” – ” & manufacturer & ” ” & model & ” – ” & serviceTag
‘ Wscript.Echo newDescription ‘— TESTING PURPOSE ONLY
‘ Do not update description if logged on with Administrator account
if (objComputer.Description = “” or left(objComputer.Description,1) = “?”) and (not left(LCase(WshNetwork.UserName),4) = “adm_”) then
objComputer.Description = newDescription
objComputer.SetInfo
end if
Very nice Shaun! I may have to steal a few of these ideas. 🙂
I’m having trouble getting this to actually update the Description field. Even when I run the script manually I get no update. I’ve set the permissions to allow read/write of all properties but still nothing when I run the script. Looks like I’m not the only one, but there doesn’t appear to be any solution that got posted. Maybe something offline?
Anyway, any help would be appreciated. I’m using the original script from the top of the post. The second one with the alternate method gives me compile errors when I try to run it.
Thanks
If you run the script as a domain admin, does it update the field?
No luck that way either. No errors when the script runs, but no updates to the computer object either. I was initially concerned that it was updating the wrong property, etc., but the object modification date doesn’t change. I did notice when I was trying to set the permissions that my dialogs look substantially different that the ones you show. I didn’t actually find the read/write description choices, only read/write adminDescription.
My contact email is in the top right of this page – can you email me a screenshot of your dialogs?
I realized after I sent that the visual difference is all Windows version. I was running things from a Win 7 box. I tried it on my 8.1 machine and it looks pretty much like yours. I still don’t have the Description fields however, only adminDescription. I’m running on a 2008r2 domain in case that makes a difference. I didn’t think about that until just now. I can check on by 2012 domain at home tonight.
I’ve used a different method to get the type and serial number:
Function GetMonitors
On Error Resume Next
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:”_
& “{impersonationLevel=impersonate}!\\” & strComputer & “\root\wmi”)
i=1
Set colMonitors = objWMIService.ExecQuery _
(“Select * from WMIMonitorID”)
For Each objMonitor in colMonitors
monitor = monitor & BytesToString(objMonitor.UserFriendlyName) & “;” & BytesToString (objMonitor.SerialNumberID) & “;”
i = i+1
Next
GetMonitors = monitor
End Function
Thanks Gwen! That is a pretty good method of getting the serial number.
I am running the first script that puts the model of the computer in the comment field. It’s working fine except that our systems administrator is concerned about the permissions in AD to allow the computer to modify itself. He is obviously concerned about security and if a virus or someone figures out how to modify the computer in ad somehow. I doubt this could be a security hole but it was a raised concern.
Jason
It should not be a security issue at all. The comment field isn’t used by anything security wise. I would bet that your computer accounts are actually more secure than your user accounts. Their passwords change constantly and are very strong!
Our network tech said that it’s an issue because it fails a penetration test. I don’t know how/why it would be an issue. Unless self modifies more than a description of itself.
I don’t see an issue with it (and neither have the Microsoft AD guys that I’ve talked with).
Hi Joseph,
How could I troubleshoot the execution of the VBS script? When I executed it on my computer it didn’t throw any errors, however when I put it on the GPO it never updates the description field. Permissions to write to the description field are set up fine (the previous script for recording the model, works fine); in fact I added the write/read permissions to all attributes, just in case. Any clues?
Info: using Windows 2008 R2 as DC and Windows 8 Pro as test client.
Thanks,
Can you show me your script?
Hi Joseph,
Here you go; I hope it pastes right.
Thanks,
**********************
sn = GetSerialNumber
arrMonitors = GetMonitorSerials
strDescription = “Computer: ” & sn
For intMon = LBound(arrMonitors) To UBound(arrMonitors)
strDescription = strDescription & ” Monitor ” & intMon + 1 & “: ” & arrMonitors(intMon)
Next
‘MsgBox strDescription
UpdateDescription(strDescription)
Function GetSerialNumber
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:” _
& “{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2”)
Set colBIOS = objWMIService.ExecQuery _
(“Select * from Win32_BIOS”)
For each objBIOS In colBIOS
GetSerialNumber = objBIOS.SerialNumber
Next
End Function
Sub UpdateDescription(strDescription)
Set objSysInfo = CreateObject(“ADSystemInfo”)
On Error Resume Next
Set objComputer = GetObject(“LDAP://” & objSysInfo.ComputerName)
objComputer.Description = strDescription
objComputer.SetInfo
End Sub
Function GetMonitorSerials
Const HKEY_LOCAL_MACHINE = &H80000002
strKey = “SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Connectivity”
strComputer = “.”
strMS = “”
Set objRegistry = GetObject(“winmgmts:{impersonationLevel=Impersonate}!\\” & strComputer & “\root\default:StdRegProv”)
If objRegistry.EnumKey(HKEY_LOCAL_MACHINE, strKey, arrKeyNames) = 0 Then
If IsNull(arrKeyNames) = False Then
For Each strKeyName In arrKeyNames
If strMS = “” Then
strMS = strKeyName
Else
strMS = strMS & “;” & strKeyName
End If
Next
End If
End If
GetMonitorSerials = Split(strMS, “;”)
End Function
And if you run this manually on a machine, it will edit the description field?
No; it doesn’t. I tried on a physical Windows 8 Pro, on a Win 8 Pro VM and Win 7 Pro VM (all 64 bits). On the first two as a regular user; on the last as admin. No errors on the console, and no messages.
I don’t know anything about VBS code, but I’ll try to insert some debugging lines of code to see what I can find.
Thanks,
In the top right corner of this page, you will see a mail/contact button. Send me email containing a screenshot of the OU permissions that you assigned to make this work.
So – you would create the script – name it say – AD_SN.vbs – and call it from the login.bat file for each user?
What would the syntax be like for this? Please provide an example.
Hi Lain – you would name the script ad_sn.vbs and just put it as a shutdown script. No syntax is required because the script doesn’t need any parameters. Let me know if you have any issues.
So – you would create the script – name it say – AD_SN.vbs – and call it from the login.bat file for each user?
What would the syntax be like for this?