[ Team LiB ] Previous Section Next Section

21.5 Account Unlocker Utility

Imagine that you need a utility that quickly enables and unlocks an NT or Active Directory user account. The account was locked because the password was entered incorrectly too many times in succession or because the account exceeded its expiration date. Writing a user-specific script is inefficient if you have many users. Using an input file to pass in the needed user data to a script also is inefficient. You'd have to create the input file just before running the script, because you can't predict whose account you need to unlock. The best approach is to use command-line arguments to pass in the user data as you need it.

Example 21-5 and Example 21-6 use this approach to enable and unlock NT and Active Directory user accounts, respectively. If you have a mixed NT and Active Directory network, you can even combine these two utilities into one script.

Example 21-5 implements the unlocker with the WinNT provider.

Example 21-5. Account unlocker utility for Windows NT
'**********************************************************************
'How to unlock and enable a Windows NT user via arguments to this script
'
'Parameters should be <domain> <username>
'**********************************************************************
Option Explicit

Dim wshArgs, objUser, strOutput

On Error Resume Next

'**********************************************************************
'Get the arguments
'**********************************************************************
Set wshArgs = Wscript.Arguments

'**********************************************************************
'If no arguments passed in, then quit
'**********************************************************************
If wshArgs.Count = 0 Then
  WScript.Echo "ERROR: No arguments passed in." & vbCrLf & vbCrLf _
    & "Please use NTUNLOCK <domain> <username>" & vbCrLf & vbCrLf
  WScript.Quit
End If

'**********************************************************************
'Error checking of the arguments could go here if we were bothered
'**********************************************************************

'**********************************************************************
'Attempt to bind to the user
'**********************************************************************
Set objUser = GetObject("WinNT://" & wshArgs(0) & "/" & wshArgs(1) & ",user")
If Err Then
  Wscript.Echo "Error: Could not bind to the following user: " & vbCrLf _
    & vbCrLf & "WinNT://" & wshArgs(0) & "/" & wshArgs(1) & vbCrLf & vbCrLf
  WScript.Quit
Else
  strOutput = "Connected to user WinNT://" & wshArgs(0) & "/" & wshArgs(1) _
    & vbCrLf
End If

'**********************************************************************
'Attempt to enable the user (but don't quit if you fail)
'**********************************************************************
Err.Clear
objUser.AccountDisabled = False
objUser.SetInfo
If Err Then
  strOutput = strOutput & vbTab & "Error: Could not enable the user account." _
    & vbCrLf
Else
  strOutput = strOutput & vbTab & "User account enabled." & vbCrLf
End If

'**********************************************************************
'Attempt to unlock the user
'**********************************************************************
Err.Clear
objUser.IsAccountLocked = False
objUser.SetInfo
If Err Then
  strOutput = strOutput & vbTab & "Error: Could not unlock the user account." _
    & vbCrLf
Else
  strOutput = strOutput & vbTab & "User account unlocked." & vbCrLf
End If

WScript.Echo strOutput

You pass in two arguments, domain and username, to the script. We use the Wscript::Arguments property method to retrieve the arguments. The Wscript::Arguments property method stores the arguments as a collection, indexing them from 0 to the number of arguments minus 1. The wshArgs collection in the script includes the argument wshArgs(0), which represents the domain, and wshArgs(1), which represents the username.

We use the WshArguments::Count method to count the number of arguments. If the count is 0, the script sends an error message and then quits. Use the Wscript.Echo method to display the error message so that you can use cscript.exe or wscript.exe to run the script. If you use the VBScript MsgBox function (which displays messages as dialog boxes) in a script that you run from cscript.exe, the error messages will be illegible in the command window.

Next, we use the GetObject method to try to connect to the user account. Instead of specifying the actual ADsPath to the User object (which would make the script user-specific), we concatenated (&) the following elements in this order: "WinNT://" (i.e., the provider), wshArgs(0) (i.e., the domain name), "/" (i.e., the slash that separates the domain name and username), wshArgs(1) (i.e., the username), and ",user" (i.e., a comma and the object class).

If the connection attempt fails, the script writes an error message and then quits. If the attempt succeeds, the script puts the output from that attempt into the strOutput text string variable. That way, if you're running wscript.exe rather than cscript.exe, the results appear in one dialog box.

The next two sections attempt to enable and unlock the user account. However, the script doesn't quit if an attempt fails. The Err::Clear method, which works only if you enable On Error Resume Next, clears the error object so that you can detect the next error.

Whether an attempt succeeds or fails, the output goes to the strOutput string variable, where it's appended to any existing text. The vbTab constant and the vbCrLf constant ensure that any new text that we concatenate appears in separate indented lines underneath the user's ADsPath. Finally, we use the WScript::Echo method to print the results in strOutput.

This script is simple but powerful. You can easily add to the script to perform other tasks, such as changing passwords and account expiration dates.

Because Active Directory supports the WinNT namespace, you can use the previous listing to enable and unlock Active Directory user accounts. However, we recommend that you instead use the script in Example 21-6, because accessing Active Directory via the LDAP provider is a more elegant and efficient approach.

Example 21-6. Account unlocker utility for Active Directory using the LDAP provider
'**********************************************************************
'How to unlock and enable a Active Directory user via arguments to this script
'
' Parameters should be <domain> <username>, where domain specifies
' a fully qualified AD domain like dc=mycorp,dc=com
'**********************************************************************
Option Explicit

Const adStateOpen = 0' Used to specify an unsuccessful ADO connection

Dim adoConnection, adoRecordset, wshArgs, objUser, strOutput

On Error Resume Next

'**********************************************************************
'Get the arguments
'**********************************************************************
Set wshArgs = Wscript.Arguments

'**********************************************************************
'If no arguments passed in, then quit
'**********************************************************************
If wshArgs.Count = 0 Then
  WScript.Echo "ERROR: No arguments passed in." & vbCrLf & vbCrLf _
    & "Please use AD-UNLOCK <domain> <username>" & vbCrLf & vbCrLf
  WScript.Quit
End If

'**********************************************************************
'Error checking of the arguments could go here if we were bothered
'**********************************************************************

'**********************************************************************
'Use SearchAD function from the end of Chapter 20 to scan the entire
'Active Directory for this user and return the ADsPath. If the search
'failed for whatever reason, then quit
'**********************************************************************
If Not SearchAD("LDAP://" & wshArgs(0), _
  "((objectClass=User)(cn=" & wshArgs(1) & "))", _
  "SubTree", "ADsPath", arrSearchResults) Then

  WScript.Echo "ERROR: No users found." & vbCrLf & vbCrLf
  WScript.Quit
Else
  '**********************************************************************
  'Attempt to bind to the first ADsPath specified in the array
  '(as there should be only one)
  '**********************************************************************
  Set objUser = GetObject(arrSearchResults(0,0))
  If Err Then
    Wscript.Echo "Error: Could not bind to the following user: " & vbCrLf _
      & vbCrLf & arrSearchResults(0,0) & vbCrLf & vbCrLf
    WScript.Quit
  Else
    strOutput = "Connected to user " & arrSearchResults(0,0) & vbCrLf
  End If

  '**********************************************************************
  'Attempt to enable the user (but don't quit if you fail)
  '**********************************************************************
  Err.Clear
  objUser.AccountDisabled = False
  objUser.SetInfo
  If Err Then
    strOutput = strOutput & vbTab & "Error: Could not enable the user." & vbCrLf
  Else
    strOutput = strOutput & vbTab & "User enabled." & vbCrLf
  End If

  '**********************************************************************
  'Attempt to unlock the user
  '**********************************************************************
  Err.Clear
  objUser.IsAccountLocked = False
  objUser.SetInfo
  If Err Then
    strOutput = strOutput & vbTab & "Error: Could not unlock the user." & vbCrLf
  Else
    strOutput = strOutput & vbTab & "User unlocked." & vbCrLf
  End If

  WScript.Echo strOutput
End If

Although more elegant and efficient, using the LDAP provider is a little tricky because users can exist in any container anywhere in a domain tree. Thus, you can't immediately attempt to bind to the user account because you don't know the ADsPath. You first must conduct an ADO search to obtain the ADsPath.

At the end of Chapter 20, we showed how to use ADO to construct the Active Directory search routine SearchAD. We use the routine here to search Active Directory for the user's ADsPath and store it in arrSearchResults(0,0). The search is executed using a set of arguments, including wshArgs(0) and wshArgs(1). If you put the individual filters on separate lines and substitute the domain and username for wshArgs(0) and wshArgs(1), the set of arguments looks something like this:

LDAP://dc=mycorp,dc=com
((objectClass=User)(cn=vlaunders))
ADsPath
SubTree
arrSearchResults

If the search fails, the script displays an error message and then quits. If the search succeeds, the script attempts to bind to the ADsPath. The rest of the script proceeds similarly to the one for Windows NT.

    [ Team LiB ] Previous Section Next Section