Лучший отвечающий
Список компов-трупов домена.

Вопрос
-
Всем привет.
Пытался вычистить AD от старых учетных записей компов используя связку dsquery|dsrm. Однако несмотря на все заявления MS "труба" в этих коммандах лично у меня не заработала (писал по этому поводу в форум). Т.е., для того чтобы удалить все аккаунты компов, неактивных более 3 месяцев пишу в cmd на DC:
dsquery computer -inactive 12 | dsrm
и получаю "болт".
В конце концов решил реализовать данную операцию скриптом. Вот тут то и возник вопрос: какой атрибут объекта "Computer" можно считать чтобы получить время последнего включения компа ? Поскольку учетная запись компа, это то же самое, что учетная запись пользователя с нулевым паролем пытаюсь считывать lastLogon следующим скриптом:
//JScript
var objConnection = WScript.CreateObject("ADODB.Connection");
var objCommand = WScript.CreateObject("ADODB.Command");objConnection.Provider = "ADsDSOObject";
objConnection.Open("Active Directory Provider");
objCommand.ActiveConnection = objConnection;objCommand.CommandText = "SELECT Name, lastLogon from 'LDAP://DC=local,DC=domain,DC=ru' Where objectClass='computer'";
var objRS = objCommand.Execute;
objRS.MoveFirst;while(!objRS.EOF)
{
WScript.Echo(objRS.Fields("Name").Value);
WScript.Echo("Last logon: " + objRS.Fields("lastLogon").Value);
WScript.Echo();
objRS.MoveNext;
}Однако в результате значение lastLogon пустое. Смотрю в ADSI Editor - значение lastLogon либо ноль, либо конкретная величина (тип - длинное целое).
Никто не подскажет - как в скрипте определить время последнего включения компа в составе домена ?
22 октября 2007 г. 13:02
Ответы
-
sie написано: Sergey Bezpalov написано: Скрипт это хорошо...
"dsquery computer -inactive 12 | dsmove -newparent OU=Marketing,DC=NW Traders,DC=Com"
Странно, но dsmove в связке с dsquery отказывается работать, а жаль...
А что странного? dsmove ожидает один DN, а получает от dsquery целый список!
Чтобы заработало надо заключить dsmove в рапер, который будет вызывать dsmove для каждого DN выдаваемого dsquery.
Работают вместе, вот пример перемещения ПК по маске в имени.
C:\WINDOWS\system32\cmd.exe /C for /f %i in ('dsquery computer "cn=computers,dc=test,dc=com" -name xx-*') do dsmove %i -newparent "ou=computers_new,dc=test,dc=com"- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:45
13 ноября 2007 г. 6:51 -
- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:44
22 октября 2007 г. 13:57Модератор -
Всем спасибо за ответы. Но я все-таки довел свою идею до конца, поскольку:
1) нет гарантии, что в среди старых аккаунтов не попадется какой-нить внезапно оживший через долгое время, поэтому уж лучще аккаунты не удалять, а перемещать в определенную орг. единицу, из которой уж после тотальной проверки аккаунт, если он действительно "труп" будет удален.
2) скрипт, все равно, дает большую гибкость чем dsquery, dsrm и т.п.
Привожу свой скрипт, написанный на JScript, который анализирует параметр lastLogon аккаунта компьютера, и если этот параметр null (т.е. комп уж точно неактивный долгое время) - аккаунт перемещается в орг. единицу oldComputers. Также по традиции закинул этот скрипт на www.sysadmins.ru
/**************************************************************************
Имя: oldcmps.js
Комментарий: Скрипт поиска и перемещения неактивных компьютеров в орг. единцу.
Язык: JScript
Автор: Андрей Мишечкин
**************************************************************************/
var NormalComputers = 1; //счетчик количества активных компьютеров
var DeadComputers = 1; //счетчик количества неактивных компьюетров
var IsError; //Флаг ошибки
var objLILastLogon; //объект длинного целого
var objConnection = WScript.CreateObject("ADODB.Connection"); //объект ODBC-соединения
var objCommand = WScript.CreateObject("ADODB.Command"); //объект запроса
var fso = WScript.CreateObject("Scripting.FileSystemObject"); //объект файловой системы
var objOldComputersOU = GetObject("LDAP://OU=oldComputers,DC=local,DC=domain,DC=ru"); //ADSI-объект целевой орг. единицы
var NCReport = fso.CreateTextFile("ncreport.txt",true); //файл списка активных компьютеров
var DCReport = fso.CreateTextFile("dcreport.txt",true); //файл списка неактивных компьютеров
var objNewComputer; //объект перемещаемого компьютера//Установка параметров соединения с Active Directory
objConnection.Provider = "ADsDSOObject";
objConnection.Open("Active Directory Provider");
objCommand.ActiveConnection = objConnection;
//Строка запроса к LDAP
objCommand.CommandText = "SELECT Name, distinguishedName from 'LDAP://DC=local,DC=domain,DC=ru' Where objectClass='computer'";
//Выполнение запроса,
var objRS = objCommand.Execute;
//////////////////////////////////////////////////////////////
// Перебор элементов отчета. Получение результатов //
/////////////////////////////////////////////////////////////
//Установка на начало выборки
objRS.MoveFirst;
//Цикл перебора всех элементов выборки
while(!objRS.EOF)
{
IsError = false;
objComputer = GetObject("LDAP://" + objRS.Fields("distinguishedName").Value); //получение ADSI-объекта компьютера
//Считывание времени последнего входа в домен с обработкой ошибок.
try
{
objLILastLogon = objComputer.Get("lastLogon");
}
catch(e)
{
IsError = true;
}
//lastLogon - не null. Компьютер - активный
if(!IsError)
{
//Вывод результата на экран
WScript.Echo(objRS.Fields("distinguishedName").Value);
WScript.Echo(objLILastLogon.HighPart);
//Запись результата в файл отчета
NCReport.WriteLine("Computer: " + objRS.Fields("Name").Value);
NormalComputers++;
}
//lastLogon - null. Компьютер - неактивный
else
{
//Вывод результата на экран
WScript.Echo(objRS.Fields("distinguishedName").Value);
WScript.Echo("lastLogon is null");
//Вывод результата на экран
DCReport.WriteLine("Computer: " + objRS.Fields("Name").Value);
//Перемещение компьютера в специальную орг. единицу
WScript.Echo("Moving computer to OldComputers OU");
try
{
objNewComputer = objOldComputersOU.MoveHere("LDAP://"+objRS.Fields("distinguishedName").Value, "CN="+objRS.Fields("Name").Value);
}
catch(e)
{
WScript.Echo("Error. " + e.description);
}
DeadComputers++;
}
WScript.Echo();
objRS.MoveNext;
}
//////////////////////////////////////////////////////////////////////////////
NCReport.WriteLine("Total: " + NormalComputers); //Запись в отчет общего количества активных компьюетров
DCReport.WriteLine("Total: " + DeadComputers); //Запись в отчет общего количества неактивных компьютеров
//Закрытие файлов отчета
NCReport.Close();
DCReport.Close();- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:45
23 октября 2007 г. 13:17 -
-
Вариант на PowerShell, с применением AD Cmdlets (бесплатны, брать тут - http://www.quest.com/activeroles-server/arms.aspx ).
$timeX
= get-date$timeX
= $timex.AddMonths(-3)Get-QADComputer
-IncludedProperties "Name","LastLogon" `-SearchRoot "ou=computers,ou=eaf,dc=mydomain,dc=local" |
where {$_.lastLogon -le $timex} |
Move-QADObject -NewParentContainer "ou=delete,dc=mydomain,dc=local"
- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:45
29 ноября 2007 г. 5:42Модератор -
Если так обернуть - всё работает:
PowerShelldsquery computer "ou=Computers,dc=Domain,dc=Local" -inactive 30 |
foreach {dsmove $_ -newparent "OU=Trash,DC=Domain,DC=Local"}
Доброго всем дня!
Тема была неактивной целы год :) но вот потребовалось и мне :) выполнить эту задачу.
Самое не веселое в том, что у меня в названии подразделений есть символ " и это сразу сбивает dsmove с толку, не работает :(
Предлагаю еще одну реализацию, не самый идеальный вариант, но работает:
$searchroot = 'OU=Компьютеры,OU=ОАО \"Суперкомплюс\",DC=Spk,DC=local' #вот тут вся проблема, символ " мешает нормальной работе
dsquery computer $searchroot -inactive 40 -limit 0 -o rdn >> result.txt$data = gc .\result.txt
foreach ($dataitem in $data)
{
$dataitem = $dataitem.TrimStart('"')
$dataitem = $dataitem.TrimEnd('"')$OldPcToMove = Get-QADComputer -SearchRoot $searchroot -name $dataitem
move-QADObject $OldPcToMove -NewParentContainer 'OU=Inactive,OU=Компьютеры,OU=ОАО \"Суперкомплюс\",DC=Spk,DC=local'}
del result.txt
MCSE, MCSA:Messaging (2000/2003) MCTS: Exchange 2007- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:55
4 июня 2009 г. 8:15 -
я побыстрому себе сделал так:
$DCs = Get-QADComputer -ComputerRole DomainController function Get-LastLogon ($comp) { BEGIN { $complatest = $null } PROCESS { foreach($DC in $DCs) { $CompInDC = Get-QADComputer $comp -Service $DC.Name -IncludedProperties lastlogon if($CompInDC.lastlogon -and (!$complatest -or ($CompInDC.lastlogon -gt $complatest.lastlogon))) { $complatest = $CompInDC } } } END { $complatest } } $computers = Get-QADComputer -ComputerRole Member foreach($computer in $computers) { Get-LastLogon $computer | where{$_.lastlogon -lt (Get-Date).AddMonths(-6)} | Remove-QADObject -watif
}
Нужны Командлеты QuestAD
Грамотная постановка вопроса - уже 50% решения. Не забывайте помечать ответы как полезные или как ответ, если они Вам помогли.- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:55
18 июня 2009 г. 9:45
Все ответы
-
- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:44
22 октября 2007 г. 13:57Модератор -
А что dsquery computer -inactive 12 | dsrm -noprompt не проходит?
22 октября 2007 г. 14:26 -
Всем спасибо за ответы. Но я все-таки довел свою идею до конца, поскольку:
1) нет гарантии, что в среди старых аккаунтов не попадется какой-нить внезапно оживший через долгое время, поэтому уж лучще аккаунты не удалять, а перемещать в определенную орг. единицу, из которой уж после тотальной проверки аккаунт, если он действительно "труп" будет удален.
2) скрипт, все равно, дает большую гибкость чем dsquery, dsrm и т.п.
Привожу свой скрипт, написанный на JScript, который анализирует параметр lastLogon аккаунта компьютера, и если этот параметр null (т.е. комп уж точно неактивный долгое время) - аккаунт перемещается в орг. единицу oldComputers. Также по традиции закинул этот скрипт на www.sysadmins.ru
/**************************************************************************
Имя: oldcmps.js
Комментарий: Скрипт поиска и перемещения неактивных компьютеров в орг. единцу.
Язык: JScript
Автор: Андрей Мишечкин
**************************************************************************/
var NormalComputers = 1; //счетчик количества активных компьютеров
var DeadComputers = 1; //счетчик количества неактивных компьюетров
var IsError; //Флаг ошибки
var objLILastLogon; //объект длинного целого
var objConnection = WScript.CreateObject("ADODB.Connection"); //объект ODBC-соединения
var objCommand = WScript.CreateObject("ADODB.Command"); //объект запроса
var fso = WScript.CreateObject("Scripting.FileSystemObject"); //объект файловой системы
var objOldComputersOU = GetObject("LDAP://OU=oldComputers,DC=local,DC=domain,DC=ru"); //ADSI-объект целевой орг. единицы
var NCReport = fso.CreateTextFile("ncreport.txt",true); //файл списка активных компьютеров
var DCReport = fso.CreateTextFile("dcreport.txt",true); //файл списка неактивных компьютеров
var objNewComputer; //объект перемещаемого компьютера//Установка параметров соединения с Active Directory
objConnection.Provider = "ADsDSOObject";
objConnection.Open("Active Directory Provider");
objCommand.ActiveConnection = objConnection;
//Строка запроса к LDAP
objCommand.CommandText = "SELECT Name, distinguishedName from 'LDAP://DC=local,DC=domain,DC=ru' Where objectClass='computer'";
//Выполнение запроса,
var objRS = objCommand.Execute;
//////////////////////////////////////////////////////////////
// Перебор элементов отчета. Получение результатов //
/////////////////////////////////////////////////////////////
//Установка на начало выборки
objRS.MoveFirst;
//Цикл перебора всех элементов выборки
while(!objRS.EOF)
{
IsError = false;
objComputer = GetObject("LDAP://" + objRS.Fields("distinguishedName").Value); //получение ADSI-объекта компьютера
//Считывание времени последнего входа в домен с обработкой ошибок.
try
{
objLILastLogon = objComputer.Get("lastLogon");
}
catch(e)
{
IsError = true;
}
//lastLogon - не null. Компьютер - активный
if(!IsError)
{
//Вывод результата на экран
WScript.Echo(objRS.Fields("distinguishedName").Value);
WScript.Echo(objLILastLogon.HighPart);
//Запись результата в файл отчета
NCReport.WriteLine("Computer: " + objRS.Fields("Name").Value);
NormalComputers++;
}
//lastLogon - null. Компьютер - неактивный
else
{
//Вывод результата на экран
WScript.Echo(objRS.Fields("distinguishedName").Value);
WScript.Echo("lastLogon is null");
//Вывод результата на экран
DCReport.WriteLine("Computer: " + objRS.Fields("Name").Value);
//Перемещение компьютера в специальную орг. единицу
WScript.Echo("Moving computer to OldComputers OU");
try
{
objNewComputer = objOldComputersOU.MoveHere("LDAP://"+objRS.Fields("distinguishedName").Value, "CN="+objRS.Fields("Name").Value);
}
catch(e)
{
WScript.Echo("Error. " + e.description);
}
DeadComputers++;
}
WScript.Echo();
objRS.MoveNext;
}
//////////////////////////////////////////////////////////////////////////////
NCReport.WriteLine("Total: " + NormalComputers); //Запись в отчет общего количества активных компьюетров
DCReport.WriteLine("Total: " + DeadComputers); //Запись в отчет общего количества неактивных компьютеров
//Закрытие файлов отчета
NCReport.Close();
DCReport.Close();- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:45
23 октября 2007 г. 13:17 -
Скрипт это хорошо...
"dsquery computer -inactive 12 | dsmove -newparent OU=Marketing,DC=NW Traders,DC=Com"
Странно, но dsmove в связке с dsquery отказывается работать, а жаль...
23 октября 2007 г. 16:25 -
Sergey Bezpalov написано: >Странно, но dsmove в связке с dsquery отказывается работать, а жаль...
))))
Вот и я говорю, что скрипт - это хорошо
Если бы в dsXXXX все работало как надо - не стал бы я со скриптом возиться.
24 октября 2007 г. 6:59 -
Sergey Bezpalov написано: Скрипт это хорошо...
"dsquery computer -inactive 12 | dsmove -newparent OU=Marketing,DC=NW Traders,DC=Com"
Странно, но dsmove в связке с dsquery отказывается работать, а жаль...
А что странного? dsmove ожидает один DN, а получает от dsquery целый список!
Чтобы заработало надо заключить dsmove в рапер, который будет вызывать dsmove для каждого DN выдаваемого dsquery.
25 октября 2007 г. 9:36Модератор -
Это конечно понятно, непонятно почему разработчик не предусмотрел возможность совместного использования, как он сделал это например dsrm -noprompt.
25 октября 2007 г. 10:00 -
-
sie написано: Sergey Bezpalov написано: Скрипт это хорошо...
"dsquery computer -inactive 12 | dsmove -newparent OU=Marketing,DC=NW Traders,DC=Com"
Странно, но dsmove в связке с dsquery отказывается работать, а жаль...
А что странного? dsmove ожидает один DN, а получает от dsquery целый список!
Чтобы заработало надо заключить dsmove в рапер, который будет вызывать dsmove для каждого DN выдаваемого dsquery.
Работают вместе, вот пример перемещения ПК по маске в имени.
C:\WINDOWS\system32\cmd.exe /C for /f %i in ('dsquery computer "cn=computers,dc=test,dc=com" -name xx-*') do dsmove %i -newparent "ou=computers_new,dc=test,dc=com"- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:45
13 ноября 2007 г. 6:51 -
Согласен !-) а вот про цикл в коммандной строке я и не подумал...
13 ноября 2007 г. 8:03 -
Вариант на PowerShell, с применением AD Cmdlets (бесплатны, брать тут - http://www.quest.com/activeroles-server/arms.aspx ).
$timeX
= get-date$timeX
= $timex.AddMonths(-3)Get-QADComputer
-IncludedProperties "Name","LastLogon" `-SearchRoot "ou=computers,ou=eaf,dc=mydomain,dc=local" |
where {$_.lastLogon -le $timex} |
Move-QADObject -NewParentContainer "ou=delete,dc=mydomain,dc=local"
- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:45
29 ноября 2007 г. 5:42Модератор -
все это хорошо, но если сайтов несколько и несколько контроллеров домена, то информация о lastLogon - недостоверна.
Все компы из другого сайта с незаполненным свойством lastLogon, а те которые переехали на другой сайт - с неправильной датой. Так можно грохнуть кучу живых компов.
6 февраля 2008 г. 16:25 -
Совершенно верно! lastLogon постоянно вводит в заблуждение
Атрибут не реплицируется! В соседней теме есть мой подробный пост про это.
7 февраля 2008 г. 3:48Модератор -
Что-то не работает такая команда
C:\WINDOWS\system32\cmd.exe /C for /f %i in ('dsquery computer "ou=Компьютеры,ou=Группа разработки и внедрения,ou=IT,ou=Users,dc=domain,dc=lz" -inactive 12') do dsmove %i -newparent "ou=Заблокированые компьютеры,dc=domain,dc=lz"
Пишет "Непредвиденное появление -newparent".7 февраля 2008 г. 14:22 -
To Sie: Какой выход вы видите? И если можно, ссылку на этот самый пост или хотя бы на название темы которую вы упоминаете
8 февраля 2008 г. 17:05 -
http://forums.microsoft.com/TechNet-RU/ShowPost.aspx?PostID=2351394&SiteID=40
sie написано: Атрибут lastlogon не реплицируемый! Т.е. надо выполнять поиск по всем DC!!! http://msdn2.microsoft.com/en-us/library/ms676823.aspx
Есть атрибут lastLogonTimeStamp Он реплицирутся, но редко (раз в 9 - 14 дней)http://msdn2.microsoft.com/en-us/library/ms676824.aspx
Можно посмотреть это
И это
Хороший пример http://www.rlmueller.net/Programs/LastLogon.txt
Куча примеров с запросами http://www.petri.co.il/ldap_search_samples_for_windows_2003_and_exchange.htm
Здесь List Bad Password Count
Атрибут тоже не реплицируемый! http://msdn2.microsoft.com/en-us/library/ms675244(VS.85).aspxЕще одна ссылка на последок http://www.rlmueller.net/freecode4.htm
11 февраля 2008 г. 7:01Модератор -
Спасибо, буду читать.
11 февраля 2008 г. 12:20 -
Vitos написано: Что-то не работает такая команда
C:\WINDOWS\system32\cmd.exe /C for /f %i in ('dsquery computer "ou=Компьютеры,ou=Группа разработки и внедрения,ou=IT,ou=Users,dc=domain,dc=lz" -inactive 12') do dsmove %i -newparent "ou=Заблокированые компьютеры,dc=domain,dc=lz"
Пишет "Непредвиденное появление -newparent".
Как выяснилось у dsmove какие то проблемы с получением нескольких элементов из конвейера...
http://www.myitforum.com/forums/m_75952/mpage_1/key_dsquery%252Cdsmod/tm.htm#75952
http://forums.techarena.in/showthread.php?t=446955
Если так обернуть - всё работает:
PowerShelldsquery computer "ou=Computers,dc=Domain,dc=Local" -inactive 30 |
foreach {dsmove $_ -newparent "OU=Trash,DC=Domain,DC=Local"}
13 февраля 2008 г. 6:41Модератор -
Если так обернуть - всё работает:
PowerShelldsquery computer "ou=Computers,dc=Domain,dc=Local" -inactive 30 |
foreach {dsmove $_ -newparent "OU=Trash,DC=Domain,DC=Local"}
Доброго всем дня!
Тема была неактивной целы год :) но вот потребовалось и мне :) выполнить эту задачу.
Самое не веселое в том, что у меня в названии подразделений есть символ " и это сразу сбивает dsmove с толку, не работает :(
Предлагаю еще одну реализацию, не самый идеальный вариант, но работает:
$searchroot = 'OU=Компьютеры,OU=ОАО \"Суперкомплюс\",DC=Spk,DC=local' #вот тут вся проблема, символ " мешает нормальной работе
dsquery computer $searchroot -inactive 40 -limit 0 -o rdn >> result.txt$data = gc .\result.txt
foreach ($dataitem in $data)
{
$dataitem = $dataitem.TrimStart('"')
$dataitem = $dataitem.TrimEnd('"')$OldPcToMove = Get-QADComputer -SearchRoot $searchroot -name $dataitem
move-QADObject $OldPcToMove -NewParentContainer 'OU=Inactive,OU=Компьютеры,OU=ОАО \"Суперкомплюс\",DC=Spk,DC=local'}
del result.txt
MCSE, MCSA:Messaging (2000/2003) MCTS: Exchange 2007- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:55
4 июня 2009 г. 8:15 -
я побыстрому себе сделал так:
$DCs = Get-QADComputer -ComputerRole DomainController function Get-LastLogon ($comp) { BEGIN { $complatest = $null } PROCESS { foreach($DC in $DCs) { $CompInDC = Get-QADComputer $comp -Service $DC.Name -IncludedProperties lastlogon if($CompInDC.lastlogon -and (!$complatest -or ($CompInDC.lastlogon -gt $complatest.lastlogon))) { $complatest = $CompInDC } } } END { $complatest } } $computers = Get-QADComputer -ComputerRole Member foreach($computer in $computers) { Get-LastLogon $computer | where{$_.lastlogon -lt (Get-Date).AddMonths(-6)} | Remove-QADObject -watif
}
Нужны Командлеты QuestAD
Грамотная постановка вопроса - уже 50% решения. Не забывайте помечать ответы как полезные или как ответ, если они Вам помогли.- Помечено в качестве ответа Vasily GusevModerator 2 июля 2009 г. 6:55
18 июня 2009 г. 9:45 -
Надо -whatif :) Внимательнее с этим параметром :)
AKA Xaegr, MCSE: Security, Messaging; MCITP: Server\Enterprise Administrator; Блог: http://xaegr.wordpress.com19 июня 2009 г. 4:08Модератор -
Надо -whatif :) Внимательнее с этим параметром :)
AKA Xaegr, MCSE: Security, Messaging; MCITP: Server\Enterprise Administrator; Блог: http://xaegr.wordpress.com
Да, виноват. Наплевательское отношение к грамматике....
Грамотная постановка вопроса - уже 50% решения. Не забывайте помечать ответы как полезные или как ответ, если они Вам помогли.19 июня 2009 г. 5:42 -
Спасибо всем участникам обсуждения за помощь в нахождении решения!
В итоге сделал на PowerShell c использованием компонентов quest так:
Add-PSSnapin Quest.ActiveRoles.ADManagement
Connect-QADService -Service 'DC1.sitename.domen'
Get-QADComputer -NotLoggedOnFor 90 | where {$_.Name -notlike "W*"} | move-QADObject -NewParentContainer 'sitename.domen/NotUsedComputers'В общем сначала коннектимся к доменконтроллеру DC1, затем ищем все компы, на которые не логинились более 90 дней, из выборки исключается всё, что начинается на W (их перемещать не нужно), а остальное перемещается в специальный раздел АД.
Запускать скрипт можно так: powershell -executionPolicy Unrestricted c:\MoveLostComps.ps1
4 февраля 2014 г. 4:52