Search code examples
c#active-directorydomaincontroller

Query user data from a specific AD DC


I'd like to query some user attribute in our AD, but on a specific DC, in C#. We've a few dozens of DCs and I suspect that there is some replication issue between them. I'd like to make a cleanup of unused accounts, to which I'd like to use the last logon time attribute and I'd like to query this on all DCs one bye one (I know this is a bit like brute forcing, however I don't intended to do such a thing too often) so I can see if the most recent value is up-to date or not. I had the code to query all the DCs:

            Domain TestDomain = Domain.GetCurrentDomain();
            Console.WriteLine("Number of found DCs in the domain {0}", TestDomain.DomainControllers.Count);
            foreach (DomainController dc in TestDomain.DomainControllers)
            {
                Console.WriteLine("Name: " + dc.Name);
                ///DO STUFF
            }

And I also found help to construct the code that can query a user from AD:

    PrincipalContext context = new PrincipalContext(ContextType.Domain, "test.domain.com");
            string userName = "testusername";
            UserPrincipal user = UserPrincipal.FindByIdentity(context, userName);
            Console.WriteLine(user.LastLogon.Value.ToString());                
            Console.ReadKey();

And here I stuck. Now I'd like to get the user's last logon timestamp from all DCs. In the past I already deleted accidentaly account that seemed to be unused for a long time (check on only one DC), than it turned out the user use it every day so the info came from the DC was not synced. I'm aware that the most reasonable action would be to review what cause this incorrect sync phenomena, however in my current status that would take ages and probably ended without any finding... Thanks in advance for any constructive response/comment!


Solution

  • EDIT: After re-reading your question I realised what the problem actually is. You believe you have a replication issue because a user's Last Logon attribute doesnt match on all domain controllers? This is by design! That attribute is domain controller specific and IS NOT REPLICATED. To check the true last logon time of a user you must always query every domain controller to find the latest time!

    You are almost there, try this:

    public static List<UserPrincipal> GetInactiveUsers(TimeSpan inactivityTime)
    {
        List<UserPrincipal> users = new List<UserPrincipal>();
    
        using (Domain domain = Domain.GetCurrentDomain())
        {
            foreach (DomainController domainController in domain.DomainControllers)
            {
                using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domainController.Name))
                using (UserPrincipal userPrincipal = new UserPrincipal(context))
                using (PrincipalSearcher searcher = new PrincipalSearcher(userPrincipal))
                using (PrincipalSearchResult<Principal> results = searcher.FindAll())
                {
                    users.AddRange(results.OfType<UserPrincipal>().Where(u => u.LastLogon.HasValue));
                }
            }
        }
    
        return users.Where(u1 => !users.Any(u2 => u2.UserPrincipalName == u1.UserPrincipalName && u2.LastLogon > u1.LastLogon))
            .Where(u => (DateTime.Now - u.LastLogon) >= inactivityTime).ToList();
    }
    

    It won't show people who've never logged in though. If you need that you can probably figure it out.