If you plan to rebuild domain controllers (DCs) from scratch or migrate from Windows NT 4.0 SAM domains to Windows Server 2003 Active Directory (AD) domains, you can easily set group permissions by creating local groups, setting their permissions, then placing your global groups inside the local groups. (Global groups can go into local groups but not vice versa.) As soon as you add the global groups to the local groups, the global groups inherit the appropriate permission set.
However, creating local groups from the GUI is clumsy, time-consuming, and prone to typographical errors. So, I decided to automate the group creation process. I first looked into using the Net Localgroup command, which lets you create local groups from the command line rather than the GUI. However, although this command works well for creating groups on one local computer, it's not too useful for automating group creation on multiple remote computers. In addition, you can't create group or user accounts before adding them to the new local group. Rather than try to work around the Net Localgroup command's limitations, I decided to write a script.
The result is NewLocalGroup.vbs, a fully commented script that you can download from the Windows Scripting Solutions Web site. Go to http://www.winnetmag.com/windowsscripting, enter InstantDoc ID 41833 in the InstantDoc ID box, then click Download the Code. You can run this script on computers running any NT-based OS (NT 4.0 through Windows 2003) with Active Directory Service Interfaces (ADSI), Windows Script Host (WSH) 5.6, and Windows Management Instrumentation (WMI) installed. Remote computers have the same requirements.
NewLocalGroup.vbs performs the following tasks:
- connects to the target computer
- enumerates the local groups to see whether a group with that name already exists
- creates the local group
- adds the global group to the new local group
- reports the results
However, before the script can perform these tasks, it needs certain information.
Getting the Needed Information
NewLocalGroup.vbs uses a command-line argument to obtain the names of the computers to which to connect. However, the script relies on hard-coded values for the name of the local group and the name of the global group. I followed this approach because you'll likely connect to different computers each time, but you probably won't change the local group or global group that often.
NewLocalGroup.vbs determines which computers to connect to when you launch the script. To run the script, you use the command
where argument can be one of the following:
- one or more computer names
- the word all (to run the script against all the servers in AD)
- the word file (if you want to use an input file to provide the computer names)
If you want the script to run on only the local computer, you don't need to include an argument. I devised this system for a script that I discussed in "Check Service Status on Local or Remote Servers," March 2004, InstantDoc ID 41670. This article is available for public viewing if you'd like more information about that system.
NewLocalGroup.vbs determines the local group to create and the global group to add from the sLGroup and sGGroup variables, respectively. I set the local and global group names to variables so that you have to change the names in only one spot in the script. Specifically, you need to customize these variables' values in the code that Listing 1 shows. You also need to customize the sDC variable's value with the name of your DC.
If you decide to use an input file to provide the computer names or you want the script to run on all AD computers, you need to make another modification to NewLocalGroup.vbs. I discuss that modification in the next section.
Connecting to Remote Computers
NewLocalGroup.vbs begins with the typical declarations, after which it checks to see whether you supplied an argument when launching the script. If so, the script assigns the argument to the sInput variable, then uses a Select Case statement to evaluate that input. As Listing 2 shows, the Select Case statement checks for three cases: file, all, and /?. If none of those cases apply, the script runs the code in the Case Else clause.
Case "file". The Select Case statement checks to determine whether you specified file as an argument. If so, the script opens and reads the specified input file and assigns its contents (i.e., the computer names) to the sComputerName variable. After closing the file, the script uses a For Each...Next statement to iterate through the computer names in sComputerName and runs the EnumGroups subprocedure against each computer.
Note that if you decide to use an input file, you need to create a text file that contains the names of the computers you want to access. Because the script uses a line return (vbCrLf) as a delimiter, place one name per line in the input file. In addition, in the code at callout A in Listing 2, you need to replace C:\Scripts\Servers.txt with your input file's pathname.
Case "all". The Select Case statement checks to determine whether you specified all as an argument. If so, the script connects to the specified Lightweight Directory Access Protocol (LDAP) service, retrieves the names of all the computers in AD, and assigns them to the colComputers collection. The script then iterates through each computer in colComputers and runs the EnumGroups subprocedure against that computer.
Note that if you decide to use the all argument, you need to customize the code at callout B in Listing 2 with the appropriate path.
Case "/?". The Select Case statement checks to determine whether you specified the Help option (/?) as an argument. If so, the script displays instructions on how to launch NewLocalGroup.vbs.
Case Else. If none of the three specified cases apply (i.e., you specified one or more computer names as the argument), the script runs the code in the Case Else clause. This code iterates through those computers and runs the EnumGroups subprocedure against each computer.
After the script connects to each specified computer, the script executes subprocedures to perform the remaining tasks. EnumGroups is the first subprocedure that the script executes.
Enumerating Existing Local Groups
EnumGroups searches each computer's local groups for the name of the local group you want to add. If the specified group already exists, EnumGroups reports the problem and the script exits.
Technically speaking, the EnumGroups subprocedure isn't necessary because if a script tries to create a group that already exists, the script will fail and you'll receive an error message. However, for the sake of completeness and to make sure that you don't unintentionally tamper with the wrong group, NewLocalGroup.vbs uses this subprocedure.
Enumerating the groups might take a moment, so if the script takes too long to run, you might want to skip this subprocedure. Adapting the script to do so is easy. In the Select Case statement in Listing 2, simply replace each instance of the word EnumGroups with the word NewLocal. This replacement will prompt the script to call the NewLocal subprocedure rather than the EnumGroups procedure.
Creating the Local Group
After NewLocalGroup.vbs knows that the local group in question doesn't exist, the script uses the NewLocal subprocedure to create that group. As Listing 3 shows, this subprocedure connects to the WMI service on the specified computer, creates an object representing that computer, then creates the specified local group. After NewLocal writes the changes to AD, the subprocedure reports that it has successfully created the local group. NewLocal ends by calling the AddGlobal subprocedure.
Adding the Global Group
The AddGlobal subprocedure adds an existing global group (the name of which was specified at the beginning of the script) to the newly created local group. As Listing 4 shows, the AddGlobal subprocedure first creates two objects: an object representing the local group on the specified computer and an object representing the global group hosted by the DC. AddGlobal assigns the local group object and the global group object to the oLGroup and oGGroup variables, respectively. (Although I used a global variable for the name of the global group, you can use a local variable instead if you need to make this subprocedure more portable.) AddGlobal then adds the global group to the local group and reports the results.
A Faster Alternative
NewLocalGroup.vbs takes a minute or so to run because connecting to remote machines and enumerating groups is time-consuming. However, running the script remotely is much faster than going from console to console and manually creating and populating the local groups.