In environments in which you have roaming users—that is, users who don't use the same computer every day—an important piece of information you might need to know is the machines that these users are logged on to. You need to know this information before you can remotely troubleshoot a problem (e.g., missing registry keys, missing environment variables) on a roaming user's computer.
By default, you can gather roaming users' machine information by filtering the Security logs on your domain controllers (DCs). However, this solution is time-consuming and potentially error prone, especially if you have two or more large Security logs. Instead, you can use one of two strategies to acquire this information. If you don't have too many computers on the network, you can simply poll each computer for the logged-on user. Alternatively, you can have users advertise the machines they're logged on to. By advertising I mean that the users can write to a central repository the name of the machine that they're logged on to. The advertising can be part of roaming users' logon script. Let's look at how you can implement both of these strategies and look at their advantages and disadvantages.
The Polling Strategy
If you have a small number of computers on the network (i.e., less than 50), polling each machine is the easiest way to determine the machine that a roaming user is logged on to. One approach is to use the Net View command to get a list of computers on the network, then use either the Nbtstat command or Sysinternals' PsLoggedOn utility to poll each machine to see whether the user is logged on to it. (PsLoggedOn is a utility that's part of Sysinternals' PsTools freeware suite, which you can find at http://www.sysinternals.com.)
Nbtstat uses NetBIOS over TCP/IP (NetBT) to display protocol statistics and current TCP/IP connections. The Nbtstat command's -a switch lets you obtain the name table of any remote machine you specify. (Note that the Nbtstat command's switches are case sensitive.) In the Nbtstat -a command's output, Messenger service names are followed by a <03> entry. In addition, when a user logs on with a domain account, the command output includes an entry with the user's ID. Thus, you can determine which computer a user is logged on to by running the Nbtstat -a command against a list of computers, then using the Findstr command to check the output for the existence of a <03> entry that matches the username you're interested in.
Based on this knowledge, I created the FindUser.cmd script, which Listing 1, page 12, shows. You can use this script on machines running Windows XP, Windows 2000, and Windows NT 4.0. To launch this script, you use the command
where username is the name of the roaming user for which you're searching. The script stores the username parameter's value in the usertofind variable.
The For command that the code at callout A in Listing 1 shows uses the Net View command to obtain a list of computers on your network. The Net View command returns the computer names in the format \\computername (e.g., \\WKSTN1) as well as other output, such as headers. Because you're interested only in the computer names, the Find command filters the Net View command's output and keeps only those lines that contain the string \\.
For each computer name, the script runs the block of code labeled :findit, which the code at callout B shows. The :findit code determines whether the user you're looking for is logged on to that computer. This code first assigns the \\computername value from the For command that the code at callout A shows to the target variable. Because the Nbtstat command requires that computer names be without \\ characters, the line
replaces the \\ characters with nothing and assigns the edited computer name to the same target variable. The result is that the target variable now contains just the computer name.
Next, the :findit code runs the Nbtstat -a command against the computer name in the target variable. The code passes the Nbtstat -a command's output to the Findstr /i command, which searches the output for the string in the usertofind variable. The /i switch makes the search case insensitive. By default, Findstr returns any lines that contain the specified string. The :findit code redirects the Findstr /i command's output to NUL because you don't need this output. Instead, you need to determine whether the Findstr command executed successfully. Most shell scripting commands return a number, or error-level value, when they execute. You can use the If command with the Errorlevel clause to determine whether the preceding command executed successfully. An error-level value of 0 signifies success, whereas an error-level value of 1 or higher signifies failure. In this case, if %ERRORLEVEL% is equal to 0, Findstr found a match for the usertofind variable's value and the :findit code displays a message that specifies the name of the computer the user is logged on to. Note that this method won't work if the Messenger service is disabled, if the <03> UNIQUE type lists the computer name rather than the username, or if NetBT is disabled.
If you want to use PsLoggedOn instead of Nbtstat, you can easily adapt FindUser.cmd. Simply place PsLoggedOn in the same folder or system path as the script, then replace the Nbtstat command
Nbtstat -a %target% | Findstr /i "%usertofind%" > NUL with the psloggedon.exe command Psloggedon.exe -L \\%target% | findstr /i "%usertofind%" > NUL
(Although the psloggedon.exe command appears on several lines here, you would enter it on one line in FindUser.cmd.) The psloggedon.exe command's -L switch specifies that you want the PsLoggedOn utility to return only accounts that are locally logged on to the specified computer. By default, PsLoggedOn also displays accounts that are connected remotely to the computer, such as through network shares.
Both the psloggedon.exe and Nbtstat commands provide the same results, but some minor differences exist in the logistics. FindUser.cmd doesn't check to see whether a computer is on before it tries to determine whether the user is logged on to that machine. Both the psloggedon.exe and Nbtstat commands eventually time out if the destination host is unreachable. Nbtstat has an advantage, though, because it will time out in a mere 2 seconds, whereas psloggedon.exe can take up to 7 seconds before it times out. This additional time can significantly affect how fast the script executes. However, PsLoggedOn utility has the advantage of including those users who are logged on to the computer as part of a service account or local computer account.
Note that you can also use the PsLoggedOn utility at the command line rather than in FindUser.cmd. To have PsLoggedOn search the network to locate the computer that a user is logged on to, open a command-prompt window and run the command
where username is the name of the roaming user for which you're searching.
The Advertising Strategy
The second strategy for locating roaming users is to have them advertise the machines they're logged on to. The simplest method is to create a central share on one of your servers and have the roaming users write to a text file on that share the name of computer they're logged on to. You can facilitate this advertising by using now.exe and adapting the roaming users' logon script.
Now is a Microsoft resource kit utility that outputs the specified text just like the Echo command, except that now.exe adds a timestamp to the beginning of the string. Because now.exe is a resource kit utility, you need to copy this utility to your logon share or another location that's part of the roaming users' search path.
Adapting the logon script is simple. For example, if you have a server called Server1 that has a share called Logs, you can add the following command to your logon script:
Now.exe Logged on to %computername% >> \\server1\logs\%username%.txt
(Although this command appears on several lines here, you would enter it on one line in the logon script.) Each time a roaming user logs on to a computer, the logon script appends a string to the text file called username.txt where username is the user's logon name. The string consists of the time the user logged on and the name of the computer he or she logged on to. For example, the now.exe command might append the string Sat May 03 22:28:18 2003 — Logged on to WKSTN1 to the msmith.txt file on Server1's Logs share.
The advantage of this text-file advertising method is its simplicity. You need only one line of code in the logon script, and finding out which machine a user is logged on to is as simple as opening the text file named after the user's logon name. This method also scales fairly well so that you can use it in large organizations.
Another advertising method is to store the logon information in a central database, such as Active Directory (AD). Because the database advertising method stores the information in database fields, you can have an application query that information. For example, you can create an Active Server Pages (ASP) application that first queries AD for the computers that roaming users' are logged on to, then displays these values in a Web page.
For regular authenticated users, most of the fields, or attributes, in AD are read-only. Two exceptions are the wwwHomePage attribute and its associated multivalued attribute called url. Not only can regular authenticated users write to the wwwHomePage and url attributes, but these attributes are infrequently used and are visible through the Microsoft Management Console (MMC) Active Directory Users and Computers snap-in. Thus, you can use the Active Directory Users and Computers snap-in to find a roaming user's machine. The wwwHomePage attribute value appears in the Web Page field, and the url attribute value appears in the Web Page Addresses (Others) field in the user's Properties dialog box.
I created a code module called LogUser that takes advantage of the wwwHomePage and url attributes. You can add this code to your roaming users' logon script to update the information whenever they log on to a computer. LogUser works by querying AD for the user object that represents the current user. If a previous entry exists for this object's wwwHomePage attribute (i.e., the LogUser module was executed previously for the user), the module moves the value of wwwHomePage attribute to the url attribute. The module then updates the wwwHomePage attribute with the name of the computer that the user is currently logged on to.
Listing 2, page 14, shows the LogUser module. LogUser begins with the usual declarations and error-handling statements, after which it sets the domain's root to the rootDN variable. The module then obtains a reference to the WScript.Network object and assigns that reference to the wshNetwork variable. The module later uses this reference to determine the distinguished name (DN) of the user who executed the logon script and the name of the computer on which the logon script was executed.
Next, LogUser creates an instance of the ADODB.Connection object, which it uses to connect to AD. After the connection is made, the module executes a query that, if successful, returns the DN of the user who executed the logon script. The query searches AD for a sAMAccountName attribute value that matches the person's username. (AD's sAMAccountName attribute stores a user's logon name.) If the module doesn't find a match, it displays the message No Match, then quits. If the module finds a match, it assigns that user's distinguishedName attribute value (which is the user's DN) to the dn variable. The module uses the dn variable with the GetObject function to bind to the object that represents the user. LogUser stores a reference to this user object in the oUser variable.
LogUser reads and assigns the value of the user object's wwwHomePage attribute to the lastLogon variable. If wwwHomePage already contains a value (i.e., the LogUser module was executed previously for the user), the module copies the existing lastLogon variable's value into the url attribute so that you can store historical information about the previous computers that the user has logged on to. Finally, the module updates the wwwHomePage attribute with the name of the computer the user is currently logged on to and uses the SetInfo method to commit the changes to AD.
I tested the LogUser module on a machine running Win2K Service Pack 3 (SP3). Before you can add the LogUser code to your logon script, you need to modify the line that callout A in Listing 2 shows. In this line, replace DC=DOMAIN,DC=COM with your domain's root.
The database advertising method is a longer, more complex solution than the text-file advertising method. However, the database advertising method is more flexible because it lets you incorporate the machine information in other solutions.
Although completely functional, the database advertising method might have limited usability in some environments. Because the LogUser module updates AD each time the user logs on, the updates will be replicated to all the DCs, which can dramatically increase replication traffic. These updates, however, aren't much of a problem for single-forest, single-domain environments in which all the DCs are local to each other, in which the DCs have high-speed connections to one another, and in which the roaming user count is only in the hundreds.
Note that AD best practices recommend against storing values in attributes for which they were not intended. In this case, the LogUser module stores computer names in the wwwHomePage and url attributes, both of which are intended for URL values. If you're uncomfortable with storing the computer names in these attributes but still want to use the LogUser module, you can adapt the code so that it writes the computer names to another type of central database, such as a Microsoft SQL Server database.
The Best Solution
I've shown you two general strategies and several specific tools and methods that you can use to determine which machines roaming users are logged on to. The solution that will work best for you depends on your environment (i.e., the number of users and computers in your Windows network) and how you want to use this information.
The polling strategy is a simple solution that lets you determine the machine a roaming user is logged on to. No matter whether you use the Nbtstat command or the PsLoggedOn utility, you don't have to create a central repository or modify a logon script.
The advertising strategy has the advantage of being able to quickly determine the machine in which a user is logged on without having to run a separate script. By adding either a batch command or the LogUser code to the roaming users' logon script, you can quickly access not only current machine information but also historical machine information. However, you shouldn't use the machine information you gather through either advertising method for auditing purposes. The machine information stored in text files on the central share is accessible and modifiable by the roaming users because they have read/write permissions for their particular files. Storing the machine information in an AD database is slightly more secure than storing it in text files. Although the roaming users have read/write permissions for the wwwHomePage and url attributes, the users can't modify these attributes too easily because they must know the exact attribute names and have access to the necessary tools (e.g., the Active Directory Users and Computers snap-in) to make the changes.
No matter which solution you choose, quickly knowing the machine that a roaming user is logged on to can save time and effort. These solutions are especially helpful in environments in which remote administration and troubleshooting occurs frequently and the users can't reliably provide the computer name when they call for help.