Enumeration of nested AD user groups using C#

5.1k Views Asked by At

I've written some code that gets all users of groups and nested groups. I also wanted to make sure that looping did not happen if the group membership caused a loop by having the first group a member of the last group.

The code I wrote works OK but is a little slow.

This is the first time I've tried to do AD look-ups.

Could someone take a look and tell me if the code looks OK or bad-coding (or worse), or I've gone about it the wrong way?

using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;
using System.IO;

namespace Tester3
{
    class Program3
    {
        public static List<string> appGroupList = new List<string>();        
        public static List<string> userList = new List<string>();
        public static List<string> groupList = new List<string>();
        public static List<string> groupChecked = new List<string>();

        static void Main(string[] args)
        {
            // Create Output File
            StreamWriter outputfile = new StreamWriter("output.txt", false);

            appGroupList.Add("GLB-SBCCitrixHelpdesk-DL");
            appGroupList.Add("SBC_UKBSAVIA001_PROD_ROL_Siebel");

            foreach (string appGroup in appGroupList)
            {
                string appGroupCN = GetCN(appGroup);

                GetMembers(appGroupCN);

                groupChecked.Clear();
            }

            foreach (string item in userList)
            {
                Console.WriteLine(item);
                outputfile.WriteLine(item);
            }

            outputfile.Flush();
            outputfile.Close();
            Console.ReadLine();
        }

        private static string GetCN(string group)
        {
            string groupCN = string.Empty;

            try
            {
                using (DirectorySearcher search = new DirectorySearcher())
                {
                    search.Filter = "(&(cn=" + group + ")(objectClass=group))";
                    search.PropertiesToLoad.Add("CN");
                    SearchResult result = search.FindOne();

                    if (result != null)
                    {
                        groupCN = result.Properties["adsPath"][0].ToString();
                        groupCN = groupCN.Replace("LDAP://", "");
                    }

                    return groupCN;
                }
            }
            catch (Exception)
            {
                return groupCN;
            }
        }

        public static void GetMembers(string group) // get members using the groups full cn 
        {
            // Check if group has already been checked
            if (groupChecked.Contains(group))
            {
                return;
            }

            // Add group to groupChecked list
            groupChecked.Add(group);

            try
            {
                // Connect to group object
                using (DirectoryEntry groupObject = new DirectoryEntry("LDAP://" + group))
                {
                    // Get member of group object
                    PropertyValueCollection col = groupObject.Properties["member"] as PropertyValueCollection;

                    // Loop through each member
                    foreach (object member in col)
                    {
                        // Connect to member object
                        using (DirectoryEntry memberObject = new DirectoryEntry("LDAP://" + member))
                        {
                            // Get class of member object
                            string memberClass = memberObject.Properties["objectClass"][1].ToString();
                            string memberCN = memberObject.Properties["Name"][0].ToString();

                            if (!groupChecked.Contains(member.ToString()))
                            {
                                if (memberClass.ToLower() == "group")
                                {                                    
                                    GetMembers(member.ToString());
                                }
                                else
                                {
                                    userList.Add(memberCN);
                                }
                            }
                            else
                            {
                                if (memberClass.ToLower() != "group")
                                {
                                    userList.Add(memberCN);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
            }
        }
    }
}
1

There are 1 best solutions below

4
On

If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:

Basically, you can define a domain context and easily find users and/or groups in AD:

// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");

if(user != null)
{
   // get a user's group memberships 
   foreach(Principal principal in me.GetGroups())
   {
       GroupPrincipal gp = (principal as GroupPrincipal);

       if(gp != null)
       {
           // do something with the group
       }
   }
}

The new S.DS.AM makes it really easy to play around with users and groups in AD. The call to .GetGroups() also handles all the problems of nested group memberships and so forth for you - no need to deal with that hassle anymore!