Оптимизировать фильтр Get-ADUser
В AD я пытаюсь определить учетные записи пользователей, в которых одно и то же значение EmployeeID содержится в 2 или более записях. Ниже приведен мой фрагмент кода (кредит: я использую Show-Progressопределенную здесь функцию ), и Get-ADUserодной команде потребовалось более 2 часов, чтобы получить все записи. Остальные шаги (2–5) были выполнены довольно быстро. Завершая работу, я пытаюсь понять, можно ли сделать это более эффективно с помощью PowerShell.
Get-ADUser -LDAPFilter "(&(ObjectCategory=Person)(objectclass=user)(employeeid=*))" -Properties $properties -Server $server_AD_GC -ResultPageSize 1000 |
# *ISSUE HERE*
# The Get-ADUser extract process seems to work very slow.
# However, it is important to note that the above command will be retrieving more than 200K records
# NOTE: I've inferred that employeeid is an indexed attribute and is replicated to GlobalCatalogs and hence have used it in the filter
Show-Progress -Activity "(1/5) Getting AD Users ..." |
select $selectPropsList -OutVariable results_UsersBaseSet | Group-Object EmployeeID | Show-Progress -Activity "(2/5) Grouping on EmployeeID ..." | ? { $_.Count -gt 1 } |
Show-Progress -Activity "(3/5) Filtering only dup EmpID records ..." |
select -Exp Group |
Show-Progress -Activity "(4/5) UnGrouping ..." |
Export-Csv "C:\Users\me\op_GetADUser_w_EmpID_Dupes_EntireForest - $([datetime]::Now.ToString("MM-dd-yyyy_hhmmss")).csv" -NoTypeInformation |
Show-Progress -Activity "(5/5) Exporting ..." |
Out-Null
PS: Я также попытался сначала экспортировать все учетные записи пользователей в файл csv, а затем выполнить постобработку с помощью Excel, но мне пришлось нахмуриться из-за размера набора данных, и это было как время, так и память.
Любое предложение приветствуется.
Ответы
Поскольку мы не знаем, что входит в $propertiesили $selectPropsList, ваш вопрос на самом деле только о том, чтобы выяснить, каким пользователям был выдан один и тот же EmployeeID, верно?
По умолчанию Get-ADUser уже возвращает следующие свойства:
DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname,UserPrincipalName
Так что все, что вам нужно дополнительно, - это идентификатор EmployeeID. Попытка собрать ОЧЕНЬ много свойств действительно замедляет работу, поэтому сведение к минимуму помогает ускорить процесс.
Затем, используя Show-Progressскрипт, с которым вы связались, вы значительно замедляете его выполнение. Вам действительно нужен индикатор выполнения? Почему бы просто не записать строки с шагами активности прямо в консоль?
Кроме того, соединение всего вместе не помогает и в отделе скорости.
$server_AD_GC = 'YourServer' $selectPropsList = 'EmployeeID', 'Name', 'SamAccountName', 'Enabled'
$outFile = "C:\Users\me\op_GetADUser_w_EmpID_Dupes_EntireForest - $([datetime]::Now.ToString("MM-dd-yyyy_hhmmss")).csv"
Write-Host "Step (1/4) Getting AD Users ..."
$users = Get-ADUser -Filter "EmployeeID -like '*'" -Properties EmployeeID -Server $server_AD_GC -ResultPageSize 1000
Write-Host "Step (2/4) Grouping on EmployeeID ..."
$dupes = $users | Group-Object -Property EmployeeID | Where-Object { $_.Count -gt 1 } Write-Host "Step (3/4) Collecting duplicates ..." $result = foreach ($group in $dupes) {
$group.Group | Select-Object $selectPropsList
}
Write-Host "Step (4/4) Exporting ..."
$result | Export-Csv -Path $outFile -NoTypeInformation
Write-Host "All done" -ForegroundColor Green
PS Get-ADUserуже возвращает только пользовательские объекты, поэтому фильтр LDAP не нужен (ObjectCategory=Person)(objectclass=user). Использование -Filter "EmployeeID -like '*'", вероятно, быстрее
Этот ответ дополняет полезный ответ Тео и фокусируется на демонстрации прогресса во время операции :
Связана Show-Progressфункция , которая является последней в этой записи:
имеет явную ошибку в том, что он не передает входные данные конвейера (соответствующая строка случайно закомментирована)
концептуально ошибочен в том, что он не использует
processблок, что означает, что весь ввод конвейера собирается в первую очередь , прежде чем он будет обработан, что опровергает идею индикатора выполнения.
Следовательно, ваши
Show-Progressвызовы не будут показывать прогресс, пока предыдущая команда в конвейере не выведет весь свой вывод. Простая альтернатива - разбить конвейер на отдельные команды и просто выдавать одно сообщение о ходе выполнения перед каждой командой, объявляя о следующем этапе обработки (а не о ходе выполнения для каждого объекта), как показано в ответе Тео.Как правило, нет способа показать ход внутренней обработки команды, только ход вывода команды (многообъектного) .
Самый простой способ сделать это - ForEach-Objectпозвонить по телефону
Write-Progress, но при этом возникают две проблемы:Чтобы отобразить индикатор выполнения в процентах , вам необходимо знать, сколько всего объектов будет , что вы должны определить заранее , потому что конвейер не может знать, сколько объектов он получит; ваш единственный вариант - сначала собрать весь вывод (или найти другой способ его подсчета), а затем использовать собранный вывод в качестве ввода конвейера, используя количество объектов в качестве основы для вычисления значения, которое нужно передать
Write-Progress -PerCentComplete.Вызов
Write-Progressдля каждого объекта , полученным приведут к значительному замедлению общей обработки; компромисс заключается в том, чтобы вызывать его только для каждых N объектов, как показано в этом ответе ; подход там может быть заключен в правильно реализованную функцию, например,Show-Progressкоторая требует передачи общего количества объектов в качестве аргумента и выполняет надлежащую потоковую обработку входных объектов (черезprocessблок); Тем не менее, простое использование кода PowerShell для передачи входных объектов обходится дорого.
Заключение:
Отображение процентного выполнения имеет две неотъемлемые проблемы :
Они требуют, чтобы вы знали общее количество объектов для обработки заранее (конвейер не имеет возможности узнать, сколько объектов пройдет через него):
Либо: Собрать все объекты процесса в памяти , заранее , если это возможно; количество элементов в коллекции может служить основой для вычислений процента выполнения. Это может не подходить для очень больших наборов входных данных.
Или: заранее выполните дополнительный этап обработки, который просто подсчитывает все объекты без фактического их извлечения. Это может оказаться непрактичным с точки зрения добавленного дополнительного времени обработки.
Обработка объекта за объектом в коде PowerShell - с помощью ForEach-Objectили расширенного сценария / функции - по своей сути медленная.
- Вы можете несколько смягчить это, ограничив Write-Progressвызовы для всех N объектов, как показано в этом ответе.
В целом это компромисс между скоростью обработки и возможностью показывать конечному пользователю процент выполнения .