Поиск устаревших учетных записей в AD

 Очень часто на компьютерных форумах возникает вопрос: каким образом можно выполнить поиск «устаревших» учетных записей пользователей и компьютеров в AD? Постараюсь дать развернутый ответ в этой заметке. И так, давайте сначала определимся, что под устаревшими учетными записями мы будем понимать учетные записи, которые давно не использовались. Каким образом можно  определить, как давно не использовалась та или иная учетная запись? Вполне логично было бы вы полнить поиск учетных записей, которые давно не выполняли вход в систему. Казалось, нет ничего проще: мы знаем, что у каждой учетной записи, будь то компьютер или пользователь, имеется атрибут lastLogon, в котором содержится дата/время последнего входа в систему. Делаем запрос к LDAP-каталогу (DC) и, если у вас более одного DC в домене, … получаем ошибочные данные. Все дело в том, что атрибут lastLogon не является реплицируемым, поэтому на разных DC значения этого атрибута для одной и той же учетной записи будут разными. Каждый DC сохраняет в этом атрибуте свою отметку времени о  «логоне» (процессе входа в систему), который был обслужен  данным конкретным DC. Сделано это, надо полагать, намерено, иначе любой вход в систему должен был бы приводить к репликации. Как же нам быть в этой ситуации? По всей видимости, необходимо опросить все DC в домене, и выбрать из всех имеющихся значений атрибута lastLogon тот, который наиболее близок к настоящему моменту времени. Именно так и приходилось поступать для доменов, которые работают в Режиме Windows 2000 (Windows 2000 Domain Functional Level). Для доменов, которые работают в Режиме Windows 2003 (Windows 2003 Domain Functional Level) и выше, можно поступать по-другому. Дело в том, что в Windows 2003 у каждой учетной записи появился еще один атрибут: lastLogontimeStamp. Этот атрибут был введен, как компромисс между желанием получить оценку последнего времени входа на основании опроса только одного DC и  нежеланием проводить слишком частую репликацию из-за изменения этого атрибута. Компромисс заключается в том, что значение этого атрибута обновляется не при каждом входе в систему (по умолчанию, значение атрибута будет обновлено не раньше, чем через 9 дней (и не позже, чем через 14 дней) относительно прошлого времени обновления этого атрибута). Подробно и очень хорошо об этом  написано здесь:  “The LastLogonTimeStamp Attribute” – “What it was designed for and how it works”

 И так, если Режим работы домена не ниже, чем Windows 2003, то мы будем пользоваться атрибутом lastLogontimeStamp. Делать это можно при помощи любых доступных нам  инструментов. Например, можно получить перечень давно неиспользовавшихся «учеток» компьютеров или пользователей при помощи хорошо известной команды dsquery:

 dsquery user -inactive 8

 эта команда выдаст на экран список пользователей, которые не выполняли вход в систему в течении 8 недель.

На PoSh аналогичного результата можно добиться  таким образом:

$InactiveDays = New-TimeSpan -days 56
$LastLogonTimeMark= (get-date) - $InactiveDays
#
Get-QADComputer -IncludedProperties lastLogonTimestamp| ?{$_.lastLogonTimestamp -lt $LastLogonTimeMark}

PowerShell хорош своей гибкостью и можно легко модифицировать вышеприведенный скрипт, сделав его более полезным:

$InactiveDays = New-TimeSpan -days 56
$LastLogonTimeMark= (get-date) - $InactiveDays
Get-QADComputer -IncludedProperties lastLogontimeStamp|
?{$_.lastLogontimeStamp -lt $LastLogonTimeMark}| sort lastLogontimeStamp| ft Name,lastLogontimeStamp, whenCreated –AutoSize

Результат работы этого скрипта:

Name            lastlogontimestamp  whencreated
----            ------------------  -----------
tmptest                             10.03.2010 15:50:19
org-20080513-1N 11.07.2008 14:26:36 13.05.2008 20:39:41
org-20070717-2  10.10.2008 11:32:00 10.10.2008 15:31:59
org-20080521-1N 17.10.2008 8:24:15  21.05.2008 16:15:25
org-20070516-01 19.12.2008 12:08:58 30.04.2008 13:22:44
org-SRV-TEST    20.05.2009 8:24:01  15.10.2008 10:06:44
org-20070528-1  04.08.2009 12:27:40 14.10.2008 17:45:56
org-20070724-1  10.08.2009 5:54:00  13.07.2009 9:33:35
org-20070726-1T 07.09.2009 12:59:59 05.06.2008 12:22:29
org-20071024-1T 29.09.2009 8:39:32  05.06.2008 16:12:50
org-20070801-1T 07.10.2009 13:36:24 06.10.2008 16:05:06
org-20070906-1T 15.10.2009 13:03:34 03.06.2008 13:13:18
org-20070715-1N 18.11.2009 12:43:42 29.05.2008 10:39:38

Теперь мы видим на экране список имен учетных записей компьютеров, отсортированных по времени последнего входа в систему, и время создания оных «учеток».

Кстати, вышеприведенный скрипт позволит вам обнаружить такие учетные записи, которые, возможно, ни разу не выполняли вход в систему. Для таких учетных записей атрибут lastLogonTimestamp не определен, и  в результатах работы скрипта для таких «учеток» мы вместо даты увидим пустое поле в соответствующем столбце. В этом случае нам пригодится информация из соседнего столбца (whenCreated), в котором указана дата создания учетной записи. Благодаря этой информации, мы можем сделать вывод о том, была ли «учетка» создана давным-давно и позабыта-позаброшена или, наоборот, она настолько молода, что ей еще никто не успел воспользоваться.

Ну, и наконец, было бы неплохо убрать из результатов вывода отключенные учетные записи (disabled accounts). И, если в случае с учетными записями пользователя сделать это не трудно, благодаря тому, что у объектов типа Quest.ActiveRoles.ArsPowerShellSnapIn.Data.ArsUserObject имеется свойство AccountIsDisabled, то для объектов типа Quest.ActiveRoles.ArsPowerShellSnapIn.Data.ArsComputerObject такого свойства не определено и придется немного повозиться. В AD информация о том, отключена учетная запись или нет, хранится во втором бите многобитового атрибута UserAccountControl. Для своего удобства мы можем добавить к результирующему объекту нашего скрипта поле, значение которого будет сформировано на основе содержимого вышеназванного бита. Добавление нового поля объекта можно выполнить при помощи оператора select-object:

…| select …,  @{Name=”Disabled”; Exp={$_.useraccountcontrol -band 2}}

В конечном счете, получим такой скрипт:

$InactiveDays = New-TimeSpan -days 56
$LastLogonTimeMark= (get-date) - $InactiveDays
#
Get-QADComputer -IncludedProperties lastLogontimeStamp, useraccountcontrol|
?{$_.lastLogontimeStamp -lt $LastLogonTimeMark}|
select name, lastLogontimeStamp, whenCreated, @{Name="Disabled"; Exp={$_.useraccountcontrol -band 2}}|
?{$_.Disabled -eq 0}|
sort lastLogontimeStamp| ft Name,lastLogonTimeStamp, whenCreated, Disabled -AutoSize

Скрипт вышел универсальным, если мы заменим Get-QADComputer на Get-QADUser, то получим такой же отчет, но не по компьютерам, а по пользователям.

Будут вопросы – пишите письма, искренне ваш и «все такое» ;).

34 Comments

  1. А откуда береться команда Get-QADComputer, Get-ADComputer есть а той нету. у меня ругаеться на данный скрипт

  2. Прошу прощения а вы вывод не делали в csv фаил, для того чтоп в дальнейшем проанализировать

  3. А если скрипт научить еще и перемещать такие учетки в другое OU да еще и лочить их, то цены скрипту не будет.

  4. вот так у меня
    Name lastLogontimeStamp whenCreated Disabled
    —- —————— ———– ——–
    TSO6 22.03.2007 14:09:51 0
    BOSS 15.03.2010 12:08:39 0
    KAFIYA3 20.02.2007 12:02:13 0
    KAFGTP2 19.03.2004 15:01:33 0
    BIBLIOTEKA3 19.01.2009 16:16:46 0
    SMD1 02.06.2005 10:07:10 0

    • мне пришло в голову только следующее: либо была допущена ошибка при копипасте скритпта, либо этого атрибута нет у вас в схеме AD. Какова версия ОС на ваших DC? Каков Функциональный уровень домена?

  5. установил acctinfo.dll lastLogontimeStamp в закладке показывается
    домен уровня 2003
    правда перевел только вчера может ещё параметр не реплицировался

    • если ваши dc не разбросаны по сайтам, то репликация должна была закончиться за 15 минут, как максимум. Проверьте сначала, как работает репликация. Выполните
      netdiag /q
      dcdiag /q
      каковы результаты ?

  6. Pingback: Ищем не используемые учетные записи пользователей и компьютеров. Lastlogon vs LastLogonTimeStamp « IT Блог Андрея Лукьянова

  7. Pingback: Ищем неиспользуемые учетные записи пользователей и компьютеров. Lastlogon vs LastLogonTimeStamp « IT Блог Андрея Лукьянова

  8. У меня скрипт дает ошибку:
    Неверный аргумент для оператора “-lt”: Не удается сравнить “129902525360352297” с “04.07.2012 10:30:06”. Ошибка: “Не удается преобразовать значение “04.07.2012 10:30:06” в тип “System.Int64”. Ошибка: “Недопустимое приведение “DateTime” к “Int64″.””.
    D:\script\ps\LastLogonDate.ps1:6 знак:28
    + ?{$_.lastLogontimeStamp -lt <<<< $LastLogonTimeMark}| sort lastLogontimeStamp| ft Name,lastLogontimeStamp, whenCreated –AutoSize
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : BadOperatorArgument

        • Вот в этом и проблема. В моем скрипте используются командлеты от Quest-software. Вероятно, в случае использования штатных командлетов значение атрибут lastLogontimeStamp возвращается в “сыром” виде (по крайней мере, как показывает ваше сообщение об ошибке, не в формате DateTime) и вам нужно самостоятельно привести сравниваемые переменные к единому формату. Вот здесь используется следующее преобразование перед сравнением:

          $lastLogon = (get-date).adddays(-90).ToFileTime()
          Get-ADComputer -filter {lastLogonTimestamp -gt $lastLogon}

              • Я изменил скрипт:
                $InactiveDays = New-TimeSpan -days 56
                $LastLogonTimeMark = (get-date) – $InactiveDays
                Get-ADComputer -Filter * -Properties lastLogontimeStamp,whenCreated | `
                ?{$_.lastLogonTimestamp -lt $LastLogonTimeMark.ToFileTime()}| sort lastLogonTimestamp| ft Name,lastLogontimeStamp, whenCreated –AutoSize
                Вывод после отработки скрипта:

                Name lastLogontimeStamp whenCreated
                —- —————— ———–
                WXP-DPOAKR-1 129715715180905865 13.02.2006 13:42:41
                WXP-PVL-005 129800561611791282 02.06.2011 11:49:22
                WXP-USH-1 129826044352305253 16.06.2011 11:12:30
                WXP-RO2-1695 129827334861224062 18.05.2011 18:13:46
                Как вывести в поле lastLogontimeStamp нормальный вид даты?
                Я только начинаю изучать PS, если что, заранее прошу прошения.

                • По всей видимости нужно использовать метод DateTime обратный к ToFileTime: FromFileTime

                  попробуйте так:

                  $InactiveDays = New-TimeSpan -days 56
                  $LastLogonTimeMark = (get-date) – $InactiveDays
                  Get-ADComputer -Filter * -Properties lastLogontimeStamp,whenCreated | `
                  ?{[DateTime]::FromFileTime($_.lastLogonTimestamp) -lt $LastLogonTimeMark}| sort lastLogonTimestamp| ft Name,lastLogontimeStamp, whenCreated –AutoSize

    • Ага, сравниниваются преобразованные значения, а выводятся непреобразованные. Тогда попробуйте так:
      $InactiveDays = New-TimeSpan -days 56
      $LastLogonTimeMark = (get-date) – $InactiveDays
      Get-ADComputer -Filter * -Properties lastLogontimeStamp,whenCreated |`
      ?{[DateTime]::FromFileTime($_.lastLogonTimestamp) -lt $LastLogonTimeMark}|`
      select Name, @{Name=”LastLogon”;Expression={[datetime]::FromFileTime($_.lastLogontimeStamp)}}, whenCreated|`
      sort LastLogon| ft Name,LastLogon, whenCreated –AutoSize

  9. Pingback: Ищем неиспользуемые учетные записи пользователей и компьютеров. Lastlogon vs LastLogonTimeStamp

  10. Подскажите пожалуйста скрипт, мне нужно список учетных записей, заблокированных, логинившихся 3 месяца назад и ранее. И чтоб присутствовали атрибуты displayName ,Title,Department ,company, lastLogonTimestamp.

        • $InactiveDays = New-TimeSpan -days 90
          $LastLogonTimeMark= (get-date) - $InactiveDays
          #
          Get-QADComputer -IncludedProperties lastLogontimeStamp, Title, displayName, Department, company, useraccountcontrol|
          ?{$_.lastLogontimeStamp -lt $LastLogonTimeMark}|
          select name, lastLogontimeStamp, whenCreated, @{Name="Disabled"; Exp={$_.useraccountcontrol -band 2}}|
          ?{$_.Disabled -eq 0}|
          sort lastLogontimeStamp| ft Name,lastLogontimeStamp, Title, displayName, Department, company, whenCreated, Disabled -AutoSize

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.