0

I'm hoping to improve the performance of the below as well as return the GUID of each user.

I've converted and modified the code found here for searching through groups recursively to get all the members and their details and adding them to a class collection. However I also need to capture the user's managers details so I'm calling the AD twice for each user. This doesn't seem efficient as many of the users will have the same manager. It would seem logical to get the distinct manager details from the collection class and call only these, then replace them wherever they occur in the collection. I don't know the best way to do this however - still fairly new to all this ;)

I'd also like to be able to get the user's GUID, I've tried accessing it directly as a property of the collection, but it doesn't return anything.

Here's my code, I'd really appreciate any comments/suggestions :) - or any bad habits I have in general pointing out! ;)

I'm using vs2005 and .Net 2.0

Public Class ADCLass
        ''' <summary>
        ''' Calls recursive function to return users in group
        ''' </summary>
        ''' <param name="DistListAlias">CN for the Group</param>
        ''' <returns>Collection of class holding user details</returns>
        ''' <remarks></remarks>
        Public Function GetDistListUsers(ByVal DistListAlias As String) As Collection(Of ADMembers)
            Dim path As String = "LDAP://DC=systems,DC=Private"
            Dim filter As String
            Dim filterFormat As String = "(cn={0})"
            Dim sAMAccountFilter As String

            filter = String.Format(filterFormat, DistListAlias)

            Dim properties As PropertyCollection = GetPropertiesForEntry(path, filter)

            sAMAccountFilter = "(|(ObjectClass=Group)(objectCategory=user))"
            Dim groupMembers As Collection(Of ADMembers) = New Collection(Of ADMembers)

            If Not IsNothing(properties) Then

                Dim sAMAccountTypes As Collection(Of Integer) = New Collection(Of Integer)
                groupMembers = GetUsersInGroup(properties, groupMembers, sAMAccountFilter)

            End If
            Return groupMembers
        End Function

#Region "GetUsersInGroup"
        ''' <summary>
        ''' Recursive function to list all users in group and sub group
        ''' Returns the sAMAccountName for the Managers
        ''' </summary>
        ''' <param name="Properties">Group Properties</param>
        ''' <param name="groupMembers">Collection fo Users</param>
        ''' <param name="filter"></param>
        ''' <returns>Collection of class holding user details</returns>
        ''' <remarks></remarks>
        Private Function GetUsersInGroup(ByVal Properties As PropertyCollection, ByVal groupMembers As Collection(Of ADMembers), ByVal filter As String)
            Dim pathFormat As String = "LDAP://{0}"
            Dim memberIdx As String = "member"
            Dim sAMAccountNameIdx As String = "sAMAccountName"
            Dim sAMAccountTypeIdx As String = "sAMAccountType"
            Dim personnelNumberIdx As String = "extensionAttribute4"
            Dim TelNo As String
            Dim prop As Object
            Dim managerID As String
            Dim manColl As PropertyCollection

            If Not IsNothing(Properties(memberIdx)) Then
                'Loop through found Members
                For Each prop In Properties(memberIdx)

                    Dim distinguishedName As String = prop.ToString
                    Dim path As String = String.Format(pathFormat, distinguishedName)
                    Dim childProperties As PropertyCollection = GetPropertiesForEntry(path, filter)

                    If Not IsNothing(childProperties) Then
                        'Check that this is a user
                        If childProperties(sAMAccountTypeIdx).Value = 805306368 Then

                            'GetManager ID
                            managerID = childProperties("manager").Value.ToString
                            manColl = GetPropertiesForEntry(String.Format(pathFormat, managerID), filter)
                            managerID = manColl(sAMAccountNameIdx).Value.ToString

                            'Get Phone Number, if telephone number is null, check mobile, if null
                            'return ""
                            If Not IsNothing(childProperties("telephoneNumber").Value) Then
                                TelNo = childProperties("telephoneNumber").Value.ToString
                            Else
                                If Not IsNothing(childProperties("mobile").Value) Then
                                    TelNo = childProperties("mobile").Value.ToString
                                Else
                                    TelNo = ""
                                End If
                            End If
                            'Add the Properties to the class collection
                            groupMembers.Add(New ADMembers(childProperties(sAMAccountNameIdx).Value.ToString, _
                                                    childProperties("cn").Value.ToString, _
                                                    managerID, _
                                                    childProperties("Title").Value.ToString, _
                                                    TelNo, _
                                                    childProperties("mail").Value.ToString))
                        Else
                            'Found a group - recurse
                            GetUsersInGroup(childProperties, groupMembers, filter)
                        End If
                    End If
                Next

            End If
            Return groupMembers
        End Function


#End Region
#Region "GetPropertiesForEntry"
        ''' <summary>
        ''' Gets properties for given user in AD
        ''' </summary>
        ''' <param name="path">Distinguished AD name</param>
        ''' <param name="filter"></param>
        ''' <returns>Property collection for given user</returns>
        ''' <remarks></remarks>
        Private Function GetPropertiesForEntry(ByVal path As String, ByVal filter As String) As PropertyCollection

            Dim rootEntry As New DirectoryEntry(path)
            Dim searcher As New DirectorySearcher(rootEntry)

            With searcher
                .Filter = filter
                .PageSize = 5
                .ServerTimeLimit = New TimeSpan(0, 0, 30)
                .ClientTimeout = New TimeSpan(0, 10, 0)
            End With

            Dim result As SearchResult = searcher.FindOne

            Return result.GetDirectoryEntry.Properties

        End Function
#End Region

    End Class

Code I am using as per JPBlancs's suggestion, whilst this works, it is much slower than my original, am I implementing it incorreclty?

Public Sub GetPropertiesForEntry()

    Dim rootEntry As New DirectoryEntry("LDAP://DC=d1,DC=d2")
    Dim searcher As New DirectorySearcher(rootEntry)

    With searcher
        .Filter = "(&(memberof:1.2.840.113556.1.4.1941:=CN=grp1,OU=Messaging Groups,OU=Groups,DC=d1,DC=d2)(objectCategory=user))"
        .SearchScope = SearchScope.Subtree
        .PropertiesToLoad.Add("cn")
        .PageSize = 100
        .ServerTimeLimit = New TimeSpan(0, 0, 30)
        .ClientTimeout = New TimeSpan(0, 10, 0)
    End With

    Dim results As SearchResultCollection = searcher.FindAll()
    For Each result As SearchResult In results
        Debug.Print(result.Properties("cn").Item(0).ToString)
    Next
End Sub
SWa
  • 4,343
  • 23
  • 40

1 Answers1

1

Can you have a look to Finding users that are members of two active directory groups. You'll find a method using LDAP_MATCHING_RULE_IN_CHAIN to collect recursively users from a group in one query.

As far as th GUID is concerned don't forget list the attribute you want the query to return. Be careful, as far as I remember the GUID will return in an INT array.

dsLookFor.PropertiesToLoad.Add("objectGUID")
Community
  • 1
  • 1
JPBlanc
  • 70,406
  • 17
  • 130
  • 175
  • Thanks JPBlanc, that's really useful, so if I've got this right I can use it to return all users with a group that may not be directly in that group but within any number of nested sub groups? Sorry if that sounds obvious but I'm still very new at this!! – SWa Mar 04 '12 at 19:20
  • Thanks for your answer JPBlanc, however unless I am not implementing this correctly (very possible ;)!!) I've posted my code above, would you mind taking a quick look and see if I am doing anything glaringly wrong? – SWa Mar 05 '12 at 14:23
  • It's not the first time I heard about the fact this request is slow. First do you get the good answers ? How many responses do you grab ? Second can you make the same test using (objectCategory=person) or even removing this part. – JPBlanc Mar 05 '12 at 14:49
  • In total about 180 users are returned, there are about 8 or 9 nested groups (distribution lists) within the main list that it has to grab from. I get the right answers, exactly the same as the original code:), I'll give objectcategory a go, thanks for your help +1 – SWa Mar 05 '12 at 14:52
  • I'm still using my original code, but it only needs running once a day in the middle of the night, so performance isn't that crucial now. Your solution works and I'm grateful for all your help and certainly simplifies things :) – SWa Mar 09 '12 at 09:10