HomeBrainDump Finding Logged on Users and More Scripting...
Finding Logged on Users and More Scripting Secrets
Welcome back for the fourth installment of Nilpo’s Scripting Secrets. If you missed the first three articles in this series, this is where I present collections of useful script bits that perform useful tasks or present scripting workarounds that might not otherwise be possible in scripting. I’ve got some good stuff in store for you today, so let’s jump right in.
The first script I want to present to you is a very useful script that will enumerate all of the STOP or bug check errors on a system. These are commonly referred to as BSODs or errors that result in a Blue Screen Of Death. This script is especially handy for network administrators who want to poll an entire network of computers for trouble spots, or for technicians who are profiling a troublesome machine. Heck, it's even good for the average user to inspect a computer for problems they might not even know exist!
This script starts off the same way as any other WMI script. VBScript's GetObject method is used to connect to the local WMI service in the rootcimv2 namespace. This is where all of the most common WMI classes are found.
Set colLoggedEvents = objWMIService.ExecQuery _
("Select * from Win32_NTLogEvent Where Logfile = 'System'" _
& " and SourceName = 'SaveDump'")
Next we query the System Event Log for entries that have the source name "SaveDump." This is the name used by Windows any time that it logs a BSOD error. Of course, your system will have to have logging enabled or it won't be creating event log entries in the first place. If you're not sure what I'm talking about, don't worry; it's enabled by default.
The WMI query will return a collection of events from the event log. A simple For loop can be used to move through each of them and print some information about the errors. The TimeGenerated and Message properties are probably the most useful. They return the time when the BSOD occurred and the associated error message, respectively.
Since I mentioned that the system must be configured to write crashes to the System Event Log, let's look at how we can check if it is. Once again, WMI is our friend!
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & strComputer & "rootcimv2")
Set colRecoveryConfigs = objWMIService.InstancesOf _
("Win32_OSRecoveryConfiguration")
For Each objRecoveryConfig In colRecoveryConfigs
WScript.Echo "Write crashes to System Log: " _
& CStr(objRecoveryConfig.WriteToSystemLog)
Next
This is a typical WMI script that returns a single property from a WMI class. You've probably seen this done a million times. I decided to switch it up a little here though. Instead of writing a Select query and using WMI's ExecQuery method, I've instead chosen to use the InstancesOf method. Whenever you want to return all instances of a WMI class, this is a little more efficient than a "Select * from" query.
In this particular case, the Win32_OSRecoveryConfiguration class returns a single object that represents the OS recovery options. The WriteToSystemLog property returns a Boolean value that indicates whether or not system crashes should be written to the event log. And guess what? It's a read/write property, which means you can use a simple variation of this script to set that particular system setting.
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & strComputer & "rootcimv2")
Set colRecoveryConfigs = objWMIService.InstancesOf _
("Win32_OSRecoveryConfiguration")
For Each objRecoveryConfig In colRecoveryConfigs
objRecoveryConfig.WriteToSystemLog = vbTrue
Next
Here's a script that does just that! Once again we use WMI to connect to the Win32_OSRecoveryConfiguration class. This time we assign the Boolean value True to the WriteToSystemLog property to configure the current system to write crash events to the System Event Log. Since this is WMI, you could easily roll this out to all of the computers in a network.
I'm constantly getting questions on my web site and various forum sites about how to determine the currently logged on user in WSH. You would think that the WScript environment would provide such a useful piece of information, but it doesn't. That doesn't, however, mean that it's not possible. In fact, I'm going to show you not one, but three different ways of doing it.
strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!" _
& strComputer & "rootcimv2")
Set colComputers = objWMIService.InstancesOf("Win32_ComputerSystem")
The first way involves using the Win32_ComputerSystem WMI class, which returns basic information about the operating system and various system and environmental settings used for auditing a Windows session.
For Each objComputer in colComputers
strUser = objComputer.UserName
strUser = Replace(strUser, objComputer.Name & "", "") 'Local User Account
strUser = Replace(strUser, objComputer.Domain & "", "") 'Domain User Account
'Exit For
Next
WScript.Echo "User:", strUser
Once you grab the current instance of the Win32_ComputerSystem class, you can use its UserName property to return the currently logged on user. This will return in the form of either ComputerNameUser or DomainUser, dependent upon whether this current user is a local or domain user, respectively.
In the example above, I've done a little parsing to return just the user's name. VBScript's Replace function is used to strip out the computer name or the domain name from the string. These two bits of information can be returned by the Win32_ComputerSystem class as well using the Name and Domain properties.
The second method I'll show you for getting the local logged on user is a little more involved script that returns the user name of a currently logged on interactive user. It's also capable of returning a lot of information about the current Windows session, including logon time among other things.
strComputer = "."
Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!" _
& strComputer & "rootcimv2")
Set colSessions = objWMI.ExecQuery _
("Select * from Win32_LogonSession Where LogonType = 2")
The code begins in a way that should be very familiar to you for WMI scripts. It first returns a reference to the local WMI service. Then, it executes a query that returns all instances of the Win32_LoginSession class where the LogonType property has a value of 2 (interactive logon).
If colSessions.Count = 0 Then
' No interactive session found
WScript.Echo "No interactive user found"
Else
Next, I add a simple If statement that prevents the script from bombing out if there is no current interactive session.
'Interactive session found
For Each objSession in colSessions
Set colList = objWMI.ExecQuery("Associators of " _
Here's where the script begins to get tricky. The query we ran to start the script returned a collection of current logon sessions. For each of those sessions we are going to run a second query that uses a special Associators of syntax.
In WMI, the Associators of statements will return all instances from other WMI classes that are associated with a given WMI instance. In this case, we're returning all associated instances for the current session logon instance that was provided by the Win32_LogonSession. In this case, the associated instances provide user information for the user who is logged on to the current session.
' Show user info
For Each objItem in colList
WScript.Echo "User: " & objItem.Name
WScript.Echo "FullName: " & objItem.FullName
WScript.Echo "Domain: " & objItem.Domain
Next
Here you can see where I'm accessing properties provided by the associated instances. Specifically, I'm accessing the Name property for the logged-on user's name, the FullName property for the user's Full Name, and the current domain name.
The instance returned by the Win32_LogonSession class also contains valuable information, such as the time when the user logged on. Notice that this property is coming from the original objSession instance, and not the associated objItem instance.
The third and final method I'm presenting for finding the locally logged on user returns information based on networking settings. This is the credential information used by Windows NT Authentication when attempting to connect to other computers or servers. Since NT Authentication bases this off the current user's Windows login information, the details are the same.
strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!" _
& strComputer & "rootcimv2")
Set colProfiles = objWMIService.InstancesOf("Win32_NetworkLoginProfile")
For Each objProfile In colProfiles
If InStr(objProfile.Caption, "NT AUTHORITY") = 0 Then
strUser = objProfile.Caption
Exit For
End If
Next
WScript.Echo "Network User:", strUser
Again, this should be a familiar-looking WMI script. It simply returns all instances of the Win32_NetworkLoginProfile class which represent logged-on network users. However, since there are also default profiles (such as those used by network shares), we need to weed this list down to just the currently logged on user. Fortunately, all of the system profiles begin with the text NT AUTHORITY. Thus, ignoring all instances containing this text will return only the locally logged on user's instance. The Caption property contains the user's name.
That wraps up this installment of Nilpo's Scripting Secrets. I hope you've found something useful or educational in these scripts. For nearly every task you'd like to complete in scripting, there is a solution if you are willing to look hard enough for it. Until next time, keep coding!