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.
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.
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:
Log on to a server or workstation with access to the network with the DNS server.
Choose Start, Run.
Type Nslookup
and press Enter.
Type Server
followed by the name of the DNS server you want to test. For example, type Server ns1.companyabc.com
. Then press Enter.
Type in an Internet record, such as www.microsoft.com, and press Enter.
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 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 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 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 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.
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.
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.
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.
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:
Before you start creating scripts, create a directory called Scripts on the root of the C drive.
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:
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.
Save the file with a .VBS extension, using a name such as c:ScriptsVBtime.vbs
, and close Notepad.
Choose Start, Run and then type cmd.exe
to open a command prompt.
Change directory to the c:Scripts directory.
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.
Click OK to close the pop-up window and return to the command prompt.
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.
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:
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
.
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>
Save this file as C:InetpubASPscriptsVBtime.asp
on the IIS server.
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.
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.
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 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.
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.
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.
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.
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.
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.
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:
Log on to a workstation or server with Domain Admin rights and Local Admin rights on the machine.
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.
After the support tools are installed, choose Start, Run.
Type MMC
and click OK to open the Microsoft Management Console.
Choose File, Add/Remove Snap-in.
In the Add/Remove Snap-in window, click the Add button to bring up a list of available snap-ins.
On the Add Standalone Snap-in page, select ADSI Edit Snap-in and click Add.
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.
When you’re back in the MMC window, right-click the ADSI Edit applet and select Connect To.
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.
In the Computer section, choose to specify a domain or server or choose the default domain.
Click OK to create the connection.
Choose File, Save.
Save the console as ADSI Edit in the suggested location and click the Save button.
In the console window, expand the domain partition to find objects within the containers in the Active Directory domain.
To find the directory name of a particular user attribute—for example, the Pager attribute—follow this simple process:
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.
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.
Browse the directory to locate the correct user object.
Right-click that user and select Properties.
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.
Scroll to the bottom to find the value ZZZZ.
Note the particular attribute name associated with the page value.
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.
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:
Log on to a workstation or server with Domain Admin rights and Local Admin rights on the machine.
If you’re using a Windows Server 2003 system, proceed to step 4.
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.
After you install the Administration pack, choose Start, Run.
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.
Choose Start, Run.
Type MMC
and click OK to open the Microsoft Management Console.
Choose File, Add/Remove Snap-in.
In the Add/Remove Snap-in window, click the Add button to bring up a list of available snap-ins.
On the Add Standalone Snap-in page, select Active Directory Schema Snap-in and click Add.
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.
Choose File, Save.
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:
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.
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.
Right-click the Pager attribute and select Properties to open a window showing the attribute properties.
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.
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.
To create a new AD user using ADSI and VBScript, you can break down the process into these four simple steps:
Connect to the directory or specific container object.
Create the user by populating the mandatory attributes.
Populate additional attributes and update the user object.
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:
Save the file as ADuser.vbs in the C:Scripts directory, which will be used to store scripts.
Choose Start, Run.
Type cmd.exe
and click OK to open a command prompt.
Type cscript c:scriptsADuser.vbs
to execute the script in the command-line environment.
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:
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.
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.
Browse the directory to locate the user or container for which you want to find the DistinguishedName
value.
Right-click that object and select Properties.
On the Attribute Editor tab, make sure both the Show Mandatory Attributes and the Show Optional Attributes boxes are checked.
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.
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.
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.
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:
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).
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:
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.
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.
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.
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.
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:
Log on to a workstation or server in the Companyabc.com
domain with an account that has Domain Admin rights.
Choose Start, Run.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
Log on to a workstation or server with an account that has administrative rights on the server you want to query.
Choose Start, Run.
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
Using the code from step 3, change the reference from dc1.companyabc.com
to the fully qualified domain name of your server or workstation.
Save the file as diskinfo.vbs
in the c:Scripts directory and close Notepad.
Choose Start, Run.
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.
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.
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
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
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
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:
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
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
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
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.
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.