Search code examples
c#dnsactive-directorygroupprincipal

Adding Users to AD Universal Group from Different Domains C#


We have many domains in the same forest (i.e. sw.main.company.com, nw.main.company.com, main.company.com) and I have control of an OU in sw.main.company.com where I have set up a Universal Active Directory Group.

I have no difficulty pragmatically (c#) adding "sw" domain users to the group using System.DirectoryServices.AccountManagement .NET 4.5 etc. on the default AD port, but when it comes to adding users from the other domains (nw, mw, etc.), I get "HResult=-2147016651 Message=The server is unwilling to process the request" and "Operation not allowed through GC port, data 0, v1db1" when setting the new PrincipalContext(ContextType.Domain "sw.main.company.com:3268", "DC=main,DC=company,DC=com").

All of the Domain Controllers are also Global Catalog servers and invoking port 3268 allows the users from the other domains to resolve correctly, but I cannot commit the additions using the GlobalPrincipal.Save() command without throwing the error.

I have included the relevant code below as well as the detailed error stack. I need help with this.

public void SyncADUsers()
{
    AddUserToGroup("MW\\abc123user", "Universal_Group_1");
}

public void AddUserToGroup(string userId, string groupName)
{
    try
    {
        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "sw.main.company.com:3268", "DC=main,DC=company,DC=com"))
        {
            GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
            group.Members.Add(pc, IdentityType.SamAccountName, userId);
            group.Save();
        }
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //doSomething with E.Message.ToString(); 
    }
}

System.InvalidOperationException was unhandled HResult=-2146233079 Message=The server is unwilling to process the request. Source=System.DirectoryServices.AccountManagement StackTrace: at System.DirectoryServices.AccountManagement.ADStoreCtx.UpdateGroupMembership(Principal group, DirectoryEntry de, NetCred credentials, AuthenticationTypes authTypes) at System.DirectoryServices.AccountManagement.SDSUtils.ApplyChangesToDirectory(Principal p, StoreCtx storeCtx, GroupMembershipUpdater updateGroupMembership, NetCred credentials, AuthenticationTypes authTypes) at System.DirectoryServices.AccountManagement.ADStoreCtx.Update(Principal p) at System.DirectoryServices.AccountManagement.Principal.Save() at ExampleUsers.SyncAD.AddUserToGroup(String userId, String groupName) in c:\SourceControl\ExampleUsers\ExampleUsers\SyncAD.cs:line 33 at ExampleUsers.SyncAD.SyncADUsers() in c:\SourceControl\ExampleUsers\ExampleUsers\SyncAD.cs:line 18 at ExampleUsers.Program.Main(String[] args) in c:\SourceControl\ExampleUsers\ExampleUsers\Program.cs:line 62 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: System.DirectoryServices.DirectoryServicesCOMException HResult=-2147016651 Message=The server is unwilling to process the request. Source=System.DirectoryServices ErrorCode=-2147016651 ExtendedError=8245 ExtendedErrorMessage=00002035: LdapErr: DSID-0C090B3E, comment: Operation not allowed through GC port, data 0, v1db1 StackTrace: at System.DirectoryServices.DirectoryEntry.CommitChanges() at System.DirectoryServices.AccountManagement.ADStoreCtx.UpdateGroupMembership(Principal group, DirectoryEntry de, NetCred credentials, AuthenticationTypes authTypes) InnerException:


Solution

  • In reference to BaldPate's response, if the Global Catalog is read only, we need to read and resolve users in different domains using the 3268 port and then SAVE the users using the 389 port all in the same context. This can be accomplished with the following code (note the separate calls to the 3268 and default 389 ports) and thanks to BaldPate for making this clear:

    using System;
    using System.Collections;
    using System.Data;
    using System.Data.SqlClient;
    using System.Collections.Generic;
    using System.DirectoryServices.AccountManagement;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Configuration;
    
    namespace OurUsers
    {
    class SyncAD
    {
        #region Variables
    
        private string sDomain = "sw.main.company.com";
        private string sDomainGC = "sw.main.company.com:3268";
        private string sDefaultOU = "DC=sw,DC=main,DC=company,DC=com";
        private string sDefaultRootOU = "DC=main,DC=company,DC=com";
        private string sGroupName = "Production_Universal_AD_Group";
        private string connectionString = "Server=OurServerName\\PROD; Integrated Security=True; Initial Catalog=OurUsers";
        private string sqlAdd = "SELECT FullID FROM ViewFolkstoAdd";
        private string sqlRemove = "SELECT FullID FROM ViewFolkstoRemove";
    
        #endregion
        public void SyncADUsers()
        {
            // Get Database Ready and Remove Users
            SqlConnection connectionRemove = new SqlConnection(connectionString);
            SqlCommand commandRemove = new SqlCommand(sqlRemove, connectionRemove);
            connectionRemove.Open();
            SqlDataReader readerRemove = commandRemove.ExecuteReader();
    
            if (readerRemove.HasRows)
            {
                int i = 0;
                while (readerRemove.Read())
                {
                    string sUserName = readerRemove.GetString(0);
                    RemoveUserFromGroup(sUserName, sGroupName);
                    i = i + 1;
                    Console.WriteLine("{0} {1}", i, sUserName);
                }
            }
            else
            {
                Console.WriteLine("No rows found.");
            }
            readerRemove.Close();
    
            // Get Database Ready and Add Users
            SqlConnection connectionAdd = new SqlConnection(connectionString);
            SqlCommand commandAdd = new SqlCommand(sqlAdd, connectionAdd);
            connectionAdd.Open();
            SqlDataReader readerAdd = commandAdd.ExecuteReader();
    
            if (readerAdd.HasRows)
            {
                int i = 0;
                while (readerAdd.Read())
                {
                    string sUserName = readerAdd.GetString(0);
                    AddUserToGroup(sUserName, sGroupName);
                    i = i + 1;
                    Console.WriteLine("{0} {1}", i, sUserName);
                }
            }
            else
            {
                Console.WriteLine("No rows found.");
            }
            readerAdd.Close();
        }
    
        /// Gets a certain user on Active Directory
        /// Returns the UserPrincipal Object
        public UserPrincipal GetUser(string sUserName)
        {
            PrincipalContext oPrincipalContext = GetPrincipalContextGC();
            UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, sUserName);
            return oUserPrincipal;
        }
    
        /// Adds the user for a given group
        /// Returns true if successful
        public bool AddUserToGroup(string sUserName, string sGroupName)
        {
            try
            {
                UserPrincipal oUserPrincipal = GetUser(sUserName);
                GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);
                if (oUserPrincipal != null && oGroupPrincipal != null)
                {
                    oGroupPrincipal.Members.Add(oUserPrincipal);
                    oGroupPrincipal.Save();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
    
        /// Removes user from a given group
        /// Returns true if successful
        public bool RemoveUserFromGroup(string sUserName, string sGroupName)
        {
            try
            {
                UserPrincipal oUserPrincipal = GetUser(sUserName);
                GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);
                if (oUserPrincipal != null && oGroupPrincipal != null)
                {
                    oGroupPrincipal.Members.Remove(oUserPrincipal);
                    oGroupPrincipal.Save();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
    
        /// Gets PrincipalContext from the Local Domain
        /// Returns the PrincipalContext
        public PrincipalContext GetPrincipalContext()
        {
            PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomain, sDefaultOU, ContextOptions.Negotiate);
            return oPrincipalContext;
        }
    
        /// Gets PrincipalContext from the Global Catalog
        /// Returns the PrincipalContext
        public PrincipalContext GetPrincipalContextGC()
        {
            PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomainGC, sDefaultRootOU, ContextOptions.Negotiate);
            return oPrincipalContext;
        }
    
        /// Gets a certain group on Active Directory
        /// Returns the GroupPrincipal Object
        public GroupPrincipal GetGroup(string sGroupName)
        {
            PrincipalContext oPrincipalContext = GetPrincipalContext();
            GroupPrincipal oGroupPrincipal = GroupPrincipal.FindByIdentity(oPrincipalContext, sGroupName);
            return oGroupPrincipal;
        }
    }
    }