<

Active Directory Shadow Groups

Published on
3,844 Points
744 Views
1 Endorsement
Last Modified:
Editors:
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!

 

1
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free