Become a Columnist Microsoft Exchange Site Microsoft Support SiteMSDN Exchange Site

   

Subscribe to OutlookExchange
Anderson Patricio
Ann Mc Donough
Bob Spurzem
Brian Veal
Catherine Creary
Cherry Beado
Colin Janssen
Collins Timothy Mutesaria
Drew Nicholson
Fred Volking
Glen Scales
Goran Husman
Guy Thomas
Henrik Walther
Jason Sherry
Jayme Bowers
John Young
Joyce Tang
Justin Braun
Konstantin Zheludev
Kristina Waters
Kuang Zhang
Mahmoud Magdy
Martin Tuip
Michael Dong
Michele Deo
Mitch Tulloch
Nicolas Blank
Pavel Nagaev
Ragnar Harper
Ricardo Silva
Richard Wakeman
Russ Iuliano
Santhosh Hanumanthappa
Shannal L. Thomas
Steve Bryant
Steve Craig
Todd Walker
Tracey J. Rosenblath
 
   

Searching the AD

Page 1 | Page 2

Overview

In this article, I will show you how you can use VBScript and ADO to query the Active Directory to find a single or multiple objects.  The search methods outlined here are used in almost every script and Visual Basic application that queries the AD for objects.  Higher-level languages, like C++, normally use the IdirectorySearch interface, which is not available to lower level languages and scripting interfaces.  The search methods shown here will make it very easy to find and work with objects in the AD no matter which OU they maybe located in.

The Problem

In NT you could easily access an object with ADSI using the WinNT provider by doing Set objUser = GetObject("WinNT://altered/sherrja").  However, you are only able to get at the NT properties when using the WinNT provider.  In order to get at the ?AD only? properties you must used the LDAP provider.  Since objects are no longer stored in one container in the AD you need the full path to the object before you can bind to it with LDAP.  Therefore, you need someway to query the AD with a known property, or properties, and get the path to an object so you can get additional properties of that object and\or make changes to that object.

The Solution

In order to get at objects from the AD we are going to use ADO (ActiveX Data Objects).  ADO allows us to execute SQL like statements, SELECT * FROM *, to query the AD for objects and properties.

The first thing we need to do is open a connection to the AD using ADO.  This is done with the lines below where the first one creates a connection to the AD via ADO, and then the provider is set to ADsDSOObject, which is the OLE DB provider in ADSI.  Last, the connection is opened.

Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"

Next, we need to create a Command object that is used to issue queries to the AD via the ADO connection created above.  We then pass the Command are current connection to ADO that was created above.

Set objCommand = CreateObject("ADODB.Command")
Set objCommand.ActiveConnection = objConnection

Now that we have the Command object created and open we can pass it the search string of the objects we are looking for.  The search string supports queries in a SQL dialect format.  In the example below, we are looking for a user objects that have a department set to Sales.  The SELECT AdsPath,cn tells the AD to return the AdsPath and cn fields of the objects found.  Next FROM 'LDAP://DC=corp,DC=com? set where the search should be carried out, in this case the root of the CORP domain will be the starting point for the search.  Last we use the WHERE statement to set the two fields we will be searching on, objectClass and department.

objCommand.CommandText = ?SELECT AdsPath,cn FROM 'LDAP://DC=corp,DC=com' WHERE objectClass = 'User' AND department = 'Sales'?

Besides the search string, we can pass other properties to the Command object that controls how the search is carried out.  Below we set the search scope so all sub OU are queried, since we are starting the search at the root of the AD we need to at least search one level deep.  The 2 (ADS_SCOPE_SUBTREE) indicates that the specified location and all sub OUs should be searched.  Next, we set the maximum number of records to return each time from the AD.  This will limit the results to 1,000 rows of data at a time, once we process the last row AD will return the next 1,000 records.  Last, we set how long the query should wait before returning the results that have been collected so far.  All three of these settings are optional, the default is to search all sub OUs, return only the first 1,000 rows of data, and wait until the page size has been filled or all values have been returned.

objCommand.Properties("searchscope") = 2
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Timeout") = 15

Now that we have the query and all the properties set, we now need to execute the query.  This is done with the Execute method.  The results, 1000 rows at a time, are then returned to the objRecordSet variable.

Set objRecordSet = objCommand.Execute

Therefore, we now have the results from our query let us look at what was retuned.  First, we use the MoveFirst method to get the first record.  If there was no data returned this first line will return an error.  Assuming we do have additional records we then start a loop that will continue until the last record has been read in.  Inside the loop we retrieve the AdsPath and cn fields from the current record; the AdsPath contains the fully qualified LDAP path to the object.  Then with the objPath set, we then use GetObject to retrieve the object from the AD so we can get at other properties of the object.  On the next line, we display the samAcccountName and cn attribute of that object in a pop-up message.  Last, we use the MoveNext method to retrieve the next record.

objRecordSet.MoveFirst
Do Until objRecordSet.EOF
  AdsPath = objRecordSet.Fields("AdsPath")
  cn = objRecordSet.Fields("cn")
  Set objObject = GetObject(AdsPath)
  MsgBox ?Logon name: ? & objObject.samAccountName & ? for ? & cn
  objRecordSet.MoveNext
Loop

Putting it all together

Below is the entire code covered above that is used to find all users with ?Sales? in their department field.  See the include script for a more flexible way for searching for object by using the code below as a function.  This way you can carry easily out a search whenever you need to find all objects with certain attributes.

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand =   CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.CommandText = "SELECT AdsPath,cn FROM 'LDAP://DC=corp,DC=com' WHERE objectClass = 'User' AND department = 'Sales'"
objCommand.Properties("searchscope") = 2
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Timeout") = 15
objCommand.Properties("Cache Results") = False
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
  objPath = objRecordSet.Fields("AdsPath")
  Set objObject = GetObject(objPath)
  MsgBox "Logon name: " & objObject.samAccountName & " for " & cn
  objRecordSet.MoveNext
Loop

Extra features in the included script

The included script includes two search methods, one for returning a single record and the other for returning multiple records similar to the steps outlined above.  In addition, the method for returning multiple records is written as a function so other parts of your code can call it and get all values that match the search fields you pass it.  The single search method is also a function and it will return the ADsPath of the first object that matches the search criteria given.  Both of these functions are very useful when working with objects in the AD.

 

Searching the AD

Page 1 | Page 2

Disclaimer: Your use of the information contained in these pages is at your sole risk. All information on these pages is provided "as is", without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by Stephen Bryant or Pro Exchange. OutlookExchange.Com, Stephen Bryant and Pro Exchange shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.

Copyright Stephen Bryant 2008