Search code examples
c#.netactive-directory

Get users that are 'memberof' a group


I got a working solution, however I'm pretty sure there is a less resource-intensive method because the current solution involves doing a query to get the groups member and then a query to get each users information.

Here is the code I have :

DirectoryEntry root = new DirectoryEntry( "LDAP://server:port" );
DirectorySearcher searcher = new DirectorySearcher( root );
searcher.Filter = "(&(ObjectClass=Group)(CN=foo-group))";

var members = (IEnumerable)searcher.FindOne()
              .GetDirectoryEntry()
              .Invoke( "members" );

Dictionary<string , string> results = new Dictionary<string , string>();

foreach( object member in members ) {
   DirectoryEntry de = new DirectoryEntry( member );
   results.Add( de.Properties[ "SAMAccountname" ][ 0 ].ToString(), de.Properties[ "cn" ][ 0 ].ToString() );
}

Ideally I'd like to be able to do a single query to get every user that are member of a group, filters the properties to load and then display them. So something like this

DirectoryEntry root = new DirectoryEntry( "LDAP://server:port" );
DirectorySearcher searcher = new DirectorySearcher( root );
searcher.PropertiesToLoad.Add( "cn" );
searcher.PropertiesToLoad.Add( "SAMAccountname" );
searcher.Filter = "(&(ObjectClass=user)(memberof=foo-group))";

foreach( var user in searcher.FindAll() ) {
    //do whatever...
}

Unfortunately, that doesn't work for some reason.


Solution

  • If you can use System.DirectoryServices.AccountManagement:

    var context = new PrincipalContext(ContextType.Domain, "YOUR_DOMAIN_NAME");
    using (var searcher = new PrincipalSearcher())
    {
        var groupName = "YourGroup";
        var sp = new GroupPrincipal(context, groupName);
        searcher.QueryFilter = sp;
        var group = searcher.FindOne() as GroupPrincipal;
    
        if (group == null)
            Console.WriteLine("Invalid Group Name: {0}", groupName);
    
        foreach (var f in group.GetMembers())
        {
            var principal = f as UserPrincipal;
    
            if (principal == null || string.IsNullOrEmpty(principal.Name))
                continue;
    
            Console.WriteLine("{0}", principal.Name);
        }
    }
    

    I have some VB code that'll do it the old way also, but this is definitely simpler with AccountManagement.


    Here's the VB code I was referring to (again it isn't pretty but it's functional):

    Public Function GetUsersByGroup(de As DirectoryEntry, groupName As String) As IEnumerable(Of DirectoryEntry)
        Dim userList As New List(Of DirectoryEntry)
        Dim group As DirectoryEntry = GetGroup(de, groupName)
    
        If group Is Nothing Then Return Nothing
    
        For Each user In GetUsers(de)
            If IsUserInGroup(user, group) Then
                userList.Add(user)
            End If
        Next
    
        Return userList
    End Function
    
    Public Function GetGroup(de As DirectoryEntry, groupName As String) As DirectoryEntry
        Dim deSearch As New DirectorySearcher(de)
    
        deSearch.Filter = "(&(objectClass=group)(SAMAccountName=" & groupName & "))"
    
        Dim result As SearchResult = deSearch.FindOne()
    
        If result Is Nothing Then
            Return Nothing
        End If
    
        Return result.GetDirectoryEntry()
    End Function
    
    Public Function GetUsers(de As DirectoryEntry) As IEnumerable(Of DirectoryEntry)
        Dim deSearch As New DirectorySearcher(de)
        Dim userList As New List(Of DirectoryEntry)
    
        deSearch.Filter = "(&(objectClass=person))"
    
        For Each user In deSearch.FindAll()
            userList.Add(user.GetDirectoryEntry())
        Next
    
        Return userList
    End Function
    
    Public Function IsUserInGroup(user As DirectoryEntry, group As DirectoryEntry) As Boolean
        Dim memberValues = user.Properties("memberOf")
    
        If memberValues Is Nothing OrElse memberValues.Count = 0 Then Return False
    
        For Each g In memberValues.Value
            If g = group.Properties("distinguishedName").Value.ToString() Then
                Return True
            End If
        Next
    
        Return False
    End Function
    

    And usage:

    Dim entries = New DirectoryEntry("LDAP://...")
    Dim userList As IEnumerable(Of DirectoryEntry) = GetUsersByGroup(entries, "GroupName")