It is also possible to sort and filter the ADO recordset that results from a query of Active Directory. However, the recordset must be disconnected. In addition, you must specify the proper cursor type and location. This is best demonstrated by examples. First, here is an example using the Sort method of the recordset object:

Option Explicit
Dim objRootDSE, strDNSDomain, adoConnection
Dim strBase, strFilter, strAttributes, strQuery, adoRecordset
Dim strLast, strFirst, intCount, strName

Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adUseClient = 3

' Determine DNS domain name.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory.
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"

Set adoRecordset = CreateObject("ADODB.Recordset")
Set adoRecordset.ActiveConnection = adoConnection
adoRecordset.CursorLocation = adUseClient
adoRecordset.CursorType = adOpenStatic
adoRecordset.LockType = adLockOptimistic

' Search entire domain.
strBase = "<LDAP://" & strDNSDomain & ">"

' Filter on all user objects.
strFilter = "(&(objectCategory=person)(objectClass=user))"

' Comma delimited list of attribute values to retrieve.
strAttributes = "sAMAccountName,sn,givenName"

' Construct the LDAP query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

' Run the query.
adoRecordset.Source = strQuery
adoRecordset.Open

' Disconnect the recordset.
Set adoRecordset.ActiveConnection = Nothing
adoConnection.Close

' Sort the recordset.
adoRecordset.Sort = "sn,givenName"
adoRecordset.MoveFirst

' Enumerate the resulting recordset.
intCount = 0
Do Until adoRecordset.EOF
    ' Retrieve values.
    strLast = adoRecordset.Fields("sn").Value
    strFirst = adoRecordset.Fields("givenName").Value
    strName = adoRecordset.Fields("sAMAccountName").Value
    Wscript.Echo strLast & ";" & strFirst & ";" & strName
    intCount = intCount + 1
    adoRecordset.MoveNext
Loop

' Clean up.
adoRecordset.Close

Wscript.Echo "Number of objects: " & CStr(intCount)

The next example demonstrates how to use the Filter method of the recordset object. Again, the recordset must be disconnected and you must specify the cursor type and location. You can filter on any single valued field in the recordset. For example:

Option Explicit
Dim objRootDSE, strDNSDomain, adoConnection
Dim strBase, strFilter, strAttributes, strQuery, adoRecordset
Dim intCount

Const adOpenStatic = 3
Const adLockOptimistic = 3
Const adUseClient = 3

' Determine DNS domain name.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory.
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"

Set adoRecordset = CreateObject("ADODB.Recordset")
Set adoRecordset.ActiveConnection = adoConnection
adoRecordset.CursorLocation = adUseClient
adoRecordset.CursorType = adOpenStatic
adoRecordset.LockType = adLockOptimistic

' Search entire domain.
strBase = "<LDAP://" & strDNSDomain & ">"

' Filter on all objects regardless of category.
' We will later filter the recordset on various object categories.
strFilter = "(objectCategory=*)"

' Comma delimited list of attribute values to retrieve. Any attributes
' (fields) we later wish to filter on must be included in the list.
strAttributes = "distinguishedName,sAMAccountName,objectCategory,memberOf"

' Construct the LDAP query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

' Run the query.
adoRecordset.Source = strQuery
adoRecordset.Open

' Disconnect the recordset.
Set adoRecordset.ActiveConnection = Nothing
adoConnection.Close

' Filter the recordset on objects of category person.
adoRecordset.Filter = "objectCategory='cn=Person," _
    & "cn=Schema,cn=Configuration,dc=MyDomain,dc=com'"
intCount = adoRecordset.RecordCount
Wscript.Echo "Number of persons: " & CStr(intCount)

' Filter the recordset on objects of category computer.
adoRecordset.MoveFirst
adoRecordset.Filter = "objectCategory='cn=Computer," _
    & "cn=Schema,cn=Configuration,dc=MyDomain,dc=com'"
intCount = adoRecordset.RecordCount
Wscript.Echo "Number of computers: " & CStr(intCount)

' Filter the recordset on objects of category group.
adoRecordset.MoveFirst
adoRecordset.Filter = "objectCategory='cn=Group," _
    & "cn=Schema,cn=Configuration,dc=MyDomain,dc=com'"
intCount = adoRecordset.RecordCount
Wscript.Echo "Number of groups: " & CStr(intCount)

' Enumerate the groups.
adoRecordset.MoveFirst
Do Until adoRecordset.EOF
    Wscript.Echo adoRecordset.Fields("sAMAccountName").Value
    adoRecordset.MoveNext
Loop

' Filter the recordset on objects of category OU.
adoRecordset.MoveFirst
adoRecordset.Filter = "objectCategory='cn=Organizational-Unit," _
    & "cn=Schema,cn=Configuration,dc=MyDomain,dc=com'"
intCount = adoRecordset.RecordCount
Wscript.Echo "Number of OU's: " & CStr(intCount)

adoRecordset.Close

Unfortunately, you cannot filter on multi-valued attributes, like memberOf. In the example above we were able to filter on objectCategory because it is a single-valued attribute. We could not filter on objectClass because it is multi-valued. If the following lines of code are added to the example above, an error is raised:

' Filter the recordset on members of Accountants group.
adoRecordset.MoveFirst
adoRecordset.Filter = "memberOf='cn=Accountants,ou=West,dc=MyDomain,dc=com'"
intCount = adoRecordset.RecordCount
Wscript.Echo "Number of members of Accountants group: " & CStr(intCount)