Sometimes it’s useful to have a standard network script perform an additional ad-hoc task for a few days. Although configuring this task isn’t difficult, it isn’t obvious how to make the solution work consistently if you aren’t familiar with the ins and outs of the scripting language. To demonstrate such a solution, I’ll use a couple of lines of VBScript to prevent code from executing after a specific date.
Although you can use this approach for anything you need to expire, user printer mappings provide a good example of why such functionality is useful. Since printer-mapping data is available only in the user context, gathering such data is easiest from a logon script. You can simply add printer enumeration code to a logon script, then send the data to an available logon server with the WScript.Shell LogEvent method.
Performing such an audit from a logon script presents a couple of problems. First, you might capture only a fraction of users if you run the code for only part of a day—or even a full day. Depending on the nature of the network, you might need several days or even a couple of weeks to obtain information about a majority of users. Second, because the code runs at every logon, you’ll get the same data repeatedly for users who log on frequently; eventually, you’ll want to stop putting all this data into the event logs.
In an ideal world, you’d remember to go back and remove the code from the logon script after a week or so. However, snags tend to occur, particularly if you’re performing the audit as part of offsite technical support or if you aren’t the only technical support person working with the network. Having a technique for skipping code after a particular date is a small bit of insurance that helps limit the effect of your audit.
To make code expire within a script, insert a line of code that checks the current date and runs the subsequent code only if the date is before a particular stop date. Keep in mind that you might run into problems with how VBScript handles dates. VBScript is very flexible about not only what it recognizes as a date but also how it converts the information to a date. This means that your off-the-cuff date used in a script written under the assumption of U.S. dating format might be misinterpreted elsewhere. Depending on the date specified, the code might never run at logon for a British or ISO localized computer—or it might run during logon for months or years.
There are multiple ways to ensure that VBScript interprets a hard-coded date correctly, but one method stands out as being both simple and reliable: Specify the date in the format yyyy-MM-dd, where yyyy is the 4-digit year, MM is the 2-digit month, and dd is the 2-digit day. Given 4 initial digits and separating dashes, VBScript will always interpret this as a standard ISO 8601 date—including the one possible edge case, in which a system displays dates using the format yyyy-dd-MM (which to my knowledge isn’t used anywhere in the world).
Using the yyyy-MM-dd approach, you’d specify February 1, 2010, as 2010-02-01. To convert this information into a date instead of merely a string that looks like a date, I use VBScript’s # mark instead of quotes when specifying the date in a script. With an unambiguously interpreted date, all you need to do is check the current system date and compare it to the stop date. You can use an if-then loop with the date test to force the code to run only until the stopping date, like so:
One thing you should be aware of is that the stop date you specify should be the day after the last day you want the code to run. In VBScript, a date is more accurately a date and a time. When you call VBScript’s Date() method, although it might be displayed as simply a specific date, the actual date inside VBScript includes a time of day. Therefore, 2 a.m. on February 1, 2010, is a larger date value than just February 1, 2010. Although you could extend the code to deal with time, doing so adds more potential ambiguities. In addition, specifying a time still doesn’t give you any guarantees, because the date is interpreted locally. So, for example, for whatever stop date you specify, a system on London time will skip the code 10 hours before a system on Honolulu time will do so. If you need a more complex solution, I suggest performing searches on the Windows Management Instrumentation (WMI) wbemDateTime class.
The code in Listing 1 demonstrates a usable date-expiring chunk of code that audits user printers and sends them to the logon server with the name of the user and the computer from which they were logging on. Data will show up in the Application log, with the source shown as WSH. As written, you can insert the code at the end of a logon script and it will work until February 1, 2010; from that date on, WSH will skip the code chunk. It’s possible to use this code as is for printer auditing; just remember to change the date from #2010-02-01#. You still need to clean up the logon script after you finish auditing, but using a date test makes the post-audit problem a simple matter of cleaning up already-dead code, rather than deleting a lot of unnecessary logging.
Listing 1: An Expiring Logon Audit of Printers
If Date() < #2010-02-01# Then
'using pn prefix for variables to limit name collisions
' Begin printer audit code
Dim pnNet, pnPrinters, pnSd, pnSh, pnCount
Dim pnUser, pnComputer, pnLogonServer, pnMsg
Set pnNet = CreateObject("WScript.Network")
set pnPrinters = pnNet.EnumPrinterConnections()
set pnSD = CreateObject("Scripting.Dictionary")
set pnSh = CreateObject("WScript.Shell")
pnUser = pnNet.UserName
pnComputer = pnNet.ComputerName
pnLogonServer = pnSh.ExpandEnvironmentStrings("%LOGONSERVER%")
pnSd( "user " & pnUser & " logging on from " & pnComputer _
& " has these printers:") = ""
for pnCount = 0 to pnPrinters.length -1 Step 2
& " = " & pnPrinters(pnCount + 1)) = ""
pnMsg = Join(pnSd.Keys, vbCrLf)
pnSh.LogEvent 4, pnMsg, pnLogonServer
' end printer audit code