Sometimes, you want to trap scripting errors before anyone notices. Good error handling is the key.

Graceful Error Handling

Sometimes, you want to trap scripting errors before anyone notices. Good error handling is the key.

I remember a certain math teacher I had in high school. She was so understanding. Whenever I made a mistake on my homework, she would lean all 275 pounds on my desk, put those big, bloodshot eyes right in front of mine, and say:

“You Got It WRONG!”

Ever since then, I’ve tried to ensure that if I do make a mistake, I fix it before anyone else notices.

As NT administrators, we must remain vigilant about errors. We perform backups, install hardware RAID 5 arrays in our mission-critical servers, and even implement expensive single-IP clustering solutions to make sure that if—heaven forbid—anything does go wrong, no one will notice (we hope).

It’s important to be equally conscientious regarding potential errors in your scripts. Exactly how idiot-proof we can make our scripts depends on the particular idiot (ourselves) running them. (I’ve always maintained that as soon as you make something idiot-proof, someone comes along and builds a better idiot!) As scripts become more complex, the potential for errors increases greatly. It’s vital to trap these errors as they happen and deal with them gracefully. The alternative is the see-no-evil, hear-no-evil approach—close your eyes, wait for something to happen, and then blame the user!

To Err is Human, to Forgive… Err.Clear

There are several ways to handle errors in your scripts. For instance, in “Supercharge Your Scripts” (October 1999), I put an “If…Then” section at the beginning of the script to ensure that the user had entered the correct command-line arguments. That’s error handling! That kind of error handling is preventative; whereas, here I’m focusing on corrective error handling.

Whenever an unexpected condition arises during script execution, all information pertaining to that error is passed automatically to the Err object. The Err object is an intrinsic object, meaning it’s always there when you run a script—just like the WScript object I discussed last issue. In other words, you don’t have to create an instance of it (via CreateObject) in order to use it. Its Properties include Err.Number and Err.Description, and it also has two Methods, Err.Clear and Err.Raise. Err.Clear clears all the error properties, and Err.Raise generates a runtime error in your script. You can use this to simulate errors, so that you can properly code to handle them.

The Details

The first requirement for using the Err object is to include “On Error Resume Next” at the beginning of your script. This enables error handling. Without this line, any errors in your script cause execution to stop and an error message to be displayed. With this line included, execution continues with the line following the one that caused the error. You can then place code in your script (if necessary) to help it recover.

‘FakeError.vbs
Dim strX
On Error Resume Next
strX="Hello World!"
strX=strX+1 ‘Try to add a number to a string value. Bad juju!
If Err.Number<>0 Then
  WScript.Echo 
   & "You can’t do that! You caused Error "
   & Cstr(Err.Number) & " " 
   & Err.Description
  Err.Clear
End If
WScript.Echo strX
WScript.Quit

This forces an error by trying to add an integer value to a string. Figure 1 shows the output from this script. As you can see in Figure 1, strX remained “Hello World!” when we printed it at the end. The important thing to note is that the script executed to the very end. Without the error handling, it would have shut down in line five.

Figure 1. With error handling, the script still executes to the end.

The Real World

Apply what you’ve just learned and write a script for an all-too-common administrative task.

Situation: You’ve just fired an employee for cause, and you want to force a password change for every user in the domain, just in case this guy tries to break in using someone else’s password. There are many ways to do this. Here, you accomplish this task using Software Artisans’ SA-Admin product, which we’ve used in the past for user management.

‘ResetPW.vbs
Option Explicit
Dim objUser, iCount, iNum
Set objUser=CreateObject("SoftArtisans.User")
objUser.GetDomainController "MyDomain", True

iCount=objUser.UserCount
iNum=0
Do
   ‘Cycle through  users
   objUser.User=objUser.UserItem(iNum)
   ‘Resetting the change PW option as we go
   objUser.MustChangePW=True iNum=iNum+1
Loop Until iNum=iCount

Set objUser=Nothing ‘Kill the object
WScript.Quit

As you can see, this is a rather simple script that cycles through every user in the specified domain and sets the MustChangePW property to true. This forces everyone to change his or her password at the next logon (and is much faster than using User Mangler—oops, I mean “Manager”—to do it).

Unfortunately, there are at least two big problems with this script. First, the domain name is hard-wired in the code. Not very flexible, is it? Second, if the PDC is unreachable, what happens to the script execution? Rather than look for more bugs, use what you’ve learned about error handling to write a better script.

‘ResetPW.vbs
‘‘**** Section 1 ****
Option Explicit
On Error Resume Next
Dim objArgs, objUser, iCount, iNum, strDomain

Set objArgs=Wscript.Arguments

‘**** Section 2 ****
‘Make sure they only pass one argument
If objArgs.Count<>1 Then
ShowUsage()
End If

‘**** Section 3 ****
strDomain=objArgs.Item(0)
Set objUser=CreateObject("SoftArtisans.User")
objUser.GetDomainController strDomain, True

If Err.Number<>0 Then ‘Can’t find PDC
   WScript.Echo 
    "Invalid Domain Controller or 
    PDC not available>"
   WScript.Echo "Please check domain name."
   ShowUsage()
End If

‘**** Section 4 ****
iNum=0
iCount=objUser.UserCount

Do
   ‘Cycle through users 
   objUser.User=objUser.UserItem(iNum) 
   ‘Resetting the change PW option as we go
   objUser.MustChangePW=True 
   iNum=iNum+1
Loop Until iNum=iCount

Set objUser=Nothing
Set objArgs=Nothing
Wscript.Quit

Sub ShowUsage()
   WScript.Echo "Please Enter the Domain name."
   WScript.Echo 
     "Example: cscript ResetPW.vbs 
     MyDomain"
   WScript.Quit
End Sub

Section 2 shows that you don’t always have to use the Err object to check for bad data. You can trap errors without it. Here, a simple If…Then statement ensures that only one argument (the domain name, one hopes) was passed to the script. Just in case the user made an error typing in the domain name, Section 3 makes sure it’s valid and reachable. As long as Err.Number=0, the SoftArtisans.User component was able to find the domain controller. If that’s the case, the script is free to cycle through all the accounts, resetting the MustChangePW Property to True.

In the ResetPW.vbs script, you’re merely trapping the errors and gracefully exiting the script—an end result similar to what would have happened if you had left well enough alone. However, now that you’ve trapped the errors, do something else with them. Your homework is to modify ResetPW.vbs to allow user input to correct both incorrectly entered arguments and invalid/unreachable domains. I’ll have my version for you next month in Error Handling, Part Deux. And don’t worry—I won’t embarrass you in front of the class.

Featured