If you want to use ADO to perform a Lightweight Directory Access Protocol (LDAP) search, you need to specify four arguments: search base, filter, attributes, and scope. Although specifying the search base, attributes, and scope arguments is straightfoward (see Alistair G. Lowe-Norris, "An ADSI Primer, Part 5: Using ADO to Script Fast ADSI Searches," page 1), specifying the filter argument isn't.
The filter argument defines which objects match the query you want to perform. Because you can use a filter on any property of any object, you can conduct powerful searches. For example, you can search for any object that has a description matching a text substring you specify. (You can't match long integers and other nontext data types.)
Although filters let you conduct powerful searches, working with filters can seem complex because they must follow the format that Request for Comments (RFC) 1960 dictates. For example, the format dictates that filters are case-sensitive, that you must not have any spaces within a string in a filter, that you must enclose each filter in parentheses, and that you must use semicolons with no spaces before or after to separate filters. You can download the text of RFC 1960 from ftp://ftp.isi.edu/in-notes/rfc1960.txt.
To help you understand this complexity, I altered the formatting in the following examples to make the filters more readable. Please keep in mind that to make these filters work in a script, you need to eliminate the line breaks and spaces that I included for clarity. I also divided the discussion into two parts: items within a filter and items connecting filters.
Items Within a Filter
You can have three types of items within a filter. The three types are operators, attributes, and substrings.
Operators. A filter can include one of four operators. The equal to (=) operator checks for exact equivalence. An example is (name=vicky). The approximately equal to (~=) operator checks for approximate equivalence. An example is (size~=10). The greater-than-or-equal-to (>=) and less-than-or-equal-to (<=) operators check for compliance with a range. Examples are (size>=5) and (size<=20).
Attributes. You can include attributes in filters when you want to determine whether an attribute exists. You simply specify the attribute, followed by the = operator and an asterisk (*). For example, the (mooseHerderProperty=*) filter searches for objects that have the moose-herder attribute.
Substrings. You can include substrings in filters when you want to search for objects with specific strings. You test for substrings by placing the attribute type (e.g., cn for common name, sn for surname) to the left of the = operator and the substring you're searching for to the right. You use the * character to specify where that substring occurs in the string. For example, the (cn=Keith*) filter searches for common name (CN) strings that begin with the substring Keith and the (cn=*Cooper) filter searches for CN strings that end with the substring Cooper.
You can place several substrings together, using the * character several times. For example, the (cn=Kei*Coo*) filter searches for two substrings in the string: The first substring begins with Kei, followed by the second substring that begins with Coo. Similarly, the (cn=*ith*per) filter searches for strings that have two substrings: the first substring ends in ith, followed by the second substring that ends in per.
The result set of a substring search might contain objects that you don't want. For example, if you use the filter (cn=Kei*Coo*) to search for the object representing Keith Cooper, your result set might contain two objects: one representing Keith Cooper and another representing Keith Coolidge.
Items Connecting Filters
Now that you know what can go inside a filter, you can combine filters to perform more powerful searches. You can use the ampersand (&), the vertical bar (|), and the exclamation mark (!) to combine filters.
Let's start by creating a filter to find all groups whose common name begins with the letter a. The filter for this search is
This filter actually consists of two filters: (objectClass=Group) and (cn=a*), but because you're enclosing the filters in parentheses, you're treating them as one filter. The & prefix specifies the use of the logical And operator. In other words, you're searching for objects that are in the Group object class and have a CN that begins with the letter a.
You can add additional filters to narrow the search. Suppose that, in groups whose CN begins with the letter a, you want to find only those users whose surname begins with the letter d. To perform this search, you use the filter
You can also widen a search. Instead of using the & operator, you use the | prefix, which specifies the logical Or operator. For example, if you want to find all group or user objects, you use the filter
You can nest sets of filters, as long as each filter conforms to the correct notation. For example, if you want to find all groups whose CN begins with the letter a or whose description begins with the substring Special groups, you use the filter
So far, you've been searching for objects that have a certain characteristic. You can also search for objects that don't have a certain characteristic. You use the ! prefix, which specifies the Not, or negation, operator. For example, you can search for all objects that aren't users with the filter
By combining the &, |, and ! operators, you can perform powerful searches. For example, see whether you can determine what the query in Listing 1 is searching for.
The query in Listing 1 is searching for any container or organizational unit (OU) that doesn't contain the "MyCorpSpecial" property and whose CN contains the letters cor or starts with the letter J. Listing 2 shows how you would include this filter in a script.
This article is adapted from Alistair G. Lowe-Norris' forthcoming book about the Windows 2000 Active Directory (O'Reilly and Associates).