?
Solved

Get all AD users (ADODB versus DirectorySearcher) performance issue.

Posted on 2008-02-05
7
Medium Priority
?
1,342 Views
Last Modified: 2013-11-26
I have 2 serprate functions, One uses an ADODB connection to Active directory and the other is using a DirectorySearcher object to get a "list" of all the user records in our domain.

The intent of this code it to get all our users telephone information from the Cisco Call Manager (LDAP) and import it into Active Directory (Win 2003).

The older method using ADODB is much quicker than the DirectorySearcher method, and the application rusn fine with it enabled. I would like to use the DirectorySearcher method as it doesnt require me to distribute any extra dll's with my application.

What is wrong with this and how could it be optimized to run more efficiently?

We have 2000+ users in AD. The ADODB method takes aprox. 10 sec to populate the array, while the DirectorySearcher method takes over 60sec.


Imports System
Imports System.IO
Imports System.DirectoryServices
Imports System.DirectoryServices.Protocols
Imports System.Net
Imports System.Text
Imports System.Runtime.InteropServices.COMException
Imports ADODB 'added via reference: C:\Program Files\Microsoft.NET\Primary Interop Assemblies\adodb.dll
 
Module modMain
 
    Sub Main()
        getADUsers_ADODB() '~10sec
        getADUsers_NEW() 'long time
    End Main
 
    Function getADUsers_ADODB()
        Dim arrADUsers As New ArrayList
 
        Try
            Dim oConn As New ADODB.Connection
            Dim oRS As New ADODB.Recordset
            Dim oComm As New ADODB.Command
            'Dim strComputerName As String = Nothing
            Dim strDomainName As String = "saws.org"
 
            ' set connection properties
            With oConn
                .Provider = "ADsDSOObject"
                .Open("Active Directory Provider")
            End With
 
            ' set command properties
            With oComm
                .ActiveConnection = oConn
                .CommandText = "<LDAP://DC=domain,DC=org>;(&(ObjectClass=user)(!ObjectClass=computer)(!description=Built-in*));sAMAccountName,employeeID,telephoneNumber,mail,distinguishedName"
            End With
 
            ' open recordset
            oRS = oComm.Execute
 
            If Not oRS.EOF Then
                oRS.MoveFirst()
                Do Until oRS.EOF
                    Dim arrADUser As New ArrayList
                    If Not oRS.Fields(0).Value.ToString = "" Then
 
                        arrADUser.Add(oRS.Fields(0).Value)
 
                        If Not oRS.Fields(1).Value.ToString = "" Then
                            arrADUser.Add(oRS.Fields(1).Value)
                        Else
                            arrADUser.Add("null")
                        End If
 
                        If Not oRS.Fields(2).Value.ToString = "" Then
                            arrADUser.Add(oRS.Fields(2).Value)
                        Else
                            arrADUser.Add("null")
                        End If
 
                        If Not oRS.Fields(3).Value.ToString = "" Then
                            arrADUser.Add(oRS.Fields(3).Value)
                        Else
                            arrADUser.Add("null")
                        End If
 
                        If Not oRS.Fields(4).Value.ToString = "" Then
                            arrADUser.Add(oRS.Fields(4).Value)
                        Else
                            arrADUser.Add("null")
                        End If
 
                    End If
                    arrADUsers.Add(arrADUser)
                    oRS.MoveNext()
                Loop
            End If
 
            ' clean up objects
            oRS.Close()
            oConn.Close()
            oRS = Nothing
            oComm = Nothing
            oConn = Nothing
 
 
        Catch ex As Exception
            Console.WriteLine()
            Console.WriteLine(ex.Message)
            Console.WriteLine()
            Console.WriteLine(ex.ToString)
        End Try
 
        Return arrADUsers
 
    End Function
 
 Function getADUsers_NEW()
        Dim arrADUsers As New ArrayList
 
        Try
            Dim de As DirectoryEntry = getDirectoryEntry()
            Dim ds As New DirectorySearcher(de)
 
            Dim filter As New StringBuilder
            filter.Append("(&(objectCategory=Person)(objectClass=user))")
 
            ds.Filter = filter.ToString
            ds.SearchScope = DirectoryServices.SearchScope.Subtree
            Dim results As SearchResultCollection = ds.FindAll
 
            Console.WriteLine("User COUNT: {0}", results.Count)
            Console.WriteLine()
 
            For Each result As SearchResult In results
                Dim dey As DirectoryEntry = getDirectoryEntry(result.Path)
                Console.WriteLine("User  : {0}", dey.Properties("sAMAccountName").Value)
                Console.WriteLine("Phone : {0}", dey.Properties("telephoneNumber").Value)
                Console.WriteLine()
                dey.Close()
            Next
 
            de.Close()
 
        Catch ex As Exception
            Console.WriteLine()
            Console.WriteLine(ex.Message)
            Console.WriteLine()
            Console.WriteLine(ex.ToString)
        End Try
 
        Return arrADUsers
 
    End Function
 
    Function getDirectoryEntry(Optional ByVal argPath As String = Nothing) As DirectoryEntry
        Dim de As New DirectoryEntry
        If argPath Is Nothing Then
            de.Path = "LDAP://DC=domain,DC=org"
        Else
            de.Path = argPath
        End If
        de.Username = "domain\username"
        de.Password = "password"
        Return de
    End Function
 
End Module

Open in new window

0
Comment
Question by:tobinmarch
  • 5
  • 2
7 Comments
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20831206

I suspect it's because of this:

                Dim dey As DirectoryEntry = getDirectoryEntry(result.Path)

I'd have to test it to be sure, but that looks to me exactly like you're connecting to the user account then retrieving values, whereas in the ADO query you're just accessing an existing record set.

Chris
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20831254

Okay, I've checked and I am as sure as I can be.

I expect you'll find that this bit below is very fast:

            Dim results As SearchResultCollection = ds.FindAll

I know I did when I created a C# version.

However, in mine I'm using FindAll to find a small collection of results (5 to 10 at the most) so the overhead is much lower on retrieving the other attributes when I'm returning and handling the results.

Moving onto the next post while I play with the "make it faster" bit :)

Chris
0
 
LVL 71

Accepted Solution

by:
Chris Dent earned 1200 total points
ID: 20831298

Theoretically that gives us something like this. The syntax in C# is slightly different, so please check I have the conversion to Vb.NET right, programming isn't really my thing.

We avoid the User connection, which takes us back to match the ADO query.

Chris

Function getADUsers_NEW()
	Dim arrADUsers As New ArrayList
 
	Try
		Dim de As DirectoryEntry = getDirectoryEntry()
		Dim ds As New DirectorySearcher(de)
 
		Dim filter As New StringBuilder
		filter.Append("(&(objectCategory=Person)(objectClass=user))")
 
		ds.Filter = filter.ToString
		ds.SearchScope = DirectoryServices.SearchScope.Subtree
 
		ds.PropertiesToLoad.Add("sAMAccountName")
		ds.PropertiesToLoad.Add("telephoneNumber")
 
		Dim results As SearchResultCollection = ds.FindAll
 
		Console.WriteLine("User COUNT: {0}", results.Count)
		Console.WriteLine()
 
		For Each result As SearchResult In results
			Console.WriteLine("User  : {0}", SearchResult.Properties("sAMAccountName").Value)
			Console.WriteLine("Phone : {0}", SearchResult.Properties("telephoneNumber").Value)
			Console.WriteLine()
		Next
 
		de.Close()
 
		Catch ex As Exception
			Console.WriteLine()
			Console.WriteLine(ex.Message)
			Console.WriteLine()
			Console.WriteLine(ex.ToString)
	End Try
 
	Return arrADUsers
End Function

Open in new window

0
Making Bulk Changes to Active Directory

Watch this video to see how easy it is to make mass changes to Active Directory from an external text file without using complicated scripts.

 

Author Comment

by:tobinmarch
ID: 20833299
I have tested the above response from Chris-Dent to my post and you are correct this is is much faster.

Before I accept your response answer me this:

By adding these lines;

ds.PropertiesToLoad.Add("sAMAccountName")
ds.PropertiesToLoad.Add("telephoneNumber")

are you specifying that these are the only LDAP properties that the SearchResultCollection should include?
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20833630

The search results collection, as far as I know, doesn't include any explicit values at all by default.

With the PropertiesToLoad specified it will grab those values as well (or populate a null field), it works in much the same way as you must define the fields to retrieve using the ADO query.

You should still find that you can use .Path to access the distinguishedName of the object without further work (if you need to access the object as a DirectoryEntry).

Chris
0
 

Author Comment

by:tobinmarch
ID: 20835048
Thanks for that info. I do need the DN of the entry for further processing.

Appreciate all your help.
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20835364

Glad I could help :)

Chris
0

Featured Post

Free tool for managing users' photos in Office 365

Easily upload multiple users’ photos to Office 365. Manage them with an intuitive GUI and use handy built-in cropping and resizing options. Link photos with users based on Azure AD attributes. Free tool!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Active Directory can easily get cluttered with unused service, user and computer accounts. In this article, I will show you the way I like to implement ADCleanup..
A bad practice commonly found during an account life cycle is to set its password to an initial, insecure password. The Password Reset Tool was developed to make the password reset process easier and more secure.
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…
Suggested Courses
Course of the Month4 days, 16 hours left to enroll

601 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question