Chapter 23. Automating Tasks Using Windows Server 2003 Scripting

<feature><title>In This Chapter</title> <objective>

Scripting Overview

</objective>
<objective>

Introduction to VBScript

</objective>
<objective>

Active Directory Scripting Overview

</objective>
<objective>

Discovering Object Properties

</objective>
<objective>

Scripting User Management

</objective>
<objective>

Creating a User from File Data

</objective>
<objective>

Searching Active Directory

</objective>
<objective>

Windows Server 2003 Scripting

</objective>
<objective>

Leveraging Sample Scripts

</objective>
</feature>

Microsoft Windows Server 2003 supports performing server management and administrative tasks from both a command line and a graphical user interface. In many cases, these tasks are repetitive and are performed manually. For example, checking each server in a site and manually recording disk volume free space are single tasks on a maintenance checklist. Other manual tasks include creating users and changing group membership. Many tasks associated with server, workstation, or user management can be automated using scripts.

This chapter describes technologies that are used for scripting, including some command-line utilities and scripts written using the Microsoft Visual Basic Scripting language known as VBScript. This chapter also covers some basic scripting commands and provides some examples using WMI, ADO, CDO, and ADSI object models to manage the Windows Server 2003 environment. The purpose of this chapter is to help administrators think of ways to simplify administrative tasks by creating scripts.

Scripting Overview

When a project or task involving scripting comes around, many administrators cringe at the thought. Administrators new in the IT field associate scripting with programming or creating applications. In many instances, that is the case. However, many administrators may already be using or even creating scripts although they disregard such scripts simply because they were too easy to create or did not have loops of code or fancy output. For example, many administrators create login scripts but don’t consider this task to be scripting because the scripts may just be simple batch files to map network drives. If a file automatically executes several commands sequentially or simultaneously, it is a script.

Scripting should not be categorized only as programming unless the use of the script defines it that way. Scripts can be created to break down large or complicated processes into many simple tasks, such as a checklist that can be followed to step someone through the entire process. A good example of a script unrelated to computing and networking is a cooking recipe. If you follow the steps in the recipe, the result could bring a delicious meal. A script that automates the process of creating several hundred user accounts can reduce the time necessary to complete the task and also reduce human error because the input data can be read from a file as opposed to being typed manually.

IT administrators can configure a number of scripts that provide some level of automation when it comes to managing hardware, software, groups, and user accounts within an organization. Scripts can be classified in a number of ways, such as documented instruction guides, server management scripts, workstation management scripts, directory management scripts, and application management scripts. Users may also have separate configuration scripts, including logon and logoff scripts.

Documented Instruction Scripts

A documented instruction script is generally fairly basic in nature. Usually, this script is used by administrators, end users, or personnel as an instruction guide or a step-by-step script that needs to be followed to perform a task. For example, this type of script could test an application’s functionality; it may also be called a quality assurance script. The following script, for example, can be used by help desk personnel to verify basic operation of the domain name system:

  1. Log on to a server or workstation with access to the network with the DNS server.

  2. Choose Start, Run.

  3. Type cmd.exe and click OK to open a command prompt.

  4. Type Nslookup and press Enter.

  5. Type Server followed by the name of the DNS server you want to test. For example, type Server ns1.companyabc.com. Then press Enter.

  6. Type in an Internet record, such as www.microsoft.com, and press Enter.

  7. If an answer is displayed, type quit. Then type exit to close the command prompt.

Although the preceding is really just a step-by-step guide, it is also a script, executed manually. It is a script that checks whether the DNS server can resolve Internet DNS records.

These types of scripts, depending on the skill levels of your staff and the scope of the document, may best be created by IT staff and then formatted, standardized, and cleaned up by a technical writer. One of the biggest factors for scripted instructions is the level of detail necessary. This detail is defined by the target audience. For example, if the preceding script were written for an administrator with a reasonable amount of knowledge with DNS, the script may simply be:

Perform a DNS lookup of an external Internet address using ns1.companyabc.com to verify Internet name resolution.

The steps here are presented so that a person who has only basic computer skills can perform the task.

Common scripts that organizations may benefit from are those that shut down and restart a server. Back in the days when remote server management was very limited, if a server crashed and the administrator was not onsite, this type of document could assist onsite personnel in completing the necessary task of rebooting the server instead of having to wait for the administrator to return.

Server Management

Server management scripts come in a few flavors. Scripts can be written to collect information stored in the Registry, on the hard drive, or in the BIOS. Information such as how long the server has been running, how much free disk space remains, and how many users are using applications and services such as file shares, printers, or terminal server sessions can be collected and analyzed. Files and printer shares can be created and configured remotely without user intervention, and NTFS permissions can be checked and updated using a script.

A script can be created to connect to a server or list of servers, and each server can be shut down, rebooted, or have a service restarted. There are really few limitations on what can be performed on a server using a script. For the most functionality when it comes to managing Windows Server 2003 systems, Microsoft Windows Management Instrumentation (WMI) may provide the most extensive functionality.

Workstation Management

Workstation management is not really different from server management in the way scripts can be used. Tasks that can be performed on workstations rather than on servers are updating security patches and restarting a number of machines. Other examples might be sending out pop-up messages to all workstations on the network that will be affected by a server that is going offline, or telling users to save data before battery backup power runs out during an extended power outage.

Scripts for User Configuration

Scripts to manage users include logon and logoff scripts primarily. Logon and logoff scripts can be used to connect and disconnect network printers and network file shares, clear out old temporary files, or save data stored in a local folder up to the server. Advanced features of logon scripts can include incorporating command-line executables and Visual Basic Script commands to connect to resources based on group membership, create email profiles, configure instant messenger settings, and record logon and logoff statistics.

Directory Administration Scripts

Directory administration scripts can perform many tasks that benefit administrators at all levels of the IT hierarchy. Script usage can include searching directories, creating user accounts, adding members to groups, and much more. To manage user objects in a directory, you can create a script to scan the directory for locked-out or disabled user accounts. Scripts can check whether new accounts were created during a certain period of time and determine which accounts they are.

Some commonly created directory administration scripts include scripts to read from files for directory imports and create output files to update separate directories. User information can be synchronized or overwritten from information stored in separate directories. Objects that commonly are synchronized between directories are user objects and properties, and group objects and their members.

Many organizations create directory scripts to give lower-level administrators and end users a way to manage and access their particular directory object or set of objects. For example, a script could be written for an employee in the Human Resources department who needed to have the ability to create user accounts and modify contact information for all existing employees in the directory. Without the benefit of scripting, this process would have to be performed by loading the administrative tools on the HR user’s desktop and then training him how to create users and how to locate users using the tool. This would be a tedious alternative and would give the user access to more information than is necessary.

A better alternative would be creating a few simple scripts—one to create a user and another to find an existing user in the directory. The scripts would be used to provide an HTML or Visual Basic interface for an HR manager to update employee contact data. Only minimal data would be necessary for user creation, and several fields can be populated from that information. For example, if two required fields were Location and Department, this data could be used to determine group membership, home folder server, profile location, and logon script. To simplify looking up or editing information, you can create pull-down menus to limit the HR manager’s options. As an added bonus to the user creation script, the user account could be set to require a password change at next logon.

Advantages of Scripting

This chapter focuses on automating user management, computer management, and server administration tasks. The scripting language VBScript will be used throughout this chapter along with a few other technologies, including ActiveX Data Objects (ADO) and Windows Management Instrumentation (WMI). A few advantages of using scripts to perform repetitive or tedious tasks is that human error is reduced because scripts will never skip a step or incorrectly type in data when synchronizing information, or even worse, stop a service to perform a maintenance task and forget to start it up afterward. Scripts that will be deployed to automate tasks should be completely tested in a lab environment before being deployed in a production environment.

Introduction to VBScript

VBScript is one of the two scripting languages created by Microsoft. For the scripts in this chapter, VBScript will be used. It is not a replacement for a full programming language such as Visual Basic .NET or Visual C++ but is tailored to provide, in many cases, portable code that can be viewed, modified, and executed on any machine with a VBScript host or interpreter.

Unlike the so-called real programming languages that must be compiled before the system can understand the code, VBScript remains in plain text until actual runtime when the code is interpreted and executed on the system. VBScript commands are not recognized directly by the operating system, so they must be run using a VBScript host or interpreter that can convert the code so that the operating system can execute the commands. VBScript is portable and does not carry a lot of the overhead that can sometimes be associated with compiled applications such as DLL files and such. VBScript files can be run using the Windows Scripting Host (WSH) or be written into a Web page that supports scripting.

The file extension for a VBScript file is .VBS, and this extension is configured by default to run using Wscript.exe. This enables a VBS file to be double-clicked to run just like executable .EXE files. Another option is to run the files in a command-line environment using Cscript.exe.

Visual Basic Script Options

A Visual Basic script is not compiled code, so it must be run within a host or context that can interpret the commands and present them to the operating system so they are executed as desired. You can make sure VBScript code is processed by calling the code using the Windows Scripting Host or adding the code within HTML or ASP Web pages on Web servers that support the VBScript language. Also, before compiling the code, you can add VBScript code to Visual Basic or C-compatible applications to handle certain tasks or functions that can be performed with less code than VBScript.

Windows Scripting Host

Using the Windows Scripting Host, scripts written in VBScript or JScript can be interpreted at runtime and executed on a server or workstation. WSH supports running scripts from the command line using Cscript.exe and supports running scripts within the graphical user interface by using Wscript.exe. Both are part of the Windows Scripting Host program. To see the differences and to dive right into a simple script (recommended in a lab environment) using VBScript, follow these steps:

  1. Before you start creating scripts, create a directory called Scripts on the root of the C drive.

  2. Choose Start, Run. Type notepad.exe and click OK to create a new VBScript. To create the script, type the following code in the Notepad window, pressing Enter after each line:

    Dim CurrentTime
    CurrentTime = time
    Wscript.echo "The current time is "& CurrentTime & "."
    

    The Dim command declares a variable called CurrentTime that can be referenced throughout the rest of the code. The next line of code sets the CurrentTime variable to the value of the time function, which will give you the current time. The last command, Wscript.echo, will display the text enclosed in double quotation marks followed by the value of the CurrentTime variable. Notice the & symbol after the text and after the variable; it is used to tell the Wscript.echo command that there is still more information to echo and to continue writing to the same line.

  3. Save the file with a .VBS extension, using a name such as c:ScriptsVBtime.vbs, and close Notepad.

  4. Choose Start, Run and then type cmd.exe to open a command prompt.

  5. Change directory to the c:Scripts directory.

  6. Type Wscript.exe VBtime.vbs and press Enter. Note how the output is displayed as a pop-up window while Wscript.exe runs by default in the graphical user interface.

  7. Click OK to close the pop-up window and return to the command prompt.

  8. Type Cscript.exe VBtime.vbs and press Enter. Note how the output is displayed in the command-line interface and does not require user intervention to acknowledge the response the way Wscript.exe does.

The reason the same code returns the information in different ways is inherent to the Wscript.exe and Cscript.exe applications. The output is also directly related to the actual commands being called. For example, modify the VBtime.vbs script as follows, save the file, and run the script using both Wscript.exe and Cscript.exe:

Dim CurrentTime
CurrentTime = time
MsgBox "The current time is "& CurrentTime & "."

When this script is run using either Wscript.exe or Cscript.exe, the output is the same: a pop-up window displaying the current time. The reason for this result is that the message box (MsgBox) function is a graphic function, whereas the Wscript.echo command merely echoes output to the current interface.

Note

If a command such as Wscript.echo is used several times in a script, be sure to run the script using Cscript. Otherwise, user intervention will be necessary to close each pop-up window sequentially.

Active Server Pages

Active Server Pages (ASP) running on Windows Server 2003 Internet Information Services (IIS) enable Web developers to include scripting code within dynamic HTML pages. This can be client-side scripting that is downloaded and executed on the end user’s machine or server-side scripting that is executed on the back-end server. Recently, because a few problematic viruses have been written using VBScript, many organizations now disable client-side VBScripting on their proxy servers and firewalls. In such cases, client-side scripting may not function correctly, so server-side scripting should be used. By default when a client chooses to view the source code, the script will not show up, only the returned values.

An ASP Web page can also be created using notepad.exe, but many developers use a program such as Microsoft FrontPage to simplify code creation. Different objects, object properties, and methods associated with VBScript can be used within an ASP Web page. For example, using the WSH, you displayed information to the console using Wscript.echo; but when you want to display information in a Web page, you use the response object and the write method of that object. To create an ASP Web page that will display the current time, follow these steps:

  1. Log on to an IIS server. Open Windows Explorer, and under the default Web site directory, create a folder called ASPscripts. Check to ensure that the Anonymous user account has access to this folder. It should have these permissions already because it will be inheriting permissions from the parent folder. The default location will be c:inetpubwwwroot.

  2. Choose Start, Run. Type notepad.exe and click OK to create a new VBScript. To create the script, type the following code in the Notepad window:

    <HTML>
    <HEAD>
    <TITLE>
    My ASP page Using Vbscript!
    </TITLE>
    </HEAD>
    <BODY>
    <P>
    <%
    DIM CurrentTime
    CurrentTime = time
    response.write "The current time is " & CurrentTime & "."
    %>
    </P>
    </BODY>
    </HTML>
    
  3. Save this file as C:InetpubASPscriptsVBtime.asp on the IIS server.

  4. To test the script, open Internet Explorer on the IIS server and type http://localhost/ASPscripts/VBtime.asp to see the output page, as shown in Figure 23.1.

    A sample ASP Web page using VBScript.

    Figure 23.1. A sample ASP Web page using VBScript.

Note

The Web site created in step 4 should already support Active Server Pages. To enable ASP.NET on this Web site, refer to the Help and Support menu. Also, refer to Chapter 11, “Internet Information Services v6.”

The output of this ASP Web page is simple, but it can be extended using both HTML and VBScript commands. Also, you can use different scripting languages in a single ASP Web page if necessary. The code in this ASP is similar to the VBScript file previously written. Using ASP, though, you need to designate the beginning of the script code using <% and end of the code using %>. This will assume that the default scripting language VBScript is being used. To designate a different language such as JScript or JavaScript, refer to ASP documentation.

Active Directory Scripting Overview

To automate administrative tasks for Windows Server 2003 systems specifically aimed at managing Active Directory objects such as users, groups, and computers, VBScripts must use commands and references associated with predefined programming object models. A programming object model defines the hierarchy of an object, such as a user object in Active Directory or the directory structure itself. An object model defines which properties or attributes of an object can be accessed and also how the object is accessed or changed.

For example, an Active Directory user object has a property called SamAccountName. The value of this property is used as the user’s logon name, and the SamAccountName property is accessed through the property’s Get or Put methods. Active Directory Services Interface (ADSI) provides the Get, GetEx, and GetInfo methods to connect or read data from Active Directory or an Active Directory object. To create or modify an object and its properties, a script would reference the Put, PutEx, or SetInfo method. Microsoft provides several object models that, in some cases, overlap in functionality, but they are usually tailored to provide an interface to a particular type of resource or object.

Active Directory Objects

Active Directory has a few objects that regular administrators will need to access and manage. For example, users, groups, computers, and contact objects will need to be managed to set security, make configuration changes, or add or remove members from groups. To access these objects and read or set values on particular object’s properties, VBScript needs to connect to that object or objects using a specific interface that provides the access.

The interface is commonly referred to as the application programming interface (API), which contains one or several programming object models that can be referenced through it. As an example, the Active Directory Services Interface provides access to an Active Directory user object using a built-in, predefined user object model. The ADSI user object model provides the ability to access most of the user properties. The object properties available are usually defined by what properties are available for that object, as defined in the Active Directory Schema. The Active Directory Schema defines all properties an object could ever have. It defines which properties are mandatory and must be defined before a new object can be created, and it defines the characteristics of object properties. For example, a user object property of Last Name is an optional attribute. If populated, it will need to have at least one character but no more that 128 as defined in the Active Directory Schema.

To access and modify all the attributes on Active Directory objects, you can use several different application programming interfaces and programming object models to manage the entire directory or a single directory object. After the next few sections cover frequently used object and directory interfaces, we will provide and outline a few sample scripts to show how different technologies can be used when you need to script an administrative task. Half the battle associated with scripting is knowing which object models and interfaces can be used to perform the task or access the desired object in the directory. After the use of the interface is revealed, it is only a matter of finding the property names and the methods available to manipulate the object properties.

Active Directory Services Interface

ADSI is a directory service model that was developed to create a single interface to access and modify directories and directory objects. ADSI supports several directories such as Microsoft Exchange 5.5, Novell NetWare NDS, and Microsoft Active Directory. Using ADSI, you can automate many directory-related tasks, such as creating users or dynamically adding or removing members from groups. ADSI will be used in conjunction with ADO, CDO, WMI, and VBScript when devices in the enterprise need to be located and when directory objects need to be created or modified.

Working with Active Directory Objects

Active Directory objects can be created, deleted, or modified using scripts. Before any object can be accessed, a connection to the directory must first be established. You perform this task by presenting a directory services path to the directory container using a standard protocol. For example, to connect to Active Directory, you can use the LDAP protocol to connect and use ADSI to specify the directory container object to connect to. By using the string “LDAP://CN=Users,DC=Companyabc,DC=com”, you can use a specific ADSI method called Get to connect to the Users container in the Companyabc.com domain. When the connection or binding is established, the container can be queried for a list of computers, users, or groups; or new objects can be created. The initial binding to the directory determines the root starting point for directory searches and sets the level of permission granted to the directory, which is based on the user context in which the script is run.

Discovering Object Properties

When Active Directory objects are discussed, the terms object properties and object attributes are frequently used. Sometimes the two can be interchanged. Depending on the interface used to access the object, though, this may not be the case. To make things more confusing, when the Active Directory Users and Computers MMC snap-in is used to view or change a user’s attribute values, the friendly name presented in the graphical user interface may not be the same as the actual directory name. For example, on a user’s Address property page, there is a field labeled City. Accessing this user object directly using ADSI Edit, the directory name for the City field is “l,” which stands for “location,” as shown in Figure 23.2.

Accessing Active Directory user objects.

Figure 23.2. Accessing Active Directory user objects.

To discover the directory names of object attributes and to find out what possible attributes an object can contain, you can use two utilities to simplify this task. The Active Directory Users and Computers MMC snap-in can also provide a roundabout way to find object attributes using the Saved Queries applet.

The directory name of an object’s attribute is used in a script when the script attempts to read or update the attribute’s value. To find the directory name of an attribute, you may find the ADSI Edit MMC snap-in to be the easiest tool to use.

ADSI Edit MMC Snap-in

The ADSI Edit MMC snap-in provides a direct peek into Active Directory partitions to view and modify the objects contained within. ADSI Edit helps you figure out the actual directory names and values of objects and their attributes.

Caution

ADSI Edit is to Active Directory as Registry Editor is to the System Registry. ADSI Edit is a very powerful tool, but it’s important to keep in mind that it doesn’t have built-in safeguards that make Active Directory Users and Computers a relatively idiot-proof application. For example, ADUC doesn’t allow having more than one primary SMTP address for a user account. There is nothing in ADSI Edit that prevents from assigning multiple primary SMTP addresses, and that may cause serious problems. Changes can be irreversible without a restore from backup. Therefore, perform a full backup prior to working with ADSI Edit.

You can use ADSI Edit to manually populate object attributes when the attribute is not readily available using the Active Directory Users and Computers MMC snap-in. This snap-in can be handy when an attribute name is not known. The entire list of attributes can be displayed in a single window within the snap-in. To connect to the Active Directory Domain Naming Context partition, perform the following steps:

  1. Log on to a workstation or server with Domain Admin rights and Local Admin rights on the machine.

  2. Install the Windows Server 2003 support tools from the setup CD-ROM. You can install the support tools by running the setup program D:SupportTools SUPTOOLS.msi, where D represents the letter assigned to the CD-ROM if the setup CD-ROM is used.

  3. After the support tools are installed, choose Start, Run.

  4. Type MMC and click OK to open the Microsoft Management Console.

  5. Choose File, Add/Remove Snap-in.

  6. In the Add/Remove Snap-in window, click the Add button to bring up a list of available snap-ins.

  7. On the Add Standalone Snap-in page, select ADSI Edit Snap-in and click Add.

  8. When the ADSI Edit snap-in is listed in the Add/Remove Snap-in window, click Close on the Add Standalone Snap-in page and click OK in the Add/Remove Snap-in window.

  9. When you’re back in the MMC window, right-click the ADSI Edit applet and select Connect To.

  10. Enter the Active Directory partition or a container’s distinguished name as the connection point. To connect to the entire domain so that you see a view similar to Active Directory Users and Computers, click the radio button labeled Select A Well Known Naming Context. Then choose the domain naming context, as shown in Figure 23.3.

    Selecting the domain naming context as the initial connection point.

    Figure 23.3. Selecting the domain naming context as the initial connection point.

  11. In the Computer section, choose to specify a domain or server or choose the default domain.

  12. Click OK to create the connection.

  13. Choose File, Save.

  14. Save the console as ADSI Edit in the suggested location and click the Save button.

  15. In the console window, expand the domain partition to find objects within the containers in the Active Directory domain.

Discovering the Directory Name of a User Attribute

To find the directory name of a particular user attribute—for example, the Pager attribute—follow this simple process:

  1. Using the Active Directory Users and Computers MMC snap-in, find a test user that can be manipulated. Populate the Office attribute on the user’s General property page using something that will be easy to locate, such as ZZZZ. Save the change and close the user object.

  2. After you save the value, open ADSI Edit by choosing Start, All Programs, Administrative Tools, ADSI Edit. If the console does not appear, perform the steps outlined in the preceding section to create the console.

  3. Browse the directory to locate the correct user object.

  4. Right-click that user and select Properties.

  5. In the Attribute Editor page, click the button labeled Values in the window. This will sort the list of attributes based on the value string, with numbers followed by an alphabetical listing.

  6. Scroll to the bottom to find the value ZZZZ.

  7. Note the particular attribute name associated with the page value.

  8. If the value cannot be located, close the window, right-click the object, choose Refresh, and then open the properties again.

By using the Active Directory Users and Computers MMC snap-in, you find the attribute labeled Office actually has a directory name of PhysicalDeliveryOfficeName. If a script were trying to find this information referencing an attribute called Office, the script would always generate an error.

Active Directory Schema MMC Snap-in

The Active Directory Schema MMC snap-in is a powerful tool that can be used to modify and extend the Active Directory Schema. You can also use it to view and modify the characteristics of directory objects and attributes. For example, if a script will be used to populate a user object’s Pager attribute, by using the Schema MMC snap-in, you can locate the Pager attribute to view attribute settings such as what type of data can be stored in this attribute, minimum and maximum range of characters it can support, and whether the attribute is single valued or multivalued.

To create and use a Schema MMC snap-in, follow these steps:

  1. Log on to a workstation or server with Domain Admin rights and Local Admin rights on the machine.

  2. If you’re using a Windows Server 2003 system, proceed to step 4.

  3. Install the Windows Server 2003 Administration pack from the setup CD. The Administration pack can be installed on Windows XP Professional systems. Install it by running the setup program D:i386Adminpak.MSI, where D represents the letter assigned to the CD-ROM if the setup CD-ROM is used.

  4. After you install the Administration pack, choose Start, Run.

  5. Type the command Regsvr32.exe schmmgmt.dll and click OK. A confirmation popup window should appear, stating that the file has been registered correctly. This makes the Schema MMC snap-in available for use. Click OK to close this pop-up confirmation window.

  6. Choose Start, Run.

  7. Type MMC and click OK to open the Microsoft Management Console.

  8. Choose File, Add/Remove Snap-in.

  9. In the Add/Remove Snap-in window, click the Add button to bring up a list of available snap-ins.

  10. On the Add Standalone Snap-in page, select Active Directory Schema Snap-in and click Add.

  11. When the Active Directory Schema snap-in is listed in the Add/Remove Snap-in window, click Close in the Add Standalone Snap-in page. Then click OK in the Add/Remove window.

  12. Choose File, Save.

  13. Save the console as Schema in the suggested location and click the Save button.

After you create the Schema MMC snap-in, you can review objects to understand which attributes are available for each object. For example, to find out the characteristics of a Pager attribute, follow these steps:

  1. If the Schema MMC snap-in is not open already, choose Start, All Programs, Administrative Tools and select Schema.msc. This, of course, assumes that the console was created as outlined in the preceding steps. Otherwise, open MMC and add the Schema MMC snap-in.

    Note

    When connected, Domain Administrators can view the Schema using this tool, but only members of the Schema Admins group can make modifications to object classes or attributes.

  2. Select the Attributes container in the left pane; then in the right pane, scroll down and select Pager. If you don’t know the directory name of the desired attribute, refer to the “Discovering the Directory Name of a User Attribute” section earlier in this chapter.

  3. Right-click the Pager attribute and select Properties to open a window showing the attribute properties.

  4. If you need to change something—for example, if this attribute should be indexed in the global catalog to improve searches for pager numbers—you can make that change using this window. Only members of the Schema Admins group can make this change. Close this window and the Schema MMC snap-in when you’re finished.

By using the Schema MMC snap-in, you can extend the Active Directory by adding new attributes that can be placed in specific classes, such as the user class. Extending the schema is beyond the scope of this chapter. For more information on extending the schema in a Windows Server 2003 forest of domains, refer to the Help and Support menu on a Windows Server 2003 server. This menu will also scan the Microsoft Knowledge Base on the Internet for relevant articles if the server has such access.

Scripting User Management

Scripts to manage Active Directory users include features such as creating users, searching AD containers to get a list of users, and changing user attribute values for existing users. For the particular user object in AD, mandatory and optional attributes are available.

Each mandatory attribute must have a value in order for the user to be created. Every user will have a value for each mandatory attribute. When searching for a user, you should use these primary attributes. For example, a mandatory user attribute is the SamAccountName attribute. If you want to create a list of user logon names, you can query the domain for user objects and request the value of the SamAccountName attribute for each user object.

An optional attribute could be a user’s pager or telephone number. Locating users based on optional attributes could be effective only if you want to filter a search. For example, if you want to create a list of users in the domain with a last name of Smith, you can get a list of all the users using the SamAccountName attribute, query the last name value, and then compare it to Smith. The list returned would be only users whose last name matches the criteria.

Note

The Last Name field has a directory name of sn, representing surname.

Scripting User Creation

To create a new AD user using ADSI and VBScript, you can break down the process into these four simple steps:

  1. Connect to the directory or specific container object.

  2. Create the user by populating the mandatory attributes.

  3. Populate additional attributes and update the user object.

  4. Exit the script.

To access an Active Directory user object, you use ADSI, but to perform a search, you use ADO. If exchange attributes need to be populated, you use CDO. To create a user, you need to populate one of the mandatory attributes, the CN attribute, at creation. This attribute contains the value that will be used to create the DN, or distinguished name, of the user object. To create a user creation script, type the following code in a new text file using Notepad:

set obj= GetObject("LDAP://cn=users,dc=companyabc,dc=com")
set usr = obj.Create("user","cn="& "TestUser")
usr.SetInfo

Now follow these steps to continue the process:

  1. Save the file as ADuser.vbs in the C:Scripts directory, which will be used to store scripts.

  2. Choose Start, Run.

  3. Type cmd.exe and click OK to open a command prompt.

  4. Type cscript c:scriptsADuser.vbs to execute the script in the command-line environment.

Note

The ADuser.vbs script will not work in this form if a password policy configured in the domain does not allow null passwords.

The ADuser.vbs script will create a user named TestUser in the Users container of the Companyabc.com domain. The first line of the code uses ADSI to connect to the Users container and essentially binds to it. The GetObject method does not specify authentication, so the script runs in the context of the logged-in user. The second line creates a user object in the Users container. The last line actually saves the changes to the directory container specified in the first line of code.

This very basic script, outlined previously, can be modified to connect to a specific organizational unit or even specify a domain controller. The initial connection line using the GetObject method, which uses what is called the ADSPath attribute of an object, is used to bind to the directory. If the initial ADS path to the container you are binding to is unknown, you cannot connect to it using ADSI. The ADSPath attribute is made up of the protocol binding format, followed by the object or container’s DistinguishedName value. To find the DistinguishedName value of the Users container in the Companyabc.com domain, use ADSI Edit as outlined in the “ADSI Edit MMC Snap-in” section earlier in this chapter. Then follow these steps:

  1. Log on to the desired workstation or server with the appropriate level of permissions to open ADSI to browse the directory objects. Usually, membership in the Domain Admin group will suffice.

  2. Choose Start, All Programs, Administrative Tools, ADSI Edit. If the console does not appear, perform the steps outlined in the “ADSI Edit MMC Snap-in” section earlier in this chapter to create the console.

  3. Browse the directory to locate the user or container for which you want to find the DistinguishedName value.

  4. Right-click that object and select Properties.

  5. On the Attribute Editor tab, make sure both the Show Mandatory Attributes and the Show Optional Attributes boxes are checked.

  6. Scroll down in the window to find the DistinguishedName attribute and note the value, as shown in Figure 23.4, for the Users container of the Companyabc.com domain.

    Locating the value of the DistinguishedName attribute for the Users container.

    Figure 23.4. Locating the value of the DistinguishedName attribute for the Users container.

In this case, the DistinguishedName for the Users container is CN = Users,DC = companyabc,DC = com. The DC reference is used for the domain. Separate subdomains also use the DC reference. Organizational units use the OU reference. Users, groups, contacts, containers, computers, and other directory objects use the CN reference. This all comes from the ADSI object model. To use the distinguished name (DN) to construct an ADSPath value for connecting to Active Directory objects, simply add LDAP:// to the beginning of the DN value when referencing it in a script.

Populating Optional User Attributes

After you create a user in Active Directory, you can add optional attribute values. Expanding on the previous three-line user creation script, you can add more attributes after the user is created. The following script populates the pager and initial password attributes:

set obj= GetObject("LDAP://cn=users,dc=companyabc,dc=com")
set usr = obj.Create("user","cn="& "TestUser")
usr.pager = "999-999-9999"
usr.SetInfo
usr.setpassword ="mycleartextpassword"
usr.SetInfo

Save this file as a VBS file and run it using Cscript.exe, as shown in previous examples. The interesting aspect of this example is that the password is set only after the user is created because the user must first exist before the password can be set.

Populating User Attributes Using Variables

When you plan to create many users, populating attributes using data stored in variables can save you many hours. Expanding on the basic user creation script, you can set a user’s logon script path using a variable. If you’re writing a complicated script, using subroutines to perform basic tasks for each object, you will need to declare variables globally so that they can be referenced throughout the script. If a variable will be used only in a particular subroutine, the variable may need to be declared, or it can just be declared and used within that subroutine. To declare a variable and populate a user’s profile path when creating the user, modify the previous script as follows:

Dim ProfilePth
ProfilePth = "companyabc.comProfiles\%Username%"
set obj= GetObject("LDAP://cn=users,dc=companyabc,dc=com")
set usr = obj.Create("user","cn=" & "TestUser")
usr.pager = "999-999-9999"
usr.ProfilePath = ProfilePth
usr.SetInfo
usr.setpassword ="mycleartextpassword"
usr.SetInfo
ProfilePth = ""

The three lines added at the beginning declare the variable. Next, the variable is populated with a value. Three lines later, the ProfilePath attribute is set to the value stored in the ProfilePth variable. Finally, the last line in the script clears the contents of the variable.

Similar to ADSI Edit, ADSI scripting has almost no safeguards. In addition, since a single script can update thousands of user objects in a matter of seconds, there is a risk that a single misspelled character can cause disastrous results.

There are several things that can be done to minimize the risk of mass updates of AD attributes:

  1. Understand exactly what effect the changes will make.

  2. Test the script in the lab.

  3. Create an Active Directory Backup.

  4. Run the script on a limited scope of Active Directory objects (such as a single OU) before doing a full-scale attribute update.

Scripting Exchange 2000 Properties for Active Directory

When Exchange 2000 or Exchange 5.5 with the Active Directory Connector (ADC) is used with an Active Directory forest, the forest schema is extended to support the necessary attributes to give a user or group Exchange messaging attributes. To manipulate a user’s messaging status or configuration, an administrator must use the programming object models made available with Collaborative Data Objects (CDO).

Collaborative Data Objects

CDO provides a programming interface and object model to manage Exchange 5.5 and 2000 server messaging objects using a script written in VBScript or a compiled application written in a programming language such as Visual Basic or Visual C–compatible languages. CDO can be used to create public folders, add contacts to the Exchange address book, and create a user’s mailbox. Scripts that create users in Active Directory can be easily modified to also give these users email addresses or mailboxes on an Exchange 2000 server. To create users and also mail-enable them, you could write a single script in VBScript that will connect to Active Directory using ADSI to create the user object and CDO to mail-enable the users. To mail-enable an Active Directory user in a forest that contains Exchange 2000 servers, follow these steps:

  1. Log on to the desired workstation or server with the appropriate level of permissions to completely administer user objects in Active Directory. This user account must also have a minimum of Exchange 2000 View Only Admin rights in the respective Exchange 2000 Administrative group. For more information on Exchange 2000 organizations and permissions, review Exchange 2000 documentation.

  2. If it has not already been installed on the system, install the Windows Server 2003 Adminpak.msi and install the Exchange 2000 System Tools. The Adminpack will be necessary to use tools such as the Active Directory Users and Computers MMC snap-in to review user status after the script has run. Installing the Exchange System Tools will install and register the CDO.dll so that the CDO object models can be used on this machine.

  3. To extend the user creation script and mail-enable the test user upon creation, create the following script:

    set obj= GetObject("LDAP://cn=users,dc=companyabc,dc=com")
    set usr = obj.Create("user","cn="& "TestUser")
    usr.pager = "999-999-9999"
    usr.SetInfo
    usr.MailEnable "smtp:" & usr.cn & "@domain.com"
    usr.setpassword ="mycleartextpassword"
    usr.SetInfo
    

Using the preceding script will create a user, add a value to the Pager attribute, mail-enable this user (which means giving this user an external email address and adding a reference to the Exchange address book), and finally set the initial password.

Creating a User from File Data

Now that we have stepped through some very basic scripts, we can start tackling more complicated tasks such as creating users from file data. When data is presented to IT personnel for the purpose of creating several user accounts, this data can be referenced and used to help automate user creation. Commonly used file formats include LDIF-compatible files, comma-separated value (CSV) files, and tab-separated value (TSV) files. Files can be read with VBScript using the File System Object (FSO) model. If the files are presented in LDIF or CSV format, the data can be cleaned up and used to create users in Active Directory using either the Ldifde.exe tool or Csvde.exe tool, respectively. Also, if the data necessary to create the directory object is stored in a database or a separate directory, the data can be retrieved using ActiveX Data Objects (ADO) provided that ADO can bind to the database.

Using Ldifde.exe or Csvde.exe

Ldifde.exe and Csvde.exe are both great tools for creating directory objects using clean formatted data presented in LDIF or CSV format. As export tools, their functionality includes exporting data based on location in the directory, OU membership, and object class such as user, group, or computer; they also specify which attributes to export. As far as specifying certain attributes, these tools are fairly limited, but for one-time mass user export and import scripts, they can add value to directory management. To use Ldifde.exe to create an export file of every object in the Users container of Companyabc.com, follow these steps:

  1. Log on to a workstation or server in the Companyabc.com domain with an account that has Domain Admin rights.

  2. Choose Start, Run.

  3. Type cmd.exe and click OK to open a command prompt.

  4. Type the following command and press Enter to execute it:

    ldifde -f UserContainer.txt –d "cn=users,dc=companyabc,dc=com"

    This command creates a file containing all the attributes and values of the User container and all the objects within it.

  5. To open the output file to see what the LDIF format looks like, in the command-prompt window, type the filename to open it in Notepad.

You also can use these tools to perform directory imports if necessary. One limitation is that you cannot set the initial password using these tools.

Connecting to Flat File Data Sources

You can connect to flat files with pure VBScript commands. Using the File System Object model, you can complete file read and write operations.

Files can be opened for reading, writing, or appending to an existing file. When files are opened for reading, the data can be read one character at a time, line by line, or the entire file can be opened and loaded into memory. As an example, the following script will read the boot.ini file of a Windows Server 2003 server; it will then read one line at a time and output that information to the console in a message box until the file reaches the “end of file marker” triggering a final message to be sent.

Dim FSO, TheFile, Line
Set FSO = CreateObject("Scripting.FileSystemObject")
Set TheFile = FSO.OpenTextFile("C:oot.ini", 1)
Do While NOT TheFile.AtEndOfStream
Line = TheFile.ReadLine
MsgBox Line
Loop
MsgBox "The end of the file has been reached."

This script creates a File System Object based on the boot.ini file. The script reads a single line and outputs it to the screen for each line in the file until the file reaches the end. At that point, a confirmation pop-up window states that the end of file has been reached.

Searching Active Directory

Sometimes searching Active Directory can prove to be the most efficient way to create a list of users, groups, or computers in the organization for the purposes of administration or inventory. You can search to find a particular attribute value of an object attribute, or you can search to check whether an object already exists before attempting to create a new one. For example, if you’re running a script to create several hundred users, you could use a search script to find existing user objects that already have the same logon name. A value could be returned to reference that conflicting account, and the script can run for the remaining users. As an alternative, the error generated when a user already exists could be used as a reference, but you would have to know the exact error code that would result. You can use ADSI to search Active Directory, but for faster searches within a script, you should use the ActiveX Data Object (ADO) interface and object model.

ActiveX Data Objects

ADO is a programming object model that is used to access databases and/or directories from within a script or a Web page. ADO can be used not only to access existing databases records, but also to add new records, delete records, or modify existing records. When you use ADO to search databases or directories, you should format the queries using common structured SQL queries. The particular databases being accessed will determine whether the query needs to be modified from standard SQL code.

Creating a Search Using ADO

ADO can be used to search an entire Active Directory domain or just a particular container object. As mentioned previously, the domain’s or container’s ADSPath attribute will be used as the root of the search. You will need to specify the data that should be returned and also use a filter for the search. You can use ADO for a variety of directory and database operations and should research ADO documentation. For connecting to Active Directory, use the following commands to call ADO and prepare to make a connection. After you define these connection settings, you can open a connection to the directory and search string and can pass requested directory information to the directory. To create a connection to Active Directory, using the Companyabc.com domain as an example and returning a list of all the computers in the domain, create a script called findpc.vbs using the following code:

Dim DomainDN, ComputerName
DomainDN = "dc=companyabc,dc=com"
Set oConnection = CreateObject("ADODB.Connection")
oConnection.Provider = "ADsDSOObject"
oConnection.Open "DS Query"
Set oCommand = CreateObject("ADODB.Command")
Set oCommand.ActiveConnection = oConnection
oCommand.CommandText = "Select cn from 'LDAP://" + DomainDN + "' where objectClass='computer'"
Set rsComputers = oCommand.Execute
Wscript.echo "This is the list of all the computers in the domain."
Do While NOT rsComputers.EOF
ComputerName = rsComputers.Fields("cn")
Wscript.echo ComputerName
rsComputers.MoveNext
Loop

By changing only the domainDN variable value to the distinguished name attribute value of the domain, domain container, or organizational unit, you can modify this script for any Active Directory domain.

The preceding code is a basic ADO search that you can easily modify by changing only the CommandText value. This value defines the container object to bind to, what the search criteria are, and what attribute values of the objects that meet the search criteria should be returned to a variable or, in this case, the console.

Searching Using the Active Directory Users and Computers MMC Snap-in

When it comes to creating a search string, many administrators can become frustrated with the formatting of the query. To help simplify this task, the new and improved Active Directory Users and Computers MMC snap-in for Windows Server 2003 has a new applet called Saved Queries. This tool can be used to create a query for searches that administrators perform on a regular basis or for administrative tasks such as finding every user with a particular City value. To use this function, refer to the help pages associated with the Active Directory Users and Computers snap-in.

Note

The query text that is generated using the Saved Queries applet is not directly portable into an ADO search string. To properly format a search string for ADO, refer to the ADO documentation.

Windows Server 2003 Scripting

When it comes to scripting tasks for the Windows Server 2003 system, you can find the server objects in Active Directory using ADSI or ADO. To actually connect to the server to make changes or record information, you should use Windows Management Instrumentation. WMI can be used to access the operating system to collect performance statistics. ADSI can be used to create, query, or modify Active Directory objects through an LDAP or global catalog (GC) connection. Likewise, command-line tools can be used to perform a variety of tasks, from remotely rebooting a server to mapping network drives and printers.

Introducing Windows Management Instrumentation

Windows Server 2003 systems can be managed using the interfaces and object models available with Windows Management Instrumentation. WMI is Microsoft’s implementation of Web-Based Enterprise Management (WBEM), which is an industry initiative, aimed at providing better management and improved remote monitoring of server and network devices in the enterprise. WMI can be used to access and manipulate server files and file security and handle server configurations such as creating file shares or installing printers and managing services. WMI can also be used to manage applications such as terminal services through specific WMI providers.

Creating a Simple WMI Script

As in the previous scripts outlined to manage users, before you can access or manipulate objects, you must know how to connect, what properties can be managed, and how they can be managed. In other words, you need to become familiar with the WMI interface and the object model for servers or workstations. To create a simple WMI script to connect to a specific server and list each of the local volume drive letter assignments and total volume capacity, follow these steps:

  1. Log on to a workstation or server with an account that has administrative rights on the server you want to query.

  2. Choose Start, Run.

  3. Type notepad.exe and click OK. Enter the following code in the Notepad window:

    Set oWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}! \dc1.companyabc.com
    ootcimv2")
    Set colDisks = oWMIService.ExecQuery ("Select * from Win32_LogicalDisk")
    For Each oDisk In colDisks
    If oDisk.DriveType = 3 Then
    Wscript.echo oDisk.Name & vbTab & CStr(Round(oDisk.Size/1048576))
    End If
    Next
    
  4. Using the code from step 3, change the reference from dc1.companyabc.com to the fully qualified domain name of your server or workstation.

  5. Save the file as diskinfo.vbs in the c:Scripts directory and close Notepad.

  6. Choose Start, Run.

  7. Type cmd.exe and click OK to open a command prompt.

  8. At the command prompt, type c:scriptsdiskinfo.vbs and press Enter. The disk drive letters should then be listed along with their volume capacity.

You can extend this script to also list free space by changing the oDisk.Size reference to oDisk.FreeSpace. Change this code in your script to see the difference.

Administrators also usually need to remotely stop and restart a service on a server. Although you can perform this task using a graphical user interface, using a WMI script provides the flexibility to change the script to manage a different server or many servers in an OU or domain. As an example, the following script will connect to the server dc1.companyabc.com and stop and start the World Wide Web Publishing service:

Set oService = GetObject("winmgmts:{impersonationLevel=impersonate}! \dc1
ootCIMV2:Win32_Service.Name=" + Chr(34) + "W3SVC" + Chr(34))
If oService.Started Then
oService.StopService
Wscript.echo "the service has been stopped"
Wscript.sleep 5000
oService.StartService
Wscript.echo "The service has been restarted."
Else Wscript.echo "The service is not currently running and will not be started."
End If
Set oService = Nothing

Using the preceding code, you should change the reference from dc1.companyabc.com to the fully qualified domain name of your server; this will work only if the server already has IIS Web services installed. Because this service may take a few seconds to stop, the wscript.sleep command was added to pause the script for 5 seconds, or 5,000 milliseconds. The better way to let the service finish before restarting is to monitor the status of the service (Starting, Running, Stopping, Stopped, and so on). Another significant improvement would be to enumerate dependent services and restart them along with this service.

To change this script to manage a different service, replace W3SVC with the desired service name. To locate the correct service name to use with this script, open the Services applet, open the properties of the desired service, and locate the service name referenced at the top of the General tab. Use the service name, not the display name.

Leveraging Sample Scripts

In the following sections, we’ve included a few sample scripts that leverage and combine some of the interfaces and object models referenced throughout this chapter. These scripts are ready to run if you type them in as shown. However, these scripts do query and return values from the entire domain, so you should test these scripts in an isolated lab environment or scale down and modify the scripts as necessary.

Finding Orphaned Group Policies

The following script searches for Group Policies that are not linked to any specific container in a domain or are otherwise orphaned. It can be saved as a .vbs script (for example, OrphanedGP.vbs).

Dim GP(10000,2)

Set FSO = CreateObject("Scripting.FileSystemObject")
Set oGPList = FSO.OpenTextFile("OrphanGP.txt",2,True)

Set RootDSE = GetObject("LDAP://RootDSE")
DomainNC = RootDSE.Get("RootDomainNamingContext")

Set con = CreateObject("ADODB.Connection")
con.Provider = "ADsDSOObject"
con.Open "DS Query"
Set command = CreateObject("ADODB.Command")
Set command.ActiveConnection = con
Command.Properties("searchscope") = 2

wscript.echo "Retrieving list of all containers in the domain..."
command.CommandText = "select GPLink,Name,ADsPath from 'LDAP://" & DomainNC & "'
where objectclass='organizationalunit' or objectclass='container' or
objectclass='site' or objectclass='domain'"
Set rs = Command.Execute

wscript.echo "Creating list of all assigned Group Policy objects..."
i = 0
Do While NOT rs.EOF
    tempGPLink = rs.Fields("GPLink")
    GPList = ParseGPLink(tempGPLink)

    'GPList returns a Tab-separated string
    'Split() function parses the string and returns an array
    GPArray = Split(GPList,vbTab)
    For j = 0 To UBound(GPArray)
        GP(i,0) = "{" & Split(GPList,vbTab)(j) & "}"
        GP(i,1) = rs.Fields("ADsPath")
        i = i + 1
    Next
    rs.MoveNext
Loop
Ngp = i

wscript.echo "Retrieving list of all Group Policy objects..."
command.CommandText = "select cn,DisplayName,name from 'LDAP://" & DomainNC & "
' where objectclass='GroupPolicyContainer'"
Set rs = Command.Execute

wscript.echo "Detecting orphan Group Policy objects..."
Do While NOT rs.EOF
    GPName = rs.Fields("DisplayName")
    If TypeName(GPName) = "String" Then
        OUFound = False
        'Searching for a GP in the array of all assigned GPs created
in the previous step
        'It would be more efficient to use a Dictionary object instead of array,
        'but for arrays of this size the performance difference
is not significant
       For i = 0 to Ngp - 1
           If rs.Fields("name") = GP(i,0) Then
               OUFound = True
           End If
       Next
       If NOT OUFound Then
           wscript.echo GPName
           oGPList.WriteLine GPName & vbTab & rs.Fields("cn")
       End If
   End If
   rs.MoveNext
Loop

Function ParseGPLink(GPLink)
    'GPLink attribute can contain links to multiple group policies:
    '"[LDAP://CN={217E2467-F743-4300-812C-2F87FBF9AFD3},CN=Policies,CN=System, DC=mydomain,DC=com;2][LDAP://CN={3CEF68F7-0201-407F-87E9-DF6CF8255E2D},
CN=Policies,CN=System,mydomain=domain-name,DC=com;0]"
    'This function reformats the value to make it a Tab-separated string:
    '"217E2467-F743-4300-812C-2F87FBF9AFD3
    3CEF68F7-0201-407F-87E9-DF6CF8255E2D"
    'Strings like that are much easier to work with
    Dim j, TempArray
    ParseGPLink = ""

    If TypeName(GPLink) = "String" AND Trim(GPLink) <> "" Then
        TempArray = Split(GPLink,"{")
        For j = 1 To UBound(TempArray)
            ParseGPLink = ParseGPLink & Left(TempArray(j),
InStr(TempArray(j),"}") - 1) & vbTab
        Next
        ParseGPLink = Left(ParseGPLink, Len(ParseGPLink)-1)
    End If
End Function

Scanning for Installed Software Components

Many times administrators need to quickly and easily scan a computer or group of computers to determine whether or not a specific software component has been installed. Although the following script scans for Macromedia Flash Player, it can also be used to scan for computers with a specific update installed. For instance, you can change the Query variable:

Query = "SELECT * FROM Win32_QuickFixEngineering WHERE HotFixID='Q329115'"

(where Q329115 is the update you are looking for).

Set RootDSE = GetObject("LDAP://RootDSE")
DomainNC = RootDSE.Get("RootDomainNamingContext")

Set ws = CreateObject("WScript.Shell")
Set FSO = CreateObject("Scripting.FileSystemObject")

Set oSoftwareScan = FSO.OpenTextFile("SoftwareScan.csv",2,True)

Set con = CreateObject("ADODB.Connection")
con.Provider = "ADsDSOObject"
con.Open "DS Query"
Set command = CreateObject("ADODB.Command")
Set command.ActiveConnection = con
Command.Properties("Sort on") = "cn"
Command.Properties("searchscope") = 2

command.CommandText = "select cn,ADsPath from 'LDAP://" & DomainNC & "
' where objectclass='computer' and operatingsystem='Windows 2000 Professional'"
Set rs = command.Execute

Query = "SELECT * FROM Win32_Product WHERE Name='Macromedia Flash Player'"

Do While NOT rs.EOF
    ComputerName = rs.Fields("cn")
    Version = ""
    If Online(ComputerName) Then
        Set WMIRef = GetObject("winmgmts:{impersonationLevel=impersonate}!\"
 & ComputerName)
            Set colProducts = WMIRef.ExecQuery(Query)
            Status = "Not installed"
            For Each oProduct In colProducts
                Version = oProduct.Version
                Status = "Installed"
            Next
       Else
            Status = "Offline"
       End If

       wscript.echo ComputerName & vbTab & Status & vbTab & Version
       oSoftwareScan.WriteLine ComputerName & "," & Status & "," & Version

       ComputerName = Null
       Status = Null
       rs.MoveNext
   Loop
   oSoftwareScan.Close


   Function Online(HostName)
       'Shell ping command is used here to determine whether the computer is online
       'It's done this way only for the purpose of compatibility with Windows 2000
       'Win32_PingStatus class is available for Windows Server 2003
    and should be used instead
       Dim ReturnCode, Results, Line
       Online = False
       Returncode = ws.Run("%comspec% /c ping " & HostName & ".domain-name.com -n 1
   -w 500 > ping.tmp",0,"True")
       set Results = fso.OpenTextFile("ping.tmp",1,False)
       Do While NOT Results.AtEndOfStream
           Line = Results.ReadLine
           If (InStr(Line, "Reply from") > 0) AND (InStr(Line, "unreachable") = 0)
     Then
               Online = True
           End If
       Loop

    Results.Close
    Set Results = Nothing
    FSO.DeleteFile "ping.tmp"
End Function

Checking Local Group Membership

The following script determines local group membership. The script first finds the computer object in Active Directory and then it analyzes group membership of that object by finding the group whose name includes the “-Computers” substring. For example, if a group named “Marketing-Computers” exists, it indicates that the computer belongs to the Marketing department. The script then enumerates members of the local Administrators group to determine whether the Marketing-Admins group is already a member of the local Administrators group. If it isn’t, then the script adds it in there.

The script also contains code to write Application log events if the group is added and whether or not critical errors occurred. Another important piece of this script is the section of code containing the Option Explicit and declared variables. This is a good habit to practice with all scripts even though all the examples in this chapter do not contain them. Option Explicit enforces variable declaration.

Option Explicit
Dim RootDSE, DomainNC, ws, WNetwork, CompName, Department
Dim AdminGroup, AdminGroupFound, oUser, oGroup

on error resume next
Const EventERROR = 1, EventWARNING = 2, EventINFORMATION = 4

Set RootDSE = GetObject("LDAP://RootDSE")
DomainNC = RootDSE.Get("RootDomainNamingContext")

Set ws = CreateObject("WScript.Shell")
Set WNetwork = Wscript.CreateObject("WScript.Network")
CompName = WNetwork.ComputerName

'Determining department where the computer account belongs
Department = GetDepartment(CompName)

If Department <> "" Then
    AdminGroup = Department & "-Admins"
Else
    AdminGroup = "IT-Admins"            'if the computer is not a member of any
 XXX-Computers group, then it should be managed by IT
End If

'Enumerating members of the local Administrators group
Set oGroup = GetObject("WinNT://" & CompName & "/Administrators,group")
AdminGroupFound = False
For Each oUser in oGroup.Members
    If oUser.Name = AdminGroup Then
        AdminGroupFound = True
    End If
Next
If Err.Number <> 0 Then
    'creating an event in the Application log and exiting the script
    ws.Logevent EventERROR, "Error occurred while enumerating members of the
 local Administrators group"
    wscript.quit
End If

'Adding Admin group only if it wasn't found in the local Administrators group
If NOT AdminGroupFound Then
    oGroup.Add "WinNT://" & AdminGroup & ",group"
    If Err.Number <> 0 Then
        ws.Logevent EventERROR, "Error occurred while adding group "
 & AdminGroup & " into the local Administrators group"
    Else
        ws.Logevent EventINFORMATION, "Successully added group "
 & AdminGroup & " into the local Administrators group"
    End If
End If

Set oGroup = Nothing
Set ws = Nothing
Set WNetwork = Nothing


Function GetDepartment(ComputerName)
    Dim oConnection, oCommand, rs, MemberOf, oGroup, oComputer, i
    Set oConnection = CreateObject("ADODB.Connection")
    oConnection.Provider = "ADsDSOObject"
    oConnection.Open "DS Query"
    Set oCommand = CreateObject("ADODB.Command")
    Set oCommand.ActiveConnection = oConnection
    oCommand.Properties("searchscope") = 2

    oCommand.CommandText = "Select ADsPath,cn From 'LDAP://" & DomainNC & "
' Where name='" + ComputerName + "' and objectclass='computer'"

    Set rs = oCommand.Execute

    GetDepartment = ""
    If NOT rs.EOF Then
        Set oComputer = GetObject(rs.Fields("ADsPath"))

        MemberOf = oComputer.GetEx("memberOf")

        If Err.Number <> 0 Then
'MemberOf attribute is not populated (the object is not a member of any group)
            Err.Clear
            Exit Function
        End If

        If TypeName(MemberOf) = "String" Then
'MemberOf attribute is single-valued (the object is a member of a single group)
            Set oGroup = GetObject("LDAP://" & MemberOf)
            If Instr(oGroup.cn,"-Computers") > 0 Then
                GetDepartment = Left(oGroup.cn, Len(oGroup.cn) - 10)
            End If
        Else
'MemberOf attribute is multi-valued (the object is a member of multiple groups)
            For i = 0 To UBound(MemberOf)
                Set oGroup = GetObject("LDAP://" & MemberOf(i))
                If Instr(oGroup.cn,"-Computers") > 0 Then
                    GetDepartment = Left(oGroup.cn, Len(oGroup.cn) - 10)
                    Exit For
                End If
                Set oGroup = Nothing
            Next
        End If
    End If
End Function

Locating Domain Printers

The following script creates a list of all printers in Active Directory and outputs the information gathered in a text file called AllPrinters.csv. Here is some other pertinent information:

  • Local printers (connected to Win9x and other client OS computers) are skipped.

  • The script uses LDAP query to create a recordset of all servers in the domain.

  • Another query is used to find all printers for every server.

  • The script uses WMI to bind to Win32_Printer objects.

The full script is as follows:

Option Explicit
Dim RootDSE, DomainDN, FSO, oFullLog, oConnection, oCommand, rsServers,
rsPrinters
Dim Sep, PrintServer, oPrinter

Set RootDSE = GetObject("LDAP://RootDSE")
DomainDN = RootDSE.Get("RootDomainNamingContext")

Set FSO = CreateObject("Scripting.FileSystemObject")
Set oFullLog = FSO.OpenTextFile("AllPrinters.csv", 2, True)

Set oConnection = CreateObject("ADODB.Connection")
oConnection.Provider = "ADsDSOObject"
oConnection.Open "DS Query"
Set oCommand = CreateObject("ADODB.Command")
Set oCommand.ActiveConnection = oConnection
oCommand.Properties("searchscope") = 2
oCommand.Properties("Sort on") = "cn"

oCommand.CommandText = "Select cn,ADsPath from 'LDAP://" & DomainDN &
"' Where objectClass='computer' and OperatingSystem=
'Windows 2000 Server' OR OperatingSystem='Windows Server 2003'"
Set rsServers = oCommand.Execute
Sep = Chr(34) & "," & Chr(34)

On Error Resume Next

Do While NOT rsServers.EOF
    PrintServer = False
    oCommand.CommandText = "Select PrinterName,cn from '" &
rsServers.Fields("ADsPath") & "' where objectClass='printQueue'"
    Set rsPrinters = oCommand.Execute

    Do While NOT rsPrinters.EOF
        If NOT PrintServer Then
            Wscript.echo rsServers.Fields("cn")
            PrintServer = True
        End If

        Set oPrinter = GetObject("winmgmts:{impersonationLevel=impersonate}!
\" & rsServers.Fields("cn") & "
ootcimv2:Win32_Printer.DeviceID=
" & Chr(34) & rsPrinters.Fields("PrinterName") & Chr(34))

        Wscript.echo vbTab & rsPrinters.Fields("PrinterName") & vbTab &
txtPrinterStatus(oPrinter.PrinterStatus)

        oFullLog.WriteLine Chr(34) & rsServers.Fields("cn") & Sep &
rsPrinters.Fields("PrinterName") & Sep & oPrinter.DriverName &
Sep & oPrinter.Location & Sep &
oPrinter.Description & Sep & txtPrinterStatus(oPrinter.PrinterStatus)
& Chr(34)

        Set oPrinter = Nothing
        rsPrinters.MoveNext
    Loop

    Set rsPrinters = Nothing
    rsServers.MoveNext
Loop

Set rsServers = Nothing
Set oCommand = Nothing
Set oConnection = Nothing

oFullLog.Close
Set oFullLog = Nothing
Set FSO = Nothing
Set RootDSE = Nothing
Function txtPrinterStatus(PrinterStatus)
    Select Case PrinterStatus
        Case 1 : txtPrinterStatus = "Other"
        Case 2 : txtPrinterStatus = "Unknown"
        Case 3 : txtPrinterStatus = "Idle"
        Case 4 : txtPrinterStatus = "Printing"
        Case 5 : txtPrinterStatus = "Warmup"
        Case Else txtPrinterStatus = "Unknown Status"
    End Select
End Function

Creating Users from Data in a CSV File

The following script creates users in the Users container in Active Directory. The script reads data for user creation in a Users.csv file. The file header defines attributes that will be populated. Only two attributes are mandatory: SamAccountName (also called the logon name) and CN (the canonical name). All other attributes are optional, including the password. The sample data used for this script would appear as follows if the Users.csv file were opened in Notepad:

SamAccountName,CN,GivenName,SN,Initials,Password
jsmith,John Smith,John,Smith,T,mysecretpassword
brobinson,Bob Robinson,Bob,Robinson,K,mysecretpassword

Attribute names should be specified exactly as they are defined in the Active Directory schema. Probably the best tool to determine which attributes should be specified is the ADSI Edit tool. If the script fails to create a user, an error message appears. Usually, errors are caused by an invalid attribute value or by the fact that a user with the same SamAccountName already exists in the domain. If at least one of the fields is specified incorrectly, an error will be reported for all users. Finally, all users are enabled after the creation.

The full script is as follows:

Option Explicit
Dim RootDSE, DomainDN, oContainer, FSO, oUserList
Dim Line, Header, SamaccountnameIndex, CnIndex, PasswordIndex,
 AttributeValue, oUser, i

Set RootDSE = GetObject("LDAP://RootDSE")
DomainDN = RootDSE.Get("RootDomainNamingContext")

Set oContainer = GetObject("LDAP://CN=Users," + DomainDN)

Set FSO = CreateObject("Scripting.FileSystemObject")
Set oUserList = FSO.OpenTextFile("Users.csv",1,False)

on error resume next

Line = LCase(oUserList.ReadLine)
Header = Split(Line,",")

SamaccountnameIndex = -1
CnIndex = -1
PasswordIndex = -1
For i = LBound(Header) To UBound(Header)
    If Header(i) = "samaccountname" Then
        SamaccountnameIndex = i
    ElseIf Header(i) = "cn" Then
        CnIndex = i
    ElseIf Header(i) = "password" Then

        PasswordIndex = i
    End If
Next
If SamaccountnameIndex = -1 OR CnIndex = -1 Then
    Wscript.echo "Incorrect header. One of the mandatory fields is missing."
    wscript.quit
End If

Do While NOT oUserList.AtEndOfStream
    Line = oUserList.ReadLine
    AttributeValue = Split(Line,",")

    Wscript.echo AttributeValue(CnIndex)
    set oUser = oContainer.Create("user","cn="& AttributeValue(CnIndex))
    oUser.samAccountName = AttributeValue(SamAccountNameIndex)
    oUser.SetInfo

    If Err.Number <> 0 Then
        Wscript.echo vbTab + "Error occurred while creating the user"
    Else
        Wscript.echo vbTab + "User created successfully"

        For i = LBound(Header) To UBound(Header)
            If i <> CnIndex AND i <> SamaccountnameIndex AND i <> _
PasswordIndex Then
               oUser.Put Header(i),AttributeValue(i)
           ElseIf i = PasswordIndex Then
               oUser.SetPassword AttributeValue(i)
           End If
       Next
       oUser.AccountDisabled = False
       oUser.SetInfo

       If Err.Number <> 0 Then
           Wscript.echo vbTab + "Error occurred while setting user properties"
           Err.Clear
       Else
           Wscript.echo vbTab + "User properties were set successfully"
       End If
   End If

   Set oUser = Nothing
   Line = Null

    AttributeValue = Null
Loop

oUserList.Close
Set oUserList = Nothing
Set FSO = Nothing
Set RootDSE = Nothing

Checking Domain Servers for Volume Free Space

The following script expands on the sample WMI script outlined earlier in this chapter. The script scans all Windows 2000 and Windows Server 2003 servers in a domain and reports free space and total capacity (in megabytes and percents) on every logical drive. The script uses LDAP query to create a recordset of all servers in the domain. It also uses Windows Management Instrumentation to demonstrate usage of this technology, but the same information can also be collected using the File System Object model.

Option Explicit
Dim oRootDSE, DomainDN, FSO, oLogFile, oConnection, oCommand, rsServers
Dim ComputerName, oWMIService, colDisks, oDisk
Dim PercentFree

Set oRootDSE = GetObject("LDAP://RootDSE")
DomainDN = oRootDSE.Get("RootDomainNamingContext")

Set FSO = CreateObject("Scripting.FileSystemObject")
Set oLogFile = FSO.OpenTextFile("FreeSpace.csv",2,True)

Set oConnection = CreateObject("ADODB.Connection")
oConnection.Provider = "ADsDSOObject"
oConnection.Open "DS Query"
Set oCommand = CreateObject("ADODB.Command")
Set oCommand.ActiveConnection = oConnection
oCommand.Properties("searchscope") = 2

oCommand.CommandText = "Select ADsPath,cn From 'LDAP://" & DomainDN & _
 "' Where objectClass='computer' and OperatingSystem='Windows 2000 Server'
 OR OperatingSystem='Windows Server 2003'"
Set rsServers = oCommand.Execute

On Error Resume Next

Do While NOT rsServers.EOF

    ComputerName = rsServers.fields("cn").value
    Wscript.echo ComputerName

    Set oWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!
\" & ComputerName & "
ootcimv2")
    Set colDisks = oWMIService.ExecQuery ("Select * from Win32_LogicalDisk")

    For Each oDisk In colDisks
        If oDisk.DriveType = 3 Then        'Local Hard Drive
            PercentFree = Round(oDisk.FreeSpace/oDisk.Size*100)
            Wscript.echo oDisk.Name & vbTab & _
CStr(Round(oDisk.FreeSpace/1048576)) & "/" & _
CStr(Round(oDisk.Size/1048576)) & vbTab & CStr(PercentFree) & "%"
           oLogFile.WriteLine ComputerName & "," & oDisk.Name &
"," & CStr(Round(oDisk.FreeSpace/1048576)) & "," & _
CStr(Round(oDisk.Size/1048576)) & "," & CStr(PercentFree)
       End If
   Next

   If Err.Number <> 0 Then
       Wscript.echo "Error while collecting information from " & ComputerName
       Err.Clear
   End If

   Set oWMIService = Nothing
   Set colDisks = Nothing
   rsServers.MoveNext
Loop

Set rsServers = Nothing
Set oCommand = Nothing
Set oConnection = Nothing
oLogFile.Close
Set oLogFile = Nothing
Set FSO = Nothing

Summary

Scripting for Windows Server 2003 systems and Active Directory can simplify many IT-related administrative tasks. Leveraging the scripting languages provided by Microsoft and the many different programming interfaces and object models, there is almost no task that cannot be scripted if the device or object is referenced correctly. This chapter just scratched the surface on scripting possibilities and, we hope, has sparked ideas on how scripting can simplify managing an IT environment.

Best Practices

  • If a command such as Wscript.echo is used several times in a script, be sure to run the script using Cscript.

  • Use Active Directory Users and Computers and ADSI Edit to discover the directory names of object attributes and to find out what possible attributes an object can contain.

  • Populate attributes using variables to save time when writing scripts that will create several user accounts.

  • Use a script to create users and also mail-enable those users.

  • Use Ldifde.exe and Csvde.exe for creating directory objects using clean formatted data presented in LDIF or CSV format.

  • Use WMI to access and manipulate server files and file security.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset