Запрос пользовательских данных из определенного AD DC

Я хотел бы запросить некоторый атрибут пользователя в нашей AD, но на конкретном контроллере домена в C#. У нас есть несколько десятков контроллеров домена, и я подозреваю, что между ними есть проблема с репликацией. Я хотел бы очистить неиспользуемые учетные записи, для которых я хотел бы использовать атрибут времени последнего входа в систему, и я хотел бы запросить это на всех контроллерах домена один за другим (я знаю, что это немного похоже на грубое форсирование, однако я не собирался делать это слишком часто), поэтому я могу видеть, является ли самое последнее значение актуальным или нет. У меня был код для запроса всех контроллеров домена:

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

И тут я застрял. Теперь я хотел бы получить отметку времени последнего входа пользователя со всех контроллеров домена. В прошлом я уже случайно удалил учетную запись, которая, казалось, не использовалась в течение длительного времени (проверка только на одном ДЦ), чем оказалось, что пользователь использует его каждый день, поэтому информация, поступающая с ДЦ, не синхронизировалась. Я знаю, что наиболее разумным действием было бы выяснить, что вызывает этот феномен неправильной синхронизации, однако в моем нынешнем статусе это заняло бы целую вечность и, вероятно, закончилось бы без каких-либо результатов... Заранее спасибо за любой конструктивный ответ/комментарий!


person SecThor    schedule 19.05.2015    source источник


Ответы (2)


EDIT: перечитав ваш вопрос, я понял, в чем проблема. Вы считаете, что у вас проблема с репликацией, потому что атрибут «Последний вход» пользователя не совпадает на всех контроллерах домена? Это по дизайну! Этот атрибут зависит от контроллера домена и НЕ РЕПЛИКИРОВАН. Чтобы узнать истинное время последнего входа пользователя в систему, вы всегда должны запрашивать последнее время у каждого контроллера домена!

Вы почти у цели, попробуйте следующее:

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

Однако он не будет показывать людей, которые никогда не входили в систему. Если вам это нужно, вы, вероятно, можете понять это.

person Ashigore    schedule 19.05.2015
comment
Спасибо, это работает для меня! Я надеюсь, что смогу реализовать остальное ;-) Еще раз спасибо за помощь! - person SecThor; 21.05.2015
comment
В моем случае все даты, созданные таким образом, более ранние, чем время последнего входа пользователя в Windows. Ожидается ли это? - person fuglede; 04.08.2018
comment
Выяснил ответ: несколько сбивает с толку, что LastLogon относится к атрибуту LastLogonTimeStamp, а не к LastLogon. Насколько я могу судить, единственный способ выбрать последнее — это перейти на один уровень ниже и обрабатывать LDAP через COM. В частности, поскольку LastLogonTimeStamp реплицируется по дизайну, проверка его для каждого отдельного контроллера домена даст то же самое, что и для одного из них. - person fuglede; 04.08.2018
comment
Позвольте мне просто добавить это решение в качестве собственного ответа для справки. - person fuglede; 04.08.2018

Хотя принятый ответ был хорошим толчком в правильном направлении, я обнаружил, что в конечном итоге он не дает ожидаемого результата: как намекается в ответе, «lastLogon» не реплицируется между контроллерами, тогда как атрибут «lastLogonTimeStamp» is< /em> реплицировано (но, с другой стороны, нет гарантии, что ошибка будет более 14 дней, см. этот ответ).

Теперь, что довольно сбивает с толку, UserPrincipal.LastLogon относится не к нереплицированному, но точному «lastLogon», а к реплицированному, но неточному «lastLogonTimeStamp», и я обнаружил, что, запустив код в принятом ответе, все созданные DateTime были одинаковыми и неправильными.

Вместо этого, вдохновленный этим ответом, я обнаружил, что для того, чтобы найти самую последнюю дату входа в систему для пользователя с заданным 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;
}
person fuglede    schedule 04.08.2018