从特定的 AD DC 查询用户数据

Query user data from a specific AD DC

我想在我们的 AD 中查询某些用户属性,但在特定的 DC 上,在 C# 中。 我们有几十个 DC,我怀疑它们之间存在一些复制问题。我想清理未使用的帐户,我想使用上次登录时间属性,我想在所有 DC 上一个一个地查询这个(我知道这有点像暴力破解,但是我不打算经常做这样的事情)所以我可以看看最近的值是否是最新的。 我有查询所有 DC 的代码:

            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
            }

我还找到了构建可以从 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();

我卡在这里了。现在我想从所有 DC 获取用户的上次登录时间戳。 过去我已经不小心删除了似乎很长时间未使用的帐户(只检查一个 DC),结果发现用户每天都在使用它,所以来自 DC 的信息没有同步。 我知道最合理的行动是检查导致这种不正确的同步现象的原因,但是以我目前的状态,这需要很长时间并且可能会在没有任何发现的情况下结束...... 提前感谢任何建设性的 response/comment!

编辑:在重新阅读您的问题后,我意识到问题实际上是什么。您认为您遇到复制问题是因为用户的上次登录属性在所有域控制器上都不匹配?这是设计使然!该属性是特定于域控制器的,并且未被复制。要检查用户的真实上次登录时间,您必须始终查询每个域控制器以查找最新时间!

你快到了,试试这个:

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();
}

它不会显示从未登录过的人。如果你需要,你可能会想出来。

虽然接受的答案是朝着正确方向迈出的一步,但我发现它最终没有产生预期的结果:正如答案中暗示的那样,"lastLogon" 不会在控制器之间复制,而属性 "lastLogonTimeStamp" 复制(但另一方面,不能保证错误超过 14 天,参见 this answer)。

现在,令人困惑的是,UserPrincipal.LastLogon 不是指未复制但精确的 "lastLogon",而是指复制但不精确的 "lastLogonTimeStamp",我发现 运行已接受答案中的代码,所有生成的 DateTime 都是相同且错误的。

相反,受 this answer 的启发,我发现为了找到具有给定 sAMAccountName(您可以轻松扩展以搜索所有用户)的用户的最近登录日期,我会执行如下操作:

public DateTime? FindLatestLogonDate(string username)
{
    var logons = new List<DateTime>();
    DomainControllerCollection domains = Domain.GetCurrentDomain().DomainControllers;
    foreach (DomainController controller in domains)
    {
        using (var directoryEntry = new DirectoryEntry($"LDAP://{controller.Name}"))
        {
            using (var searcher = new DirectorySearcher(directoryEntry))
            {
                searcher.PageSize = 1000;
                searcher.Filter = $"((sAMAccountName={username}))";
                searcher.PropertiesToLoad.AddRange(new[] { "lastLogon" });
                foreach (SearchResult searchResult in searcher.FindAll())
                {
                    if (!searchResult.Properties.Contains("lastLogon")) continue;
                    var lastLogOn = DateTime.FromFileTime((long)searchResult.Properties["lastLogon"][0]);
                    logons.Add(lastLogOn);
                }
            }
        }
    }
    return logons.Any() ? logons.Max() : (DateTime?)null;
}