<

Active Directory Shadow Groups

Published on
3,234 Points
134 Views
1 Endorsement
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!

 

1
Comment
0 Comments

Featured Post

Making Bulk Changes to Active Directory

Watch this video to see how easy it is to make mass changes to Active Directory from an external text file without using complicated scripts.

Join & Write a Comment

This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles to another domain controller. Log onto the new domain controller with a user account t…
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…

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month