In "Connecting to Printers," August 2002, http://www.winnetmag.com, InstantDoc ID 25652, and "Connecting Users to Network Resources," June 2002, http://www.winnetmag.com, InstantDoc ID 24893, I explained how to assign network printers and shares to users based on computer names and usernames. In response to these articles, readers sent email messages that asked whether they could make such assignments for more than one user or computer at a time. You can, but it requires a little more work than making selections based on individual names. Let's look at how you can assign network resources to computers with similar names and to users based on their group memberships.
Assigning Resources to Similarly Named Computers
After reading "Connecting to Printers," a reader contacted me with this question: "Suppose that I have 20 Windows 2000 PCs (named Gamma1, Gamma2 ... Gamma20) that I need to set to printer1. Can I use a Select Case statement and test for Case "Gamma*" to access those PCs?" If you used the Select Case statement as you've described, VBScript would try to match the string Gamma* with the exact value Gamma*—in other words, the only matches would be for computers named Gamma*. The wildcard character that you're accustomed to using in searches or from the command line won't work without VBScript's RegExp object, which I haven't discussed yet in a Scripting Solutions column. However, if you've named computers based on a naming scheme organized according to computer location, function, or some other rule, you can look for matches based on the common root of those names.
For example, suppose that your company has workstations set up in the lab, library, and reception area, and you've named these workstations according to their location. The lab workstations have names that start with the prefix LAB, the library workstations have names that start with LIBR, and the reception area workstations have names that start with REC. All the workstation names end with two-digit numbers, so the workstation names look something like LAB01, LIBR19, and RECP03.
Listing 1 shows SetPrinter.vbs, a script that sets the default printers for these three groups of workstations. SetPrinter.vbs starts by defining the variables and creating the oNetwork object to represent a WshNetwork object. Next, the script uses the Left function to capture a three-character prefix from each workstation name, then assigns that prefix to a variable.
VBScript's Left and Right functions count the number of characters in a string and return the number of characters you specify. The Left function returns the specified number of characters from the left side of the string, whereas the Right function returns the specified number of characters from the right side of the string. The syntax of both functions is simple: They take as arguments the string on which to operate and the number of characters to return. So, for example, if you type
the Left function returns the value go. If you type
the Right function returns the value rilla.
The first argument of the Left or Right function doesn't have to be a literal string; you can instead use code that returns a string at runtime. As the code at callout A in Listing 1 shows, SetPrinter.vbs uses the WshNetwork object's computerName property to obtain the computer's name at runtime. The Left function then captures the first three letters of the computer's name and assigns them to the string variable called sShort. As this script illustrates, the number of characters in sShort doesn't need to be the same as the number of characters in the prefix that you're using in the computers' names. As long as the characters in sShort uniquely identify a particular group of computers, the script will work.
Finally, SetPrinter.vbs uses the Select Case statement to compare the characters in sShort with the strings Lib, Lab, and Rec. When sShort matches one of those strings, the script assigns the appropriate printer path to the sPrintPath variable. SetPrinter.vbs then uses the sPrintPath variable with the WshNetwork object's AddWindowsPrinterConnection and SetDefaultPrinter methods to add a printer connection and assign the default printer, respectively.
Assigning Resources to User Groups
The Left function is useful if you're dealing with computers with similar names, but people aren't so conveniently named—and even if they were, you probably wouldn't assign network resources to people according to whether their names begin with JOH or ANN. However, you can assign network resources according to the groups to which users belong. One reader who is migrating from Novell NetWare to Win2K sent an email message that asked, "Is there an object that can expose the group membership of the user so that I can assign the drive letters based on their groups?" Such an object exists, but you can't access it through WshNetwork or any other Windows Script Host (WSH) object. To read group or organizational unit (OU) memberships for individual accounts, you need to use Microsoft Active Directory Service Interfaces (ADSI). Before I show you how to use ADSI for this purpose, you need to know some ADSI basics.
A Brief Primer on ADSI
ADSI is a set of management interfaces for abstracting your interaction with objects contained in a directory service, whether that directory service is Win2K's Active Directory (AD), Windows NT's SAM, or another supported directory service. The properties and methods available to you will vary with the directory service you're using, but the basic structure for using these objects in a script won't vary after you've retrieved them. You can use ADSI to perform any of the following tasks in a directory service:
- List users, groups, computers, OUs, domains, and other objects.
- Create new users and groups.
- View and edit group and OU memberships.
- Start and stop services.
- Create, edit, delete, or list file shares, print queues, and user sessions.
To bind to ADSI objects, you use the GetObject function with one argument. This argument must consist of at least two items: the name of the namespace to which you want to connect and the distinguished name (DN) of the object to which you want to connect in that namespace. Two slashes and a colon (//:) separate these two items. You must enclose the argument in quotes.
The namespace you use depends on the OS on which you're running the script and the kind of information for which you're looking. Two namespaces that administrators commonly use are WinNT and LDAP. Note that all the ADSI namespaces are case sensitive, so you must put the WinNT namespace in mixed case, as I've done here. ADSI namespaces are one of the few instances in which VBScript is case sensitive.
If you run the script on a Windows Server 2003 or Win2K machine, you can use either the WinNT or the LDAP namespace. However, although Windows 2003 and Win2K support the WinNT namespace, that namespace isn't hierarchical. As a result, you can connect to user and computer accounts in a domain, but you can't specify a particular subset of a domain because NT doesn't recognize those subsets. For a detailed view of a domain's directory service, you need to use the LDAP namespace.
If you run the script on an NT machine, you must always use the WinNT namespace. Your script won't work if you use the LDAP namespace.
Listing 2 shows sample GetObject functions that use the LDAP and WinNT namespaces. The first sample function uses the LDAP namespace to bind to the object representing the TS Clients OU in the labrynth.redroom.com domain. The second sample function uses the WinNT namespace to bind to the object representing the Horace user account in the REDROOM domain.
In the second sample function, note the use of ,User at the end of the argument. In WinNT namespaces, you can include the class name of the object to which you want to bind. In this case, the class is User. Providing as much information as possible is a good habit to get into when binding to ADSI objects. Without this information, ADSI would likely bind to the object, but the more information you supply to the GetObject function, the faster the binding.
That's enough about ADSI for our purposes here. If you'd like to learn more about ADSI, check out the articles in "Related Reading."
Applying What You've Learned
Let's take what you've learned so far and use ADSI to assign resources to people based on their group memberships. To do this, you need to collect the username and domain of the user currently logged on, plug that information into a GetObject function to bind to that user object, then retrieve a list of groups to which that user belongs. After you have that list, you can use VBScript's For Each...Next statement to perform an action for each group in the list. In this case, the action is comparing the group's name with a list of possible group names. If a match occurs, you map the appropriate network resource. You use the same WshNetwork object methods and properties that I showed you how to use in "Connecting Users to Network Resources." Listing 3 shows what the script might look like.
If you customize and run the script SetResource.vbs in Listing 3, it will assign network resources based on the group to which the user belongs. However, if your users belong to more than one group, you'll run into problems using this script as is. SetResource.vbs maps a drive letter to a network resource based on the user's group; if the script encounters a second group for the user, the script can't map the drive again. Instead, the script will end with an error. To avoid this problem, you have two options: You can prevent SetResource.vbs from trying to map a second network resource to the same user, or you can unmap the existing network drive before mapping the new one.
Preventing a second-mapping attempt. To prevent the script from trying to map a second network resource to the same user, you can use WScript object's Quit method to terminate the script after a match occurs. Except for the Case Else clause (which already includes the Quit method), you need to add the Quit method at the end of each case, as the following code shows:
Case "Domain Users" oNet.MapNetworkDrive "X:", _ "\\server\users" WScript.Quit
However, if your script performs other tasks after mapping a network resource, you won't want the script to terminate after it maps the resource. In such cases, you can use the Exit statement to exit the For Each...Next loop. After a mapping occurs, the script stops running through the group names and proceeds to the code that follows the Next clause. To implement this approach, you add at the end of each case an Exit statement that includes the For keyword, as the following code shows:
Case "Domain Users" oNet.MapNetworkDrive "X:", _ "\\server\users" Exit For
Because the Case Else clause already includes the Quit method, you wouldn't add an Exit statement to the Case Else clause.
No matter whether you use the Quit method or the Exit statement, you need to be careful about the order in which you place the cases in the Select Case statement. Let's assume that you want those users who are in multiple groups to be mapped to the most powerful network resource available to them. For both the Quit method and the Exit statement, you must put the most powerful group first. For example, if you're matching against two groups—Domain Users and Domain Administrators—you would put the Case "Domain Administrators" code first, followed by the Case "Domain Users" code. (The Case Else clause always goes last.)
Unmapping an existing network drive. If your users belong to more than one group, you can prevent SetResource.vbs from erroring out because of an existing network drive. To do so, you unmap the existing drive, then map to the new drive. This process uses the WshNetwork object's RemoveNetworkDrive method, as the following code shows:
Case "Domain Administrators" oNet.RemoveNetworkDrive "X:" oNet.MapNetworkDrive "X:", _ "\\server\users"
Like the Quit method and the Exit statement, the RemoveNetworkDrive method requires that you place the cases in the Select Case statement in a specific order. However, when you use the RemoveNetworkDrive method, you must place the most powerful group last and the least powerful group first. In addition, you don't include the RemoveNetworkDrive method in the code for the first case.
What's in Common?
If you want to assign network resources to more than one user or computer at a time, you can look for elements common to those users or computers. Whether
the common elements are groups to which users belong, computer names based on location or function, or something else, you can take advantage of the Select Case statement to assign network resources.
You can obtain the following articles from Windows & .NET Magazine's Web site at http://www.winnetmag.com.|
Scripting Solutions, "Easy Active Directory Scripting
for Systems Administrators, Part 2,"
November 2000, InstantDoc ID 15734
Scripting Solutions, "Easy Active Directory Scripting
for Systems Administrators, Part 1,"
September 2000, InstantDoc ID 9168
You can obtain the following articles from Windows Scripting Solution's Web site at http://www.winscriptingsolutions.com.
ALISTAIR G. LOWE-NORRIS
"An ADSI Primer, Part 12: Extending the Schema,"
December 1999, InstantDoc ID 7666
"An ADSI Primer, Part 11: More on Scripting
Permissions and Auditing," November 1999, InstantDoc ID 7456
"An ADSI Primer, Part 10: Permissions and
Auditing Basics," October 1999, InstantDoc ID 6188
"An ADSI Primer, Part 9: Manipulating Print Queues
and Print Jobs," September 1999, InstantDoc ID 6128
"An ADSI Primer, Part 8: More About Manipulating
Persistent and Dynamic Objects," August 1999, InstantDoc ID 5878
"An ADSI Primer, Part 7: Manipulating Persistent and
Dynamic Objects," July 1999, InstantDoc ID 5629
"An ADSI Primer, Part 6: Using ADSI to Create and
Manipulate User Accounts," June 1999, InstantDoc ID 5456
"An ADSI Primer, Part 5: Using ADO to Script Fast
ADSI Searches," May 1999, InstantDoc ID 5281
"An ADSI Primer, Part 4: Manipulating the Property
Cache," April 1999, InstantDoc ID 5126
"An ADSI Primer, Part 3: IADs and the Property Cache,"
March 1999, InstantDoc ID 4991
"An ADSI Primer, Part 2: Manipulating Active Directory Objects,"
February 1999, InstantDoc ID 4818
"An ADSI Primer, Part 1," January 1999, InstantDoc ID 4732