Downloads
4991.zip

Editor's note: This article is the third part of a 12-part series on Active Directory Service Interfaces (ADSI). The series started in the January 1999 issue. Refer to previous installments for definitions and background information.

Each object in a directory has a series of attributes, or properties, that uniquely define it. Although properties can vary from object to object, ADSI has a core set of six IADs properties common to all objects. These properties are common to all objects because IADs is the most basic interface in ADSI. Last month, I briefly introduced you to the IADs properties and property cache. This month, I'll discuss them in detail.

The IADs Properties
The six core IADs properties are Class, GUID, Name, ADsPath, Parent, and Schema. These properties represent the following information for an object:

  • Class represents the object's schema class.
  • GUID represents the object's globally unique ID (GUID).
  • Name represents the object's name.
  • ADsPath represents the ADsPath to the object in the current namespace.
  • Parent represents the ADsPath to the object's parent.
  • Schema represents the ADsPath to the object's schema class.

Each core property has a corresponding property method in the IADs interface. You use the property method, which has the same name as the property, to access that property's value. Accessing properties' values is fundamental in scripting. For example, if you want to add a user (i.e., a User object) to the Sales organizational unit (i.e., an OrganizationalUnit container), you need to access the User object's properties. Listing 1 contains code to enumerate the six core properties of a User object in Windows 2000's (Win2K's) Active Directory (AD). The script first uses the six property methods to enumerate the User object's properties in the WinNT namespace, then uses the six property methods to enumerate the User object's properties in the Lightweight Directory Access Protocol (LDAP) namespace. You might be surprised to find out that enumerating properties in different namespaces produces different output, as Screen 1 shows. For example, the Name property under the LDAP namespace has the CommonName (CN) X.520 key prefix from Request for Comments (RFC) 1779, whereas the Name property under the WinNT namespace doesn't.

Listing 1 and Screen 1 demonstrate another important point: The type of directory can affect the results. For example, using the Parent property makes sense when you're using the LDAP namespace to access a hierarchical directory, such as AD, because you can see parent-child relationships (e.g., you can see that the Users container is the parent for the Administrator User object). However, using the Parent property to look at NT's Security Accounts Manager (SAM) doesn't make sense, because the contents are all in one flat namespace.

Because each core property has a corresponding property method, ADSI refers to these six core property methods as being explicit. Listing 1 shows how you work with explicit property methods.

To begin, you declare two variables (i.e., str and adsUser); apply Visual Basic Script's (VBScript's) GetObject method to create a reference, or pointer, to the User object; and set adsUser to that pointer. You then set the str variable to the string "Name:" and apply the Name property method (adsUser.Name) to retrieve the Name property's value (i.e., Administrator). The carriage-return line-feed character (vbCrLf) specifies a hard return. At this point, str represents the string Name: Administrator.

In the next line, you use the GUID property method (adsUser.GUID) to retrieve the GUID property's value (i.e., \{D83F1060-1E71-11CF-B1F3-02608C9E7553\}). The str = str & portion of the line sets str to a new string that now represents the Name property value and GUID property value. You continue this process until you retrieve all six core properties in both the WinNT and the LDAP namespaces.

Unlike the six core properties, not all object properties have explicit property methods that you can use to retrieve properties. However, you can implicitly access an object's property by treating the property as the method in automation languages, such as VBScript, JScript, and Visual Basic (VB). In nonautomation languages, such as Visual C++ (VC++), you must use the IADs Get method for any property that doesn't have an explicit property method. You need to check the ADSI documentation to determine whether property methods exist. (For more information about where you can find the ADSI documentation, see "An ADSI Primer, Part 1," January 1999.)

For example, suppose you want to retrieve the mail property for the Group object Managers. The mail property is the list of e-contact mail addresses for that object. The mail property doesn't have an explicit property method, but you can treat the property as the method, as Listing 2 shows. After you set the adsGroup variable to the pointer to the Managers Group object, you use mail as the method (i.e., adsGroup.mail). The Windows Scripting Host (WSH) Echo method displays the mail property's value in a window.

In the next line, you once again use the mail property as the method to change the mail property's value to agl1@le.ac.uk. This action also returns the value to the property cache. To write the new value to AD, you use the SetInfo method.

An Alternative Approach
You don't have to use an implicit or explicit property method to access an object's properties. As Table 1 shows, you can use the IADs interface's Get and Put methods instead. For example, Listing 3 shows how you use Get and Put to retrieve, change, and return the mail property. After you set the adsGroup variable to the pointer to the Managers group, you use the Get method (adsGroup.Get) with the "mail" argument to retrieve the mail property's value. The Echo method displays the results in a window.

Changing the value and returning it to the property cache is just as simple. You use the Put method with the argument "mail"; however, you don't put the argument in parentheses when you use the Put method. The string that follows the Put function contains the Managers group's new mail contact address. To write the new mail property to AD, you use SetInfo.

The Property Cache
Having looked at the properties and property methods, let's take a look at the property cache. This cache is an area of memory that stores properties for objects. Each object that you bind to has a personal property cache; the OS creates this cache the instant the bind succeeds. However, the OS doesn't immediately populate the cache with values.

When you use the Get method to retrieve an object's property, ADSI doesn't go to the AD to retrieve the value. Instead, ADSI reads the value from the property cache on the client executing the script. If ADSI doesn't find the property in the property cache when the call comes in, the system implicitly executes a GetInfo call to read all the properties for the current object into the cache. (You can also explicitly use the GetInfo method to populate the property cache with an object's properties.) The Get method then reads the appropriate value from that newly created cache.

Microsoft designed the property cache with efficiency in mind. The property cache lets you access an object's properties with a minimum number of calls, thereby minimizing network traffic. Retrieving all an object's properties with one GetInfo call is more efficient than individually retrieving each property. Similarly, the process of writing all an object's properties first to the cache and then to the AD with one SetInfo call is more efficient than writing each property individually to the AD.

Be Careful
The GetInfo and SetInfo methods are two of the most important methods you'll use. However, you need to be aware of two possible problems.

The first problem can arise if you try to access a property that doesn't have a value. When you initially create an object, all its properties might not have values. For example, when you create a Group object, the mail property doesn't automatically receive a value; you must provide a value, such as agl1@le.ac.uk. When you use the GetInfo method, only those properties that have values appear in the property cache. Thus, if you don't give the mail property a value and you use GetInfo, the mail property value won't be in the property cache. If you try to access a property that doesn't exist in the cache, the script will give an empty value as the result.

Another problem can arise if you forget to use SetInfo after modifying a property. For example, suppose you want to change the Managers group's mail property value from My group mail to agl1@le.ac.uk, so you create the script in Listing 4 on page 10. In this script, you set the adsGroup variable to the pointer to the Managers group. To display the current mail property value in a window, you use the Echo method with the IADsGroup's mail property method, which forces an implicit GetInfo call. You then set the new value for the adsGroup's mail property, after which you use an explicit GetInfo call to again retrieve all the object's properties into the cache. Finally, you use the Echo method to display the results in a window.

When you run the script, two windows pop up. To your dismay, both windows state My group mail, which means that the system didn't write the new mail address to the AD. This cache write didn't occur because you need to explicitly call the SetInfo method to write out data from the cache to the AD. To fix the script in Listing 4, you need to insert the line

objGroup.SetInfo

between the line setting the new mail address and the line making the explicit GetInfo call.

More Complexities of Property Access
Using the IADs interface's Get method works well for properties with one value. However, some properties have multiple values, such as several telephone numbers for a user. If a property stores multiple values, you need to use the IADs interface's GetEx and PutEx methods to retrieve and return the values. (You can also use GetEx for single-value properties.)

GetEx. Listing 5 shows an example of how to use GetEx. In this script, you pass the multiple-value property as an argument to the GetEx method. You then use a For Each...Next loop on the resulting list.

When you make the GetEx call in Listing 5, the system makes an implicit GetInfoEx call rather than an implicit GetInfo call to AD. You can use an explicit GetInfoEx call to get one or more properties if you don't want to use GetInfo to get all the property values. However, few scriptwriters use GetInfoEx for this purpose, because they typically use implicit calls or use GetInfo to read all values into the property cache. In addition, if you use GetEx for every property retrieval rather than using GetInfo, your underlying network traffic will increase. Instead of sending one request to the server for all the information, you'll be sending several requests for smaller amounts of information.

Although GetInfoEx isn't a good substitute for GetInfo, GetInfoEx works well for selectively reading properties into the property cache. Listing 6 shows an example of how to selectively retrieve only two properties. After you set the adsUser variable, you create an array containing the properties you want (i.e., CN and ADsPath). Next, you pass that array to the GetInfoEx method as the first parameter. In ADSI 2.5 beta, the second parameter must be 0 for all actions. However, this situation might change in a later version of ADSI. Then, the last line uses the VBScript MsgBox function to print the CN and ADsPath attributes, separating them with a tab.

MsgBox and WScript.Echo are interchangeable for WSH scripts you execute using wscript.exe. When you use cscript.exe, WScript.Echo writes to the command prompt but MsgBox still presents a dialog box, as before. If you run your scripts from the command prompt, don't use MsgBox unless you want someone to interactively press OK after every item.

PutEx. To return multiple-value properties, you use the PutEx method. Using PutEx is slightly more complicated than using GetEx. Suppose a property already has three values (e.g., pager numbers) and you want to put in two more. You must let PutEx know whether it needs to overwrite, update, or add to the existing values. You use the constants in Table 2 to tell PutEx what to do. You use the constant name only if you're using VB. If you use WSH with VBScript, you must either define the constants, as I've done in Listing 7, or use the values directly.

The four values are fairly straightforward to use, as the code in Listing 7 shows. After you bind to the user object, you set three pager numbers for the administrator account, wiping out any existing values. You then reload the property cache explicitly to make sure it contains the new values that you just set. Now, you use a For Each...Next loop to go through the newly set property to show the individual pager numbers. You then delete the first and last pager numbers of the new property in the cache and write that cache out to the AD.

At this point, the AD should contain only one pager number, which you verify by looping through the values again. You decide to append a number to the value held for that property in the cache and subsequently write it out to the AD, leaving two numbers in the AD for that property. Looping through the values again shows there are two numbers. Finally, you delete all the values in the property cache for that property and write the changes out to the AD. Using the For Each...Next loop one last time shows no values.

Next Month
You now know how to access an object's properties from the property cache. Next month, I'll show you how to count the number of properties, display their names and values, and perform other procedures with the IADsPropertyList, IADsPropertyEntry, and IADsPropertyValue interfaces.

This article is adapted from Alistair G. Lowe-Norris' forthcoming book about the Windows 2000 Active Directory to be published by O'Reilly and Associates.