none
Find nested memberships of AD Group in C # .NET 4.6 RRS feed

  • Question

  • Suppose we have an AD Group structure that reflects an organisation :  

    Contoso Global

        -Contoso Asia

             + Contoso China
               etc.....
        -Contoso Europe
              - Contoso UK
              +Contoso Birmingham
                                        etc.
              -Contoso London
                      Ian Jones  - the boss in London
                      +Contoso UK HR
                               etc.
                      - Contoso UK Finance
                                        Fred Blogs
                                        Helen Brown


    Each AD Group is made up of both people ( EG Managers ) and other 'child' AD groups

    In the example above, 'Contoso London' would include 

    • Contoso UK Finance
    • Contoso UK HR
    • Ian Jones 

    I can find out all the groups that 'Helen Brown' belongs to both directly and via nested groups.   


    How do I take a group as the start point, using .NET Framework 4.6.  to find out the groups it belongs to, and the groups and members who belong to it? 

    I suspect this will be two distinct queries to find out 
    a) What groups and people belong to 'Contoso London' ( desired answer : Contoso UK HR, Contoso UK Finance,  Ian Jones, Fred Blogs,Helen Brown) ?

    b) What groups is 'Contoso London' a member of  (desired answer : Contoso global, Contoso Europe, Contoso UK) ?

    Any ideas gratefully received. 

    Thanks , Richard


    Richard

    Wednesday, October 16, 2019 7:55 AM

Answers

  • I don't code in C#, but you can use an LDAP syntax filter to retrieve all groups that a specified user belongs to, including due to any group nesting. The LDAP filter would be similar to below:

    (member:1.2.840.113556.1.4.1941:= 
    cn=Jim Smith,ou=West,dc=Domain,dc=com)

    You must specify the full distinguished name of the user, as the member attribute of groups is DN syntax and wildcards are not allowed. In PowerShell, for example, you can use the DirectorySearcher object to retrieve group memberships with this filter, similar to below:

    $Domain = New-Object System.DirectoryServices.DirectoryEntry
    $Searcher = New-Object System.DirectoryServices.DirectorySearcher
    $Searcher.SearchRoot = $Domain
    $Searcher.PageSize = 200
    $Searcher.SearchScope = "subtree"
    $Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
    $Searcher.PropertiesToLoad.Add("Name") > $Null
    
    $UserDN = "cn=Jim Smith,ou=West,dc=Domain,dc=com"
    
    $Filter = "(member:1.2.840.113556.1.4.1941:=$UserDN)"
    $Searcher.Filter = $Filter
    $Results = $Searcher.FindAll()
    ForEach ($Result in $Results)
    {
        $Name = $Result.Properties.Item("Name")
        "Member of: $Name"
    }
    
    
    You can also use ADO, or any technology that supports LDAP syntax filters.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)

    Thursday, October 17, 2019 12:52 PM
    Moderator

All replies

  • Hi,

    Thanks for posting here.

    You are using the c# .net Framework, and using the AD group, which are not in the support scope of this forum.

    It is recommended to ask on the Stackover flow.

    https://stackoverflow.com/

    Best Regards,

    Drake


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, October 17, 2019 5:35 AM
  • So you're telling me that a question about developing in .NET , with C# , using Visual Studio under Windows, to query Active Directory is out of scope for a MSDN forum?  My question could not be any more Microsoft !  Usually people here are more helpful.    

    Richard

    Thursday, October 17, 2019 5:46 AM
  • Hello,

    now you are in the Where-Is-forum. I'm sorry I don't know C# and AD.

    If Drake_Wu thinks, they cannot help you in MSDN C# forum, maybe you could ask here:

    https://forums.asp.net/37.aspx

    https://social.msdn.microsoft.com/Forums/azure/en-US/home?forum=WindowsAzureAD

    Regards, Guido

    Thursday, October 17, 2019 6:02 AM
  • Thanks Guido

    I'd originally posted this elsewhere in the MSDN forums and Drake_Wu moved it here !! I'll have a look there,but 

    asp net is specifically about browser apps & the other link is about azure.  Their moderators might also just simply bin my question as being off topic. 

    My question is specifically related to querying on premise AD


    Richard

    Thursday, October 17, 2019 6:12 AM
  • Might also try asking for help over here.

    https://developercommunity.visualstudio.com/spaces/8/index.html

     

     



    Regards, Dave Patrick ....
    Microsoft Certified Professional
    Microsoft MVP [Windows Server] Datacenter Management

    Disclaimer: This posting is provided "AS IS" with no warranties or guarantees, and confers no rights.

    Thursday, October 17, 2019 12:40 PM
    Moderator
  • I don't code in C#, but you can use an LDAP syntax filter to retrieve all groups that a specified user belongs to, including due to any group nesting. The LDAP filter would be similar to below:

    (member:1.2.840.113556.1.4.1941:= 
    cn=Jim Smith,ou=West,dc=Domain,dc=com)

    You must specify the full distinguished name of the user, as the member attribute of groups is DN syntax and wildcards are not allowed. In PowerShell, for example, you can use the DirectorySearcher object to retrieve group memberships with this filter, similar to below:

    $Domain = New-Object System.DirectoryServices.DirectoryEntry
    $Searcher = New-Object System.DirectoryServices.DirectorySearcher
    $Searcher.SearchRoot = $Domain
    $Searcher.PageSize = 200
    $Searcher.SearchScope = "subtree"
    $Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
    $Searcher.PropertiesToLoad.Add("Name") > $Null
    
    $UserDN = "cn=Jim Smith,ou=West,dc=Domain,dc=com"
    
    $Filter = "(member:1.2.840.113556.1.4.1941:=$UserDN)"
    $Searcher.Filter = $Filter
    $Results = $Searcher.FindAll()
    ForEach ($Result in $Results)
    {
        $Name = $Result.Properties.Item("Name")
        "Member of: $Name"
    }
    
    
    You can also use ADO, or any technology that supports LDAP syntax filters.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)

    Thursday, October 17, 2019 12:52 PM
    Moderator
  • Thanks for your helpful reply.  I have a couple of recursive  functions to  tree walk up and down from a mid tree  AD  group 

            public static List<string> GetAllGroupMembers(string groupName, string domainName = null)

            {
                var result = new List<string>();
                if (groupName == "")
                    return result;
                if (groupName.Contains('\\') || groupName.Contains('/'))
                {
                    domainName = groupName.Split(new char[] { '\\', '/' })[0];
                    groupName = groupName.Split(new char[] { '\\', '/' })[1];
                }
                try
                {
                    PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
                    GroupPrincipal myGroup = GroupPrincipal.FindByIdentity(ctx, groupName);
                    var memberList = myGroup.GetMembers();
                    foreach (Principal p in memberList)
                    {
                        UserPrincipal theUser = p as UserPrincipal;
                        if (theUser != null)
                        {
                            result.Add(theUser.Name  +"| UserID");
                        }
                        GroupPrincipal theGroup = p as GroupPrincipal;
                        if (theGroup != null)
                        {
                            result.Add(theGroup.Name + "| Group");
                            result.AddRange(GetAllGroupMembers(theGroup.Name));
                        }
                    }
                    }
                catch (Exception ex)
                {
     // Error process
                }
                return result;
            }
            private static List<string> GetGroupsBelongedTo(string groupName, string domainName = null)
            {
                var result = new List<string>();
                if (groupName == "")
                    return result;
                if (groupName.Contains('\\') || groupName.Contains('/'))
                {
                    domainName = groupName.Split(new char[] { '\\', '/' })[0];
                    groupName = groupName.Split(new char[] { '\\', '/' })[1];
                }
                try
                {
                    PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
                    GroupPrincipal myGroup = GroupPrincipal.FindByIdentity(ctx, groupName);
                    var memberList = myGroup.GetGroups();
                    foreach (Principal p in memberList)
                    {
                       UserPrincipal theUser = p as UserPrincipal;
                        if (theUser != null)
                        {
                            result.Add(theUser.Name + "| UserID");
                        }
                        GroupPrincipal theGroup = p as GroupPrincipal;
                        if (theGroup != null)
                        {
                            result.Add(theGroup.Name + "| Group");
                            result.AddRange(GetGroupsBelongedTo(theGroup.Name));
                        }
                    }
                }
                catch (Exception ex)
                {
                     // Error process
                }         
                return result;

            }

    These are called as follows 

    var groupsb = GetAllGroupMembers(item);

    //get the users out beneath this in the tree
                var groupsb1 = from element in groupsb where element.Contains("| UserID") orderby element select element.Replace("| UserID","");
    //get the groups out beneath this in the tree
                var groupsb2 = from element in groupsb where element.Contains("| Group") orderby element select element.Replace("| Group", "");
               

    // Get groups out above this in the tree

    var groupsC = GetGroupsBelongedTo(item);

                var groupsc1 = from element in groupsC  orderby element select element.Replace("| Group", "");


    Richard

    Thursday, October 17, 2019 1:33 PM
  • Good day Richard,

    I hope that you're not too discouraged from getting help by all the moving from one forum to the other and the responses 😀

    As others mentioned your message moved to this forum since someone thought that it was not in the best forum and that maybe we can help you find the best place to re-publish your question.

    This forum is not for technical discussions but for helping people to find the best forum for their needs.

    With that being said, please allow me to add my 2 cent to the discussion by going over the discussion point by point, and after this I will try to recommend the best forum to continue the technical discussion

    >> My question could not be any more Microsoft !

    You are right! This is pure Microsoft's related question.

    But I re-read your original message and as someone who also Moderator C# forum (in the Hebrew interface), I might do the same and think that this is not related to C#. You did not mentioned that you develop an application using C#, and you did not mentioned that your question is to do the task in C#. If in this sentence "I can find out all the groups that 'Helen Brown' belongs to both directly and via nested groups." you was mentioning "I can find out all the groups using C#" then I think it was more clear :-)

    You did mentioned that you are using .NET Framework 4.6 but this is a technology and not a language and it can be used for example using AD built-in tools. It is simply not clear from your original question any relation to C#.

    In first glance when I read ONLY the original message, it seems to me like a question which is best fit to forums that discuss AD and not C# forum. It might be good idea to move the thread back with clarification, or maybe to find better forum for this.

    In any case, this sentence was not in place in my opinion!
    "Usually people here are more helpful."

    People here do what they find best in order to help others, without getting paid. This is a community forum and even the Moderators are volunteers from the community (some Microsoft employees and most not). There will be cases where people will do mistake! Someone might not understand you correctly since you did not describe the scenario well or he did not read it well - the reason is not important. What ever you get here you should thanks these who try to help you!

    >> asp net is specifically about browser apps

    This is a huge mistake in my opinion. asp.Net is for web applications and these are NOT necessarily related to browsers. asp.Net fit for example for web-services, Windows Communication Foundation (WCF) services, API for any client application (web app or not, which need to pull/push information to/from the web), and so on...

    Browsers apps is only one aspect of using asp.net (probably the most common if we speak about developing websites)

    It make perfectly sense to develop asp.net application (service) which menage the AD for example, but your question is not directly related to the web but to the code, if I understand correctly (meaning the C# part if this is the language which you use)

    >> My question is specifically related to querying on premise AD

    How?!? This is the question in order to find the best forum for you.

    You did not mentioned how do you want to do it, with which language and technology.

    According to Richard Mueller answer and your response to him and the code you mentioned, I now can guess that the answer is that you want to do it with C# :-)

    This question is perfectly fit to the C# forum but there was no way to know this from your original question.

    I highly recommend to close this thread here, without moving the thread since if we will move it to C# forum or any other forum the readers will read the original question and title and will probably say that the question is not fit for their forum.

    My recommendation is to re-phrase the question from the start in a clear way of what you want to do and get and which language and technologies you use. I recommend to add any code which you already have and any attempt which you have. If in the C# forum they will see C# code and your question will focus on the C# code, then it will be clear fit there, and if you focus on the AD then in the AD will think it fit their forums and so on...

    In my opinion the question was not asked in a clear way originally, and you can do better now :-)

    I hope this was useful😀

    p.s.

    Sry I cannot help in the specific question even so I do know C# but I never controlled AD from C# and at this time it will require me to learn more than in the scope of the forum support. I will need to learn new classes related to the topic. There is a good chance that most members in the C# forum will have the same dilemma and therefore, you will not find fast answer in C# forums. There is a good chance that in AD forums you will find faster answer (or not).

    In the following article you can find a list of MSDN anfd TechNet forums ordered by Languages, categories, and field of discussions. It can help you find the best forum (if not in this case then next time)


    signature   Ronen Ariely
     [Personal Site]    [Blog]    [Facebook]    [Linkedin]




    Thursday, October 17, 2019 11:41 PM
    Moderator
  • Dear Ronen

    Thank you for the detailed response.   Howevere, the title of my question explicitly mentioned C# and .NET 4.6 - so I'd thought I did make it clear from the offset what I was after.  I didnt feel the need to repeat it in the body of the question,  but thank you for the observation.    This is a great forum where it is usually possible to find the most helpful replies.  

    Thank you again  :) 

     


    Richard

    Friday, October 18, 2019 9:24 AM
  • You are most welcome :-)

    Please close this thread (by marking one or more of the responses as answer) and open a new clean thread as we discuss here, in the forum you think best fit (C# or AD forums).


    signature   Ronen Ariely
     [Personal Site]    [Blog]    [Facebook]    [Linkedin]

    Saturday, October 19, 2019 2:46 AM
    Moderator
  • In case it helps, the LDAP filter I suggested uses a matching rule called LDAP_Matching_Rule_in_Chain. It is documented here, with C# code examples of use of the filter:

    https://docs.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax

    The point is that there should be no need to walk the tree to reveal group nesting. The matching rule does it for you on the server rather than the client, so it is more efficient. Plus, it requires much less code.


    Richard Mueller - MVP Enterprise Mobility (Identity and Access)

    Saturday, October 19, 2019 12:06 PM
    Moderator