I'm creating a script that moves users from one Active Directory (AD) organizational unit (OU) to another AD OU according to data in a comma-separated value (CSV) input file. As Figure 1 shows, each line of the input file contains two fields: the user's common name (cn) and computer type (Desktop or Laptop). I'm using the computer type to control the OU that each user moves to. For example, I'm moving users with desktop computers from the ou=corp,dc=MyDomain,dc=com OU to the ou=Desktops,dc=MyDomain,dc=com OU and users with laptop computers from the ou=corp,dc=MyDomain,dc=com OU to the ou=Laptops,dc=MyDomain,dc=com OU. I've had success opening the input file and creating a log file. My dilemma is parsing the data and performing the move operation. Can you point me in the right direction?

You bet—Listing 1, page 10, is a reworked version of the UserMove.vbs script you sent me. Let's walk through the script, which should help you identify where you took a wrong turn.

The first 17 lines are almost identical to those of your original script. These lines contain the script's directives (i.e., Option Explicit and On Error Resume Next), variable declarations, and constants. Of particular importance are the INPUTFILE and LOGFILE constants and the three OU-related constants: DESKTOP_OU_DN, LAPTOP_OU_DN, and LEGACY_OU_DN. You'll need to modify the values assigned to these constants according to your network's specific configuration.

At callout A, the script creates a reference (objFSO) to the Scripting Runtime Library's FileSystemObject object. The script uses the objFSO reference variable to open the input and log files. In the event the script can't create a reference to the FileSystemObject object, the script uses the VBScript MsgBox function to display an appropriate error message and exits.

The code at callout B opens the input file for reading by using the objFSO variable in combination with the FileSystemObject object's OpenTextFile method. If the input file fails to open, the script displays an appropriate error message and exits. Provided that the input file opens, the script uses the objInputFile variable (also known as a file handle or text stream) in conjunction with the TextStream object's ReadAll method to read the entire input file into memory. The script passes the in-memory file to the VBScript Split function, which splits the input file into substrings at each carriage return/linefeed. The result is an array in which each element contains one line of the input file, as Figure 2, page 10, illustrates. After initializing the arrUserRecords array, the script closes the input file.

The code at callout C creates the log file or opens the log file if it already exists. If the script can't create or open the log file, the script displays an error message and exits. Provided that the script can open the log file, the script uses the objLogfile variable (also a file handle or text stream) to write the script's start time to the log file, then closes the log file. The purpose of closing the log file after each write operation is to ensure that the log file captures the last action that the script performed. If you don't close the log file after important write operations and an error causes the script to end unexpectedly, the log-file data will be lost. Determining where an error occurred is difficult if you don't have accurate log-file data.

Callout D highlights where the script's real work begins. Here the script uses Active Directory Service Interfaces (ADSI) to connect to the two target OUs: ou=Desktops and ou=Laptops. Your script took a wrong turn at this point by connecting to the source OU.

Admittedly, using the ADSI MoveHere method can be a little confusing at first because you connect to the target container rather than the source container. You can compare it to navigating to the destination directory to copy a file, then providing the fully qualified pathname as the first argument to the Copy command. For example, suppose you want to copy the status.txt file from the C:\WIP directory to the C:\Backup directory. To copy the file, you can

  1. Navigate to the target directory with the command
  2. C:\> cd Backup
  3. Copy the file by providing the file's fully qualified pathname in the Copy command:
  4. C:\Backup> copy C:\WIP\status.txt

MoveHere works in a similar way. To move users to their new OUs, you can

  1. Connect to the target container with the code
  2. Set objDesktopOu = _
       GetObject("LDAP://ou=" _
       & "Desktops,dc=MyDomain," _
       & "dc=com")
  3. Move the user by calling MoveHere and passing to MoveHere the current path (also known as an ADsPath) to the user that you want to move:
  4. objDesktopOu.MoveHere _
       "LDAP://cn=UserOne,ou=corp," _
       & "dc=MyDomain,dc=com", _
       & "cn=UserOne"

    In the second parameter (cn=UserOne) passed to MoveHere, UserOne is the user's relative distinguished name (RDN). You can use this parameter to rename the user account if necessary.

    The code at callout E moves the users. The For...Next statement starts with the second element (i.e., array index 1) to adjust for the UserName,ComputerType line at the top of the input file. Next, the script uses a comma as a delimiter to split the current array element into two substrings. It assigns the first substring to strUserName and the second substring to strComputerType.

    With the value of strComputerType in hand, the script tests to see whether that value is equal to DESKTOP. If it is, the script moves the user to the Desktops OU. Otherwise, the script moves the user to the Laptops OU. The script records the result of the move operation before continuing to the next user. After moving all the users, the script records its stop time and exits.