<

Active Directory Shadow Groups

Published on
3,043 Points
43 Views
Last Modified:
Shaun Vermaak
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.
This is my take on Shadow Groups, the principle of maintaining group membership based on objects within an organizational unit within the Active Directory.

Introduction


Some administrators prefer assigning permissions based on Active Directory placement, similar to how you can assign permissions to containers in NDS. Because this capability is not available natively in Active Directory, they spend a lot of time keeping group membership and objects within an organizational unit in sync.


This is my implementation of syncing Shadow groups with organizational units.


Setup


1) Download ShadowGroup tool (http://blog.ittelligence.com/wp-content/uploads/2018/07/ShadowGroups.zip) and extract to a folder on your file server.


2) Run Configurator.exe (Configurator Editor).


a) On the Encrypt tab, enter the password for the account that will be performing the cleanup task. Encrypt it with key vEgEAgUlpSSIj8Le and record the encrypted password.


 

b) On the Settings tab, enter the distinguished name, fully qualified domain name, NetBIOS, and username and the encrypted password recorded in step 2a.



c) On the ShadowGroups tab, specify the group name and the distinguished name of an organizational unit that group should shadow  (+ or INS to add, - or DEL to delete, Enter or double-click to edit)




The code


public static bool ShadowGroup(Models.DomainInfo DomainInfo, string OUPath, string GroupName, out string Message)
{
    try
    {
        DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://" + DomainInfo.FQDN + "/" + OUPath, DomainInfo.NetBIOS + @"\" + DomainInfo.UserName, DomainInfo.Password);
        DirectoryEntry groupDirectoryEntry;
        string message;

        if (GetGroupDEByName(DomainInfo, GroupName, out groupDirectoryEntry, out message))
        {
            // Clear all members
            if (ClearGroupMembers(groupDirectoryEntry, out message))
            {
                DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry);

                directorySearcher.PageSize = 1000;
                directorySearcher.PropertiesToLoad.Add("cn");
                directorySearcher.PropertiesToLoad.Add("distinguishedName");
                directorySearcher.PropertiesToLoad.Add("objectSid");

                // Get users and groups from the specified OU
                directorySearcher.Filter = "(|(&(objectClass=person)(objectClass=user))(objectClass=group))";

                foreach (SearchResult searchResult in directorySearcher.FindAll())
                {
                    try
                    {
                        DirectoryEntry childDirectoryEntry = searchResult.GetDirectoryEntry();

                        string distinguishedName = "";
                        string groupSID = "";
                        SecurityIdentifier sid;

                        try { sid = new SecurityIdentifier(childDirectoryEntry.Properties["objectSid"][0] as byte[], 0); groupSID = sid.Value; } catch { }
                        try { distinguishedName = childDirectoryEntry.Properties["distinguishedName"].Value.ToString(); } catch { }

                        // Only add objects to group that have a security identifier
                        if (groupSID != "")
                        {
                            groupDirectoryEntry.Properties["member"].Add(distinguishedName);
                            groupDirectoryEntry.CommitChanges();
                        }
                    }
                    catch
                    {
                        // Item  error
                    }
                }
                Message = message;
                return true;
            }
            else
            {
                Message = message;
                return false;
            }
        }
        else
        {
            Message = message;
            return false;
        }
    }
    catch (Exception ex)
    {
        Message = ex.Message;
    }

    return false;
}
public static bool ClearGroupMembers(DirectoryEntry GroupDE, out string Message)
{
    try
    {
        // Get a collection of the current group members
        PropertyValueCollection members = GroupDE.Properties["member"];
        string groupName = GroupDE.Properties["sAMAccountName"].Value.ToString();

        // Remove these members one at a time
        for (int counter = members.Count - 1; counter >= 0; counter--)
        {
            members.RemoveAt(counter);
            GroupDE.CommitChanges();
            GroupDE.Close();
        }

        Message = $"Group \"{groupName}\" cleared";
        return true;
    }
    catch (Exception ex)
    {
        Message = ex.Message;
        return false;
    }
}
public static bool GetGroupDEByName(Models.DomainInfo DomainInfo, string GroupName, out DirectoryEntry GroupDE, out string Message)
{
    try
    {
        using (DirectoryEntry de = new DirectoryEntry("LDAP://" + DomainInfo.FQDN, DomainInfo.NetBIOS + @"\" + DomainInfo.UserName, DomainInfo.Password))
        {
            using (DirectorySearcher groupDS = new DirectorySearcher(de))
            {
                string queryGroup = $"(&(objectCategory=group)(CN={GroupName}))";
                groupDS.Filter = queryGroup;
                groupDS.PropertiesToLoad.Add("memberOf");
                groupDS.PropertiesToLoad.Add("member");
                groupDS.PropertiesToLoad.Add("name");
                groupDS.PropertiesToLoad.Add("sAMAccountName");
                groupDS.PropertiesToLoad.Add("distinguishedName");

                SearchResult groupSearchResult = groupDS.FindOne();

                if (groupSearchResult != null)
                {
                    Message = $"Group \"{GroupName}\" found";
                    GroupDE = groupSearchResult.GetDirectoryEntry();
                    return true;
                }
                else
                {
                    Message = $"Group \"{GroupName}\" not found";
                    GroupDE = null;
                    return false;
                }
            }
        }
    }
    catch (Exception ex)
    {
        Message = ex.Message;
        GroupDE = null;
        return false;
    }
}


Demo Execution



I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

 

Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts...  

 

Please do not forget to press the "Thumb's Up" button if you think this article was helpful and valuable for EE members.


It also provides me with positive feedback. Thank you!

 

0
Comment
0 Comments

Featured Post

Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

Join & Write a Comment

There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…
Sometimes it takes a new vantage point, apart from our everyday security practices, to truly see our Active Directory (AD) vulnerabilities. We get used to implementing the same techniques and checking the same areas for a breach. This pattern can re…

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month