Search code examples
c#active-directoryprincipal

How to avoid groupPrincipal.Members.Add throwing a NoMatchingPrincipalException


I've get an Active Directory Domain in which I create group objects under a certain newly created OU as shown below, e.g. the groups under SubOuB. The global groups under GlobalGroups are already there.

+MainOu
   +--SubOuA
     +--GroupA_A
     +--GroupA_B
     +--GroupA_C
  +--SubOuB
     +--GroupB_A
     ...
+GlobalGroups
  +--GlobalGroupA
  +--GlobalGroupB
  ...

Now I want to add the newly added groups under the newly created OU as members to one of the global groups, e.g. add GroupA_A as a member to GlobalGroupB:

using( PrincipalContext principalContext =
    new PrincipalContext( ContextType.Domain, Environment.UserDomainName ) )
{
    GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(
        principalContext, "GlobalGroupB" );
    if( groupPrincipal != null )
    {
        // Target group found.
        groupPrincipal.Members.Add( principalContext, IdentityType.Name, "GroupA_A" );
        // The next line throws a NoMatchingPrincipalException.
        groupPrincipal.Save();
    }
}

When I look at the AD structure and objects I can see both objects. The ADSI Editor shows me that both "names" are correct. When I manually run the code on its own in a test application after the new OU and group objects are created, adding the group as a member to the global group also works fine. All of the code I used works in a staging environment with an AD of the same structure, so I'm not sure why it does have problems in the productive environment.

Any ideas? I've seen that DirectoryEntry objects have a RefreshCache method that forces to reload the property values of the particular object, but I don't think that this is going to help here. Is my problem a timing problem? Can I force to "reload" the existing objects so the newly created group is found and can be added to the global group?

Update

This definitely seems to be a problem with the AD synchronisation - when I continuously check for the existence of the newly created group object (e.g. loop with a Thread.Sleep( 1000 )) my code works as expected. The drawback is that I don't know how long I'll have to wait (tests varied between 6 and 15 seconds in my environment) and I also think this is a very dirty approach. Unfortunately I couldn't find any information about how to "refresh" the AD objects or "flush" the cache.

Update 2

When I connect to a specific domain controller, the first query is slow (3000+ milliseconds), subsequent queries are faster and all take about the same time to return. I still don't know how I can avoid to periodically query the AD until the expected object is indeed available (as a query result).


Solution

  • As far as I know the only ways to solve my problem are as follows:

    • Either do as suggested in my question and periodically query "your" domain controller until the expect object becomes available or a configurable timeout is reached
    • Retrieve all available domain controllers via Domain.DomainControllers and query each of them until your object was found

    I don't like either way too much, but I couldn't find any other options. Please feel free to suggest better alternatives.