Having worked in help-desk roles in the past I know the importance of knowing which user has logged onto which computer. Its simple stuff really, but unless you have 3rd party systems like System Center 2012 (SC12) or client agents, its either hard or time consuming to find out the relation between users and computers. What we needed was an easy way to find out what the last logged on user was for every machine.
In our particular environment we had this very need even more so as we adopt automatic operating system deployments that use generated computer names containing serial numbers. Our support staff could now go to Active Directory and see useful information populated in the description field for all computers. Originally we were hoping to use SC12 reporting but it was too slow and cumbersome to bring up details.
For this simple task I tackled it simply with a log-on script and a small amount of config to Active Directory.
The Requirements
Below are the list of requirements we had for our environment.
- Include users Full Name – Helps the technician when they call the user
- Include user name – helps find user in active directory
- Make and Model – Useful to determine what form factor they are (Laptop/Desktop/Tablet)
- Serial number – helps to verify quickly with our asset inventory system – not required but useful nonetheless
- Date – Note I do not use this, but have added it to the script for some that may want to.
There is a lot more information that we could have included, both from WMI and Active Directory easily but we did not have a need for it. Im happy to modify the script if someone can think of something useful to add.
Active Directory Changes (USN)
Active Directory uses Update Sequence Numbers (USN) as its primary mechanism to control replication between Domain Controllers. Each time a change is made on an object (like a computer) the attribute on that object (uSNChanged) increases. Changing the description of a computer object increases the uSNChanged value which allows it to replicate to other domain controllers.
Active Directory replication does not primarily depend on time to determine what changes need to be propagated. Instead it uses update sequence numbers (USNs) that are assigned by a counter that is local to each domain controller. Because these USN counters are local, it is easy to ensure that they are reliable and never run backward (that is, they cannot decrease in value).
REFERENCE: How the Active Directory replication model works
http://technet.microsoft.com/en-us/library/cc772726%28v=ws.10%29.aspx
I could not find the correct documentation or supporting evidence for the below but I believe it is correct – please let me know if I am wrong:
There is a limit to the amount of USN’s that an Active Directory object can have, and this script can cause the USN limit to be reached in a large environment. To counter this problem the script does not change if the value is the same, therefore the majority of object descriptions will stay the same and not affect the USN count in a dramatic way. If you were to include a time/date stamp (for example) in the description field, ever time a user logs in it will increment the USN. if you have 20000 workstations and run this script daily you could exhaust your USN count within a couple of years. WS12 has some differences in this space with the new Active Directory system.
So to sum the above up without scaring you too much, if you have a small environment and little AD changes you could put in the date and you probably wont have any problems for the next 20 years, but if you are a large organisation you need to consider this. I have a smaller environment but still chose to not include the date because I did not find it useful. I would rely on SC12 to provide me with more detailed information if needed.
The Active Directory configuration (required)
You need to allow Authenticated Users to be able to read and write ONLY the Description attribute of the Computer objects. To do this please follow the below steps:
- Open Active Directory Users and Computers
- Ensure you have ‘Advanced Features’ enabled. To do this click on ‘View’ and make sure there is a tick next to ‘Advanced Features’
- Right-click on the domain in the left hand pane, and select properties
- Click on the Security tab, and then on ‘Advanced’
- Click on Add, and enter ‘Authenticated Users’ in the text box. Click ‘Check Names’ then Ok.
- Select ‘Descendant Computer objects’ from the Apply to drop-down box and then click on the ‘Properties’ tab.
- Tick Allow next to ‘Read Description’ and ‘Write Description’. Note we need the Read Description property to allow the script to compare existing variables with newly generated one.
- Click Ok.
Once you have followed the steps above, any authenticated user can update the description field, either with the below script or using another method. From a security perspective I think this is acceptable for almost all environments.
The script
The below VBScript is what actually sets the Computer Description. This script needs to be run on the client machines for it to work. There are several ways you can achieve this. Ones that come to mind are:
- Group Policy Log-on script
- Group Policy Log-off script
- VPN post connection script
- Scheduled task on client pc
- psexec.exe command
By far the easiest will be using Group Policy. This wont be documented here, but basically you create a new Group Policy object, and under the User context you configure the log-on script. Note you do not configure it in the computer context as it will not know who the user is.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
' =================================================================== ' Author: Ivan Dretvic ' DATE CREATED: 08/10/2012 ' ' Documentation: http://ivan.dretvic.com/2012/10/automatically-generate ' -description-field-for-computers-in-active-directory/ ' ' This script is designed to assist System Administrators by populating ' description field of Active Directory Computers. The sript can run ' at log-on and log-off, and is executed by the user. You need to set ' appropriate permissions on Active Directory for Authenticated Users ' to have write access to the description field. Refer to documentation ' for more information. ' =================================================================== On Error Resume Next Set objSysInfo = CreateObject("ADSystemInfo") Set objComputer = GetObject("LDAP://" & objSysInfo.ComputerName) Set objUser = GetObject("LDAP://" & objSysInfo.UserName) If left(objComputer.description,1) = "`" Then 'If a tilda exists the script will terminate. This allows custom ' descriptions that dont get overwritten. Wscript.Quit Else 'Sets variables for Computer name, Manufacturer, Model, ' and Serial number strComputer = "." Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colComputerSystem = objWMIService.ExecQuery ("Select * from Win32_computersystem") Set colBIOS = objWMIService.ExecQuery ("Select * from Win32_BIOS") For each objComputerSystem in colComputerSystem GetComputerManufacturer = objComputerSystem.Manufacturer GetComputerModel = objComputerSystem.Model Next For each objBIOS in colBIOS GetSerialNumber = objBIOS.SerialNumber Next 'String cleaning - Manufacturer includes only first word, and ' the serial removes any spaces. txtCount = InStr(GetComputerManufacturer," ") - 1 GetComputerManufacturer = Left(GetComputerManufacturer,txtCount) GetSerialNumber = Replace(GetSerialNumber, " ", "") 'Below are two variants in building the final string. Please chose ' which you prefer. I did read but could not validate that excessive ' computer description changes can cause AD change limits to be reached. 'First one is without dates and second is with dates. Below ar examples. ' The string is also trimmed to 1024 characters as per AD schema ' req (just in case). '### DESCRIPTION WITHOUT DATE ### 'Example: 'John Doe (jdoe) - Dell Optiplex 990 - DRP421S strCompDesc = objUser.SAMAccountName & " | " & objUser.CN & " | " & GetComputerManufacturer & " " & GetComputerModel & " | " & GetSerialNumber strCompDesc = Left(strCompDesc,1024) 'Compares AD string and generated string and skips if they are ' identical. This saves AD change count. If strCompDesc = objComputer.description Then wscript.Quit Else objComputer.Description = strCompDesc objComputer.SetInfo End If ' '### DESCRIPTION WITH DATE ### ' 'Example: ' '2012/11/23 - John Doe (jdoe) - Dell Optiplex 990 - DRP421S ' strDate = Year(Date) & "/" & Month(Date) & "/" & Day(Date) & " | " ' strCompDesc = strDate & objUser.SAMAccountName & " | " & objUser.CN & " | " & GetComputerManufacturer & " " & GetComputerModel & " | " & GetSerialNumber ' strCompDesc = Left(strCompDesc,1024) ' objComputer.Description = strCompDesc ' objComputer.SetInfo End If |
For those not familiar with VBScript you need to copy the above script to a text editor and save the file as with an extension of .vbs.
If you need to execute the script from a command prompt you can enter run the following from a command prompt:
wscript script.vbs
Attached is the original vbs script file for reference. Be sure to rename extension of file.
set_comp_desc.vbs
Comments and feedback welcome.
Ivan
Any idea how to include the ip address in this?
Hi Nik, Sorry i missed this.
Yes you could include the IP in this as the data is accessible via the script. Where you will get a problem is distinguishing which IP it is that you want.
Things to consider are, do you want the LAN IP, or WLAN. If you have VPN client how do you omit their IP details. If you are connected to both WLAN and LAN, do you add both IP’s?
As this script is specifically for Domain Joined machines you may have better luck with making sure you have clean DNS and DHCP records at which you can then get their actual IP.
Hope that helps,
Ivan
If anyone is curios, i managed to add the below to the script and at the moment its giving me the ip address:
strcomputer=”.”
Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)
Set colItems = objWMIService.ExecQuery(“select * from win32_networkadapterconfiguration WHERE IPEnabled=’TRUE’ ” _
& “AND ServiceName’AsyncMac’ ” _
& “AND ServiceName’VMnetx’ ” _
& “AND ServiceName’VMnetadapter’ ” _
& “AND ServiceName’Rasl2tp’ ” _
& “AND ServiceName’msloop’ ” _
& “AND ServiceName’PptpMiniport’ ” _
& “AND ServiceName’Raspti’ ” _
& “AND ServiceName’NDISWan’ ” _
& “AND ServiceName’NdisWan4′ ” _
& “AND ServiceName’RasPppoe’ ” _
& “AND ServiceName’NdisIP’ ” _
& “AND ServiceName” ” _
& “AND Description’PPP Adapter.'”,,48)
‘=== mac address find
For Each objItem in colItems
count_all = count_all + 1
if objItem.IPAddress(0) “0.0.0.0” then
count = count + 1
if count = 1 then
net_ip_address = objItem.IPAddress(0)
‘MsgBox date & ” ” & time & ” IP: ” & net_ip_address
net_mac_address = objItem.MACAddress
end if
end if
next
I am using the code below, when I run the vbs manually on the machine it writes correctly to AD description field. When I run it through log on script or scheduled task upon log on it outputs the ObjUser.SAMAccountName and objUser.CN as the computer name.
On Error Resume Next
Set objSysInfo = CreateObject(“ADSystemInfo”)
Set objComputer = GetObject(“LDAP://” & objSysInfo.ComputerName)
Set objUser = GetObject(“LDAP://” & objSysInfo.UserName)
If left(objComputer.description,1) = “`” Then
‘allows custom descriptions that can’t be overwritten by script
Wscript.Quit
Else
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:” & “{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2”)
Set colComputerSystem = objWMIService.ExecQuery (“Select * from Win32_computersystem”)
Set colBIOS = objWMIService.ExecQuery (“Select * from Win32_BIOS”)
Set colOperatingSystems = objWMIService.ExecQuery (“Select * from Win32_OperatingSystem”)
For each objComputerSystem in colComputerSystem
GetComputerManufacturer = objComputerSystem.Manufacturer
GetComputerModel = objComputerSystem.Model
Next
For each objBIOS in colBIOS
GetSerialNumber = objBIOS.SerialNumber
Next
For Each objOperatingSystem in colOperatingSystems
GetOS = objOperatingSystem.Caption
Next
txtCount = InStr(GetComputerManufacturer,” “) – 1
GetComputerManufacturer = Left(GetComputerManufacturer,txtCount)
GetSerialNumber = Replace(GetSerialNumber, ” “, “”)
‘ ‘write description string
strTime = Date() & ” | ”
strCompDesc = strTime & ObjUser.SAMAccountName & ” | ” & objUser.CN & ” | ” & GetComputerManufacturer & ” ” & GetComputerModel & ” | ” & GetSerialNumber & ” | ” & GetOS
strCompDesc = Left(strCompDesc,1024)
‘do not write if description has not changed’
If strCompDesc = objComputer.description Then
wscript.Quit
Else
objComputer.Description = strCompDesc
objComputer.SetInfo
End If
End If
Hi Nathan,
I was trying to understand why this would happen, and without testing i suspect that the task is being performed as the computer ‘user account’ instead of the actual user account.
If a scheduled task or startup item is run in the computer/system context then the authentication LDAP user would be the computer account.
Can you validate that the tested GPO that you are using is being applied to the user ‘logon’ and not computer ‘startup’?
Hi Ivan,
When I write out the username and name it is displaying the computer name in AD description field for both. Have you come across this before?
Thanks
No I have not. What code are you using? How are you executing it? Can you replicate it manually? Can you put break points at all steps and see what the variables are?
Hi Ivan,
Need to pull out the Computer Description value from the local computer System Properties and then populate this into AD computer Description fiels.
Does this scrip do the trick?
Thanks,
Hi Marius,
The script does not do this currently but it should be fairly easy to do. Even though the script runs in the user context it should be able to read the local computer description.
I’m on holidays now so you may have to reach out elsewhere to get some help. If you find the solution please post it up here.
Ivan
hi i need only Computer Description to be populate in AD
Hi Nitin,
Yes this script lets you do this. what did you want in the Computer Description?
Ivan
Ivan,
I have been using your script for a couple years. For some reason, it is not pulling the serial number of the machines
Hi Jayson,
What devices do you use? Hardware wise.
Have you tried running a script to see if it comes up manually?
I am running it as a login script. We use dell laptops and desktops. Everything else comes in to active directory except the serial #. I will try it manually.
It did not come up manually either
So could it be that on your Dells that the serialnumber (service tag) is held elsewhere in the win32_bios class, or indeed in another class? Ivan’s script looks at win32_bios.serialnumber. Try running the following in Powershell on an example Dell. Does the SerialNumber attribute return a value?
gwmi win32_bios
Also the Win32_SystemEnclosure class may be a good one to look at. Ref:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa394474(v=vs.85).aspx
Again try gwmi Win32_SystemEnclosure and see what is returned
I got it fixed. I didnt have read description enabled in active directory. So it wasnt changing results from the other info. Oversight on my part….
That’s great news. I’m glad the script is now working for you.
Ivan,
Will your Phone ‘solution’ work ok if and when the phone number field is blank?
Hi Stephen,
It could be something like this, but would need testing:
If objUser.telephoneNumber = "" Then
strTelephoneNumber = ""
Else
strTelephoneNumber = " – " & objUser.telephoneNumber
EndIf
strCompDesc = objUser.CN & strTelephoneNumber & " - (" & objUser.SAMAccountName & ") - " & GetComputerManufacturer & " - " & GetComputerModel & " - " & GetSerialNumber
This should check if its blank and set it to blank.
If it exists then it should add a hyphen then the phone number.
Again I have not tested any of this so please let me know via the comments.
Kind Regards,
Ivan Dretvic
Hello Ivan,
Thank you for posting this great script,
I’ve a few questions having to do with modifying your script:
Is there a way to pull the Phone Number Field for the User from AD, and place it after the user’s First Last Name?
Also, I’ve changed my format to to display:
First Name (LoginName) – Computer Manufacturer – Computer Model – Serial Number
strCompDesc = objUser.CN & ” (” & objUser.SAMAccountName & “) – ” & GetComputerManufacturer & ” – ” & GetComputerModel & ” – ” & GetSerialNumber
I do this in my environment as the login can be a couple of different formats, and being able to search for First Last easily by sorting on the Description field in an OU is more convenient .
What I would like to see is the following:
First Name – 123-555-1212 – (LoginName) – Computer Manufacturer – Computer Model – Serial Number (DATE)
Moving the date, I’m sure is simple, I’ll attempt to move that on my own.
I’m not a programmer, so pulling the Phone number out of AD for the User logging off/on might be a bit beyond what my script hacking can accomplish.
thank you for your time,
Stephen
Hi Stephen,
Hope the below can help – note I have not tested this and will rely on your feedback.
RE TELEPHONE:
The field in Active Directory you want to interrogate is telephoneNumber. Note I have not tested this but expect it to work.
http://www.selfadsi.org/ads-attributes/user-telephoneNumber.htm
To see the full list of fields you can use, I suggest:
1. Open “Active Directory Users and Computers”
2. Click on View – Advanced Features
3. Right Click on a user object (one of your users) and select properties
4. Click on a new tab that appears, called Attribute Editor.
This will give you the Attribute names that you can call.
Amend line 56 in my code to look like this:
strCompDesc = objUser.CN & " – " & objUser.telephoneNumber & " - (" & objUser.SAMAccountName & ") - " & GetComputerManufacturer & " - " & GetComputerModel & " - " & GetSerialNumber
RE DATE:
NOTE: refer to my comment on that blog:
If you were to include a time/date stamp (for example) in the description field, ever time a user logs in it will increment the USN. if you have 20000 workstations and run this script daily you could exhaust your USN count within a couple of years. WS12 has some differences in this space with the new Active Directory system.
If you want the date at the end you would do replace line 56 with two lines below.
strDate = " – " & Year(Date) & "/" & Month(Date) & "/" & Day(Date)
strCompDesc = objUser.CN & " – " & objUser.telephoneNumber & " - (" & objUser.SAMAccountName & ") - " & GetComputerManufacturer & " - " & GetComputerModel & " - " & GetSerialNumber & strDate
Lastly, I am not a programmer. I am a scripter who trolls the internet for scripts written by others and mash them together and change them to suit my needs, and that of others. If you are interested in scripting and use Windows machines in your environment, I urge you to learn PowerShell. Very powerful and there are a ton of existing scripts to do many things.
If you have any issues with the script above, remember that the “ can be altered by Outlook and web browsers so sometimes it is best to retype them, otherwise you may get syntax errors.
Please if you can repost your question on my blog so I can post up the reply for others to have access to it.
Kind Regards,
Ivan
Hello Ivan, the I tested the first string:
strCompDesc = objUser.CN & ” – ” & objUser.telephoneNumber & ” – (” & objUser.SAMAccountName & “) – ” & GetComputerManufacturer & ” – ” & GetComputerModel & ” – ” & GetSerialNumber
and that meets my needs I did change the ” – ” for a ” – ” for uniformity:
strCompDesc = objUser.CN & ” – ” & objUser.telephoneNumber & ” – (” & objUser.SAMAccountName & “) – ” & GetComputerManufacturer & ” – ” & GetComputerModel & ” – ” & GetSerialNumber
If the field is blank, I get a “- -” which for my need is fine, and a ‘reminder’ that this user needs a phone number input in AD.
Thank you for your assistance on this, very much appreciated!
Stephen
Apologies for the hyphen that was converted incorrectly.
As for the double hyphen “- -” this can be mitigated (if you wanted to) with the IF statement that I submitted above.
Cheers,
Ivan
Awesome work, all! I’m using this for a few of our clients now. I’ve actually added a section to pull out the installed anti virus product too, as that can be a pain to keep track of.
It checks the OS first, as the SecurityCenter value is different from XP to later OSes.
‘Get AV
strComputer = “.”
Set shell = CreateObject(“WScript.Shell”)
Set getOSVersion = shell.exec(“%comspec% /c ver”)
version = getOSVersion.stdout.readall
Select Case True
Case InStr(version, “n 5.”) > 1 : Set oWMI = GetObject _
(“winmgmts:{impersonationlevel=impersonate}!\\” & strComputer & “\root\SecurityCenter”)
Case InStr(version, “n 6.”) > 1 : Set oWMI = GetObject _
(“winmgmts:{impersonationlevel=impersonate}!\\” & strComputer & “\root\SecurityCenter2”)
End Select
Set colAV = oWMI.ExecQuery _
(“Select * from AntiVirusProduct”)
If colAV.Count = 0 Then
strAV = “AV not installed”
End If
For Each objAntiVirusProduct In colAV
strAV = objAntiVirusProduct.displayName
Next
Then just add & strAV to the end of the “strCompDesc = ”
As may be obvious, I’m not a scripter though. I’ve done some serious Frankenscripting here, so it’d be great if someone wanted to work it in a bit more elegantly.
Nice idea to include AV info, and glad you liked the script. I will look at the code a bit later on.
Ivan
Nice script 🙂 just reading through the comments – I’m not that experienced with vbscript but I think the problem with this “left” issue is perhaps this part:
txtCount = InStr(GetComputerManufacturer,” “) – 1
GetComputerManufacturer = Left(GetComputerManufacturer,txtCount)
If the GetComputerManufacturer variable does not have a space in it, then InStr is going to return 0 and then you’re going to subtract 1 from it, giving your txtCount variable a value of -1 and I’m pretty sure you can’t pass a value of -1 in to the Left function. I think you need to just have an IF statement in there that checks to see if InStr returned greater than 0 before you subtract 1 from it.
I could be way off but just thought it might be worth mentioning.
Hi Chris,
I think you are correct – i never expect that field to be blank, however if wmi is not working properly, or poorly made hardware might not have it. I think i revise the script and update it in the coming week.
Thanks,
Ivan
P.S. Im not a programmer – it took me a while to get it to work for me the way i wanted it.
Ivan i have tried this script in my environment(All computers are Win XP SP3) and it works fine for some computers and for some it gives following error.
Script: set_comp_desc.vbs
Line: 43
Char: 2
Error: Invalid procedure call or argument: “left”
Code: 800A0005
Source: Microsoft VBScript runtime error
I really appreiciate if you could help me to solve this error.
Regards,
Hozefa
Hi Hozefa,
Sorry for not looking into this sooner. Have you had any luck resolving this?
The left() command can be removed from the script if you like.
Note: I did read up around this couple of weeks ago but don’t remember what I found about it.
Let me know how you went.
Ivan
Hi Ivan, a copy ‘n’ paste produces the following output when run:
C:\Users\ngascoigne\Desktop\computer-description2.vbs(43, 2) Microsoft VBScript
runtime error: Invalid procedure call or argument: ‘left’
I by the way use the following script to output date time :: full name :: serial number:
Dim objSysInfo, objUser, objComputer, colSMBIOS, objWMIService, objSMBIOS
Set objWMIService = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2”)
Set objSysInfo = CreateObject(“ADSystemInfo”)
Set objComputer = GetObject(“LDAP://” & objSysInfo.ComputerName)
Set objUser = GetObject(“LDAP://” & objSysInfo.UserName)
‘ WMI query to find the entries needed
Set colSMBIOS = objWMIService.ExecQuery (“Select * from Win32_SystemEnclosure”)
For Each objSMBIOS in colSMBIOS
sAssetTag = objSMBIOS.SMBIOSAssetTag
Next
objComputer.Description = Now & ” :: ” & objUser.CN & ” :: (” & sAssetTag & “).”
objComputer.SetInfo
Hi Neil,
When you say a copy ‘n’ paste, how did you do that? When you load this page, go to the code and in the top right corner of the source code click the first icon. It will open a new window with the raw text for you to copy/paste in. This code i have used almost exactly with no problems on my end.
What OS are you using?
The code you posted above is different to mine, where you have added SMBIOSAssetTag. I believe the problem may occur if there is no AssetTag value so you need to either do some validating or give the variable a value before you commit it to objComputer.Description.
Cheers,
Ivan