Downloads
38286.zip

I always feel good when I find a nifty tool that solves some administrative problem. I feel even better when I can script against that tool to automate a task within my environment. IADsTools is such a tool. Found in Windows 2000 Support Tools, IADsTools is a DLL, not a simple command-line utility that you can call. However, it is a COM-based ActiveX automation object that you can invoke from your Windows Script Host (WSH) scripts to perform a variety of actions related to Active Directory (AD), AD replication, and Group Policy. An associated Microsoft Word document in the Win2K Support Tools installation directory describes IADsTools's functions. Let's take a look at a few sample scripts to illustrate the power of some of the more valuable functions that IADsTools provides.

Installing IADsTools
After you install Win2K Support Tools from the \support\tools folder of the Win2K Server CD-ROM, the IADsTools.dll and .doc files will be in the \%programfiles%\support tools directory. To begin using the DLL in your WSH scripts, the DLL must be registered in the registry as a COM object. Open a registry editor and look in HKEY_CLASSES_ROOT for entries beginning with IADsTools. If you find these entries, you're ready to start scripting. If not, you need to register the DLL manually. To do so, start a Windows command shell, navigate to the Win2K Support Tools installation directory, and type

regsvr32 iadstools.dll

After you successfully register the DLL in the system registry , you can begin scripting against it. You can also unregister the DLL, if necessary, by typing

regsvr32 /u iadstools.dll

Before You Get Started
Before you use IADsTools, you should be aware of a few bugs and what steps you can take to work around these problems. The Microsoft articles "Memory Leak with the GetDirectPartnersEx() Function in Iadstools.dll" (http://support.microsoft.com/?kbid=305832) and "Memory Leak Calling the DsGetDcName() or DsGetSiteName() Function" (http://support.microsoft.com/?kbid=300917) describe some memory leaks within IADsTools. The version of Win2K Support Tools that comes with Win2K Service Pack 3 (SP3) corrects these bugs. I strongly recommend that you implement both Win2K SP3 and the Win2K Support Tools update on the servers and clients on which you plan to run IADsTools scripts. You can download Win2K SP3 at http://www.microsoft.com/windows2000/downloads/servicepacks/sp3/default.asp and download the Win2K Support Tools update at http://www.microsoft.com/windows2000/downloads/servicepacks/sp3/supporttools.asp.

Preparing to Use IADsTools
To take advantage of IADsTools's functions, you need to configure your scripts to use the DLL. I used VBScript to prepare all the sample scripts in this article, but you can use any WSH-supported scripting language. To begin, an IADsTools script must include a call to instantiate the scripting object:

Set objIADS=CreateObject
  ("IADsTools.DCFunctions")

In this case, the COM programmatic identifier (ProgID) IADsTools.DCFunctions creates an object called objIADS that exposes the functions we're interested in. I also suggest enabling verbose logging of the IADsTools calls in IADsTools scripts—the DLL provides a function called EnableDebugLogging() for this purpose. I typically enable logging in my scripts immediately after I instantiate the IADsTools object by including the following call:

objIADS.EnableDebugLogging(3)

The number in parentheses that appears after the object call indicates the level of verbosity that I want; for example, level 1 records only errors; level 2 records errors and warnings; and level 3 records errors, warnings, and informational messages. Figure 1 shows a sample success message that an IADsTools script recorded.

By default, an IADsTools script sends the logged messages to the Application event log, but you can specify the name of a text file that should also receive the logged messages. After you create the IADsTools object and enabled logging, you're ready to get into the meat of the DLL and begin to query and manipulate AD.

My 5 Most Frequently Used IADsTools Functions
IADsTools includes more than 150 functions that range from general to specific. For example, the more general functions let you obtain the IP address of a server or generate a Net Send message to a computer or user. The more specific functions let you obtain detailed information about AD replication connection objects and site topology. I can't cover every function in this article, so I decided to focus on five specific functions that I use most frequently and find most useful in my day-to-day administration of AD environments. (For an overview of IADsTools's general components, see "Practical Usage of ADSI: Using IADsTools' General Functions," February 2002, http://www.winscriptingsolutions.com, InstantDoc ID 23600.)

GetPDCFSMO(). I'll start by describing IADsTools's simple functions. For example, have you ever needed to ascertain which domain controllers (DCs) in your AD environment are serving the Flexible Single-Master Operation (FSMO) roles? You can gather information about the FSMO roles interactively through the Microsoft Management Console (MMC) Active Directory Users and Computers snap-in, but certain circumstances might require that you obtain this information programmatically. IADsTools has five functions, each corresponding to one of the five FSMO roles, to help you determine which DC is doing which job. The functions are GetDomainNamingFSMO(), GetPDCFSMO(), GetInfrastructureFSMO(), GetRidPoolFSMO(), and GetSchemaFSMO(). The script in Listing 1 queries the AD infrastructure to identify which DC in the AD forest is serving the PDC FSMO role. Be aware that by default, the script uses the AD credentials of the user who executes the script, unless you provide an optional parameter within the function call that specifies that you want to use different credentials. These credentials must then be specified in a separate function, which I describe below.

The interesting part of the script, which the code at callout A in Listing 1 shows, calls the GetPDCFSMO() function and uses the Wscript.echo command to return the result to the screen. The script passes two parameters to the function: the name of the DC I want to run the command against (e.g., Yquem) and the name of the AD domain to query (e.g., mar-elia.com)—either a DNS or Lightweight Directory Access Protocol (LDAP) name will work. Of course, for forestwide FSMO roles (e.g., the schema role holder), you can simply point the function to any DC and any domain in the forest that you can access with the default credentials or alternate credentials that you specify.

Changing the credentials that you use to connect to the DC or domain isn't at all straightforward. Instead of passing the alternate credentials as parameters to the GetPDCFSMO() function, as you might expect, you must call a separate IADsTools function, SetUserCredentials(), to set alternative credentials before you call the FSMO function. SetUserCredentials() is a general function that you can use with other IADsTools functions to pass alternate credentials with a request. To see how SetUserCredentials works, let's alter the script in Listing 1 to use different credentials to connect to my AD infrastructure.

SetUserCredentials(). Note that the code at callout A in Listing 2 calls the SetUserCredentials() function and passes in the downlevel Windows NT 4.0 username and domain as well as the AD LDAP distinguished name (DN). The final parameter, "secret", is the user's password, which you can add by using a simple VBScript input box rather than hard-coding the password in clear text, as I've done. Note that this call requires all of this NT 4.0 and AD information—you can't eliminate any of the four required parameters when you use this function. After accepting the alternate credentials, the code at callout B in Listing 2 calls the GetPDCFSMO() function again, but this time passes in the optional parameter of 1 to indicate that I want to use the credentials I retrieved at callout A in Listing 2. These alternate credentials remain in effect for the duration of the script, or until the script calls the ClearUserCredentials() function.

DsGetSiteName(). Another IADsTools function, DsGetSiteName(), can be useful for troubleshooting problems with authentication, Microsoft Dfs, or other Win2K processes that rely on site affinity to determine behavior. You can run this function against a workstation or server (local or remote) to identify which AD site the computer believes it belongs to. You invoke DsGetSiteName() by passing in the name of the target server or workstation for which you want to gather site information (remember that Win2K stores the site membership information on the machine itself, not in AD).

The script in Listing 3 spices up things a bit by using VBScript's inputbox function to ask the user to enter the name of the computer that he or she wants to query, as the code at callout A in Listing 3 shows. After the script collects the input from the user, it uses Wscript.echo and some formatted text, as the code at callout B in Listing 3 shows, to pass a result to the screen. The UCASE() function converts the computer's name to uppercase, and the vbCRLF constant adds a carriage return and line feed to clean up the screen output. Finally, the script calls the DsGetSiteName() function, passing the input target name as a parameter. Notice that the code at callout B uses the cstr() VBScript function to convert the variable strTargetComputer to a proper string that the DsGetSiteName() function can handle. Figure 2 shows sample output after running the script in Listing 3.

GetGCList(). Another useful, if slightly more complex, IADsTools function is GetGCList(), which returns a list of all Global Catalogs (GCs) in a forest. The trick to using this function is determining how to get the list of servers. The code in Listing 4, page 110, shows you how to accomplish this task.

The code at callout A in Listing 4 invokes the GetGCList() function against a DC in an AD forest. GetGCList() takes as input the name of the DC against which the function should run and returns two items. First, the function returns a value that corresponds to the number of GCs that the function found in the forest. The function stores that value in the NumberofGCs variable. If the value is -1, the function found no GCs, which usually signifies a problem either connecting to the DC or properly enumerating the available GC servers. If the NumberofGCs variable is set to -1, the code at callout B in Listing 4 sends a message to the screen informing the user of the error, ends the script, and returns a non-0 value (1). The second item that the GetGCList() function returns is an enumeration that lists the names of the GCs found. The script in Listing 4 doesn't display this enumeration immediately; however, if you review the IADsTools documentation, you discover that the GetGCList() function returns a property on the IADsTools object called GCName, which is an array of GC server names that the GetGCList() function found. Using a simple For...Next loop in the code at callout C in Listing 4, the script enumerates each GC server name, up to the number found, and displays the output onscreen. With this type of WSH script, I recommend using the command-line cscript.exe engine rather than wscript.exe to run the script. Otherwise, if you use wscript.exe, each found server name will appear in a separate pop-up dialog box. Alternatively, you can use VBScript's FileSystemObject to store the results in a text file for later manipulation.

GPO-related functions. This set of functions lets you obtain information about GPOs within your AD domains. For the most part, you can't use scripts to create or edit GPOs in Win2K. This limitation will likely change in the near future, but until then, you can use a few scripting tools to obtain information about GPOs. IADsTools.dll provides several good ones. The code in Listing 5 uses several GPO-related functions to query all GPOs within a domain and, based on user input, compare the version number of a Group Policy Container (GPC—the portion of the GPO stored in AD) with the version number of a Group Policy Template (GPT—the portion of the GPO stored in SYSVOL). To be processed by the client, a GPO's GPC and GPT version numbers need to be the same on a given DC for that GPO. Thus, the script can help you see discrepancies on the DC that the script runs against.

The code at callout A in Listing 5 sets two variables that control which AD domain and DC the script will run against. You can just as easily pass those variables into the script as command-line parameters, but I chose to hard-code them instead. The code at callout B in Listing 5 asks the user to specify the friendly name of the GPO the user wants to query. The friendly name is simply the text name that the GPO is known by within the Group Policy editor tool (not the 128-bit globally unique identifier—GUID—name that the GPO is known by in AD). For example, "Default Domain Policy" is the friendly name for the GPO that the GUID \{31B2F340-016D-11D2-945F-00C04FB984F9\} represents. The second statement in callout B calls the GetGPOs() function, which returns an enumeration of all GPOs found in the domain on the target DC. Note that a GPO's version number can vary from DC to DC; therefore, if you really want the script to do its job, you might need to run it against several DCs.

The first line of code at callout C in Listing 5 creates a For...Next loop that checks each GPO found to determine whether the GPO matches the name of the GPO that the user entered. The next line of code at callout C compares the friendly name of each GPO found by the GetGPOs() function with the friendly GPO name that the user entered. The VBScript LCase() function makes both names lowercase to prevent any mismatches because of case differences. The GPOName() function contains the friendly name of each GPO returned from the code at callout B. You could use the GPOGuid() function to obtain the GPO's GUID from its friendly name. The code at callout D in Listing 5 reports on the results of the search.

After the script finds the correct GPO, it uses the GPOVersion() and GPOSysvolVersion() functions to get the GPO's GPC and GPT versions. Finally, the If...Then...Else loop in the code at callout E in Listing 5 compares the GPC and GPT versions and reports on whether they're identical (in sync) or mismatched (out of sync).

Use cscript.exe to run this script and consider capturing the script output to a file for later analysis. To check the version numbers for all GPOs on a given DC, just delete the line of code at callout B that asks the user for input—the script will then compare versions for every GPO that the GetGPOs() function finds.

Good Stuff Abounds
I've only scratched the surface of what you can do with IADsTools. Numerous other functions exist for obtaining AD-replication metadata information, finding out which subnets are part of which sites, converting LDAP names to DNS names, and more. I encourage you to sit down with the 72 pages of IADsTools documentation and find some new capabilities of your own to exploit to solve some thorny problem in your AD environment. Happy hunting!