Directory Programming .NET

Active Directory and ADAM programming support for .NET developers
Welcome to Directory Programming .NET Sign in | Join | Help
in Search

System.DS.AccountManagement.GetMembers

Last post 08-05-2008, 3:39 AM by CarstenSchaefer. 8 replies.
Sort Posts: Previous Next
  •  07-02-2008, 9:34 AM 4086

    System.DS.AccountManagement.GetMembers

    Hi there!

    I already used the new namespace AccountManagement and IMO this is great!
    But IMO there is something missing:

    I have to read all members of a given group. My first try was your incredible GroupExpander for .NET Framework 2.0, but I even have to read members across domains. So I used your GroupExpander for .NET Framework 1.1.

    Now I switched over to AccountManagement.GetMembers(True). This method saves a lot lines of code. But this method doesn't returns the users with PrimaryGroup attribute set to any of the groups nested in the first given one.

    I decided to add a new method which returns all nested groups of the given group (using GetMembers(False) in a recursive method). For every found group I will get the RID and for every RID I will search the GC for all users containing primaryGroupID=RID.
    This works fine.

    While using this tool in a huge AD forest I had a StackOverflowException. MS Support asked me to increas the size of the stack.
    Now I will receive aprox 11.000 users using GetMembers(True)
    and aprox 50.000 nested groups using the recurisve method.
     
    Now the tool crashes. Further the tool ran aprox 4 hours. Unfortunately I can not catch that exception even I am working of course with Try catch blocks.

    Does anyone have an idea to get all users including the members of primary group?
    Is there any new method in .NET Framework 3.5 SP1 ;-)

    Many thanks for any help!

    Kind Regards!

    Carsten Schaefer

  •  07-08-2008, 2:47 PM 4111 in reply to 4086

    Re: System.DS.AccountManagement.GetMembers

    StackOverFlowExceptions are signs of unbounded recursion.  I am not sure what MS means by 'increasing the size of the stack' either.  If you share the code you are looking at we can inspect it for issues.


    Ryan Dunn
    Extemporaneous Mumblings
    The .NET Developer's Guide to Directory Services Programming
  •  07-18-2008, 1:32 AM 4216 in reply to 4111

    Re: System.DS.AccountManagement.GetMembers

    Hi there,

    sorry for the delay. I was on vacation.

    The main class will call a thread to get all group members:

    Dim myThread_GetAllTrusteeUsers As Threading.Thread = New Threading.Thread(AddressOf GetAllTrusteeUsers, 15466496)
    myThread_GetAllTrusteeUsers.Start()
    myThread_GetAllTrusteeUsers.Join()

    #############################
    'snippet of GetAllTrusteeUsers()
    'if groupType is DOMAIN -> get recursive all members
    If group.GroupType = Components.Group.ENUMGROUPTYPE.DOMAIN Then

              'start the incredible AD group expander!
              Dim members As List(Of String) = adAccess.ExpandADGroup(group.Domain, group.Name)

              'get all nested groups of that group (to read the primarygroup members later on!)
    -->>>>      Dim memberGroups As List(Of String) = adAccess.ExpandADGroupOnlyGroups(group.Domain, group.Name)

              'now search the AD for all users with PrimaryGroup Property is the given group
              If Not group.Rid.Equals(0) Then

                members.AddRange(adAccess.SearchUsersByRID(group.Rid, Nothing))

              End If

            'now get the members of the groups in case they have this group defined as their primarygroup
           members.AddRange(adAccess.GetMembersOfPrimGroups(memberGroups))

    ################

    this is the function the exception occurs:
    Public Function ExpandADGroupOnlyGroups(ByVal groupDomain As String, ByVal groupName As String) As List(Of String)

          If LOG.IsDebugEnabled Then
            LOG.Debug("ENTER ExpandADGroupOnlyGroups(). Groups domain: " & groupDomain & " Groups name: " & groupName)
          End If

          Try

            'use .NET Framework 3.5 namespace AccountMgmt!
            Dim pContext As PrincipalContext = New PrincipalContext(ContextType.Domain, groupDomain)

            Dim pGroup As GroupPrincipal
            pGroup = GroupPrincipal.FindByIdentity(pContext, IdentityType.Name, groupName)

            ExpandADGroupOnlyGroupsInternal(pGroup)

            pGroup.Dispose()
            pContext.Dispose()

          Catch ex As Exception

            Throw New FSVADAccessException("Error getting group members: " & ex.Message)

         End Try

          'Return groupNames
          Return _allGroupsForPrimaryCheck

        End Function

    ###################

    Private Sub ExpandADGroupOnlyGroupsInternal(ByVal pGroup As GroupPrincipal)

          _counterGroups = _counterGroups + 1

          If LOG.IsDebugEnabled Then
            LOG.Debug("ENTER ExpandADGroupOnlyGroupsInternal(). Groups Nr.: " & _counterGroups & " DN: " & pGroup.DistinguishedName)
          End If

          'Dim groupNames As List(Of String) = New List(Of String)
          Dim pMemberGroups As List(Of GroupPrincipal) = New List(Of GroupPrincipal)

          Try

            'use .NET Framework 3.5 namespace AccountMgmt!
            Dim pSearchResultGroupsOnly As PrincipalSearchResult(Of Principal)
            pSearchResultGroupsOnly = pGroup.GetMembers()

            For Each pMember As Principal In pSearchResultGroupsOnly

              If pMember.StructuralObjectClass.Equals("group") Then
                _allGroupsForPrimaryCheck.Add(pMember.DistinguishedName)

                'recursive call
                ExpandADGroupOnlyGroupsInternal(pMember)
                pMember.Dispose()

              End If

            Next

            pSearchResultGroupsOnly.Dispose()
            pGroup.Dispose()

            'now start the garbage collector as advised by Microsoft Corp.
            GC.Collect(3)
            GC.WaitForPendingFinalizers()
            GC.Collect(3)

          Catch ex As Exception

            Throw New FSVADAccessException("Error getting group members: " & ex.Message)

          End Try

        End Sub

    #####################

    With the increased stack size I got all users and aprox 50.000 nested groups (without increasing the stack size the exception was thrown after aprox 8000 groups). Then a new exception will be thrown (this time it is not a stackOverflow Exception). Unfortunally we can not catch the new exception. I am already in touch with MS.

    Regards
    Carsten


     
  •  07-18-2008, 9:13 AM 4220 in reply to 4216

    Re: System.DS.AccountManagement.GetMembers

    Did you try to see if GroupPrincipal.GetMembers(true) will do the recursive call for you?  That is what it is supposed to do.
  •  07-21-2008, 3:51 AM 4221 in reply to 4220

    Re: System.DS.AccountManagement.GetMembers

    Hi Joe!

    I already use this great method to get all nested members of a group. This will replace your great ADGroupExpander.

    Unfortunatley the result does not contain the members of the groups in case they have defined this group as the primary group.

    Regards
    Carsten

  •  07-21-2008, 9:56 AM 4224 in reply to 4221

    Re: System.DS.AccountManagement.GetMembers

    No method that works off member/memberOf handles primary group.

    What you can do to discover if any objects have a group as its primary group is to do a search in the domain for any object with primaryGroupID= to the RID of the group (which you can get from its primaryGroupToken constructed attribute).

  •  07-21-2008, 1:53 PM 4227 in reply to 4224

    Re: System.DS.AccountManagement.GetMembers

    Hi Joe!

    That is exactly what I'am doing!
    After reading the members using the GetMembers(True) method I run a method which returns all nested groups of the given group (using GetMembers(False) in a recursive method). For every found group I will get the RID and for every RID I will search the GC for all users containing primaryGroupID=RID.

    This works fine, but in an huge AD forest the tool crashes during the recursive call (the MS supporter thinks the recursion deep is to large) for a very big group.

    Regards
    Carsten

  •  07-21-2008, 2:33 PM 4228 in reply to 4227

    Re: System.DS.AccountManagement.GetMembers

    Ok, I wasn't clear on what was going on.  It looked like you posted your own recursive function previously, so I wasn't sure if that was your code having the recursion problem or if it was the MS code that was having the recursion problem (or both). 

    Regarding the primary group,  I don't think you need to search the GC and think that might actually be a bad idea.  Primary group has to be a global group, so it can only have members from the current domain.  Also, the RID of a group is only unique domain-wide, so you could potentially have duplicates forest wide.

    Best of luck!  Let us know how it turns out.

  •  08-05-2008, 3:39 AM 4342 in reply to 4228

    Re: System.DS.AccountManagement.GetMembers

    Hi there!

    I found a solution: moved the recursive way to an iterativ:

     Public Function ExpandADGroupOnlyGroupsIterativ(ByVal groupDomain As String, ByVal groupName As String) As List(Of String)

          Dim pRootContext As PrincipalContext = New PrincipalContext(ContextType.Domain, groupDomain)
          Dim pRootGroup As Principal = GroupPrincipal.FindByIdentity(pRootContext, IdentityType.Name, groupName)

          Dim allGroupMembers As List(Of String) = New List(Of String)

          Dim stack As Stack = New Stack()
          stack.Push(pRootGroup)

          While (stack.Count > 0)
            Dim currentGroup As Principal = CType(stack.Pop, Principal)
            Dim members As List(Of Principal) = GetGroupMembers(currentGroup.DistinguishedName)
            If (members.Count > 0) Then
              For Each member As Principal In members

                'check if this group is already seen
                If allGroupMembers.Contains(member.DistinguishedName) = True Then
                
                'skip

                Else

                   'add to the list of found groups
                   stack.Push(member)
                   allGroupMembers.Add(member.DistinguishedName)

                End If
             Next
            End If
          End While

          Return allGroupMembers

        End Function



        Private Function GetGroupMembers(ByVal groupDN As String) As List(Of Principal)

         Dim returnList As List(Of Principal) = New List(Of Principal)

          Dim pContext As PrincipalContext = New PrincipalContext(ContextType.Domain, Me.GetDomainNameFromDistinguishedName(groupDN))
          Dim pGroup As GroupPrincipal = Principal.FindByIdentity(pContext, IdentityType.Name, Me.GetCNFromLdapPath(groupDN))

          Dim pSearchResult As PrincipalSearchResult(Of Principal) = pGroup.GetMembers()

          For Each pMember As Principal In pSearchResult

            'use it if it is a group
            If ("group".Equals(pMember.StructuralObjectClass)) Then

              returnList.Add(pMember)

            End If

          Next

          pSearchResult.Dispose()
          pGroup.Dispose()
        
          Return returnList

        End Function
View as RSS news feed in XML