Otimize o filtro Get-ADUser
No AD, estou tentando identificar contas de usuário em que o mesmo valor EmployeeID é preenchido em 2 ou mais registros. Abaixo está o meu pedaço de código (Crédito: estou usando uma Show-Progress
função definida aqui ) e o Get-ADUser
comando sozinho levou mais de 2 horas para buscar todos os registros. As outras etapas (2 a 5) foram bem rápidas. Enquanto concluo o trabalho, estou tentando saber se isso poderia ter sido feito de forma mais eficiente com o 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: Eu também tentei primeiro exportar todas as contas de usuário para um arquivo csv e depois pós-processar com o Excel, mas tive que franzir a testa por causa do tamanho do conjunto de dados e isso exigia tanto tempo quanto memória.
Qualquer sugestão é muito apreciada.
Respostas
Como não sabemos o que está em $properties
ou $selectPropsList
, sua pergunta é apenas sobre descobrir para quais usuários o mesmo EmployeeID foi emitido, certo?
Por padrão, Get-ADUser já retorna estas propriedades:
DistinguishedName
, Enabled
, GivenName
, Name
, ObjectClass
, ObjectGUID
, SamAccountName
, SID
, Surname
,UserPrincipalName
Então, tudo que você precisa extra é o EmployeeID, eu acho. Tentar coletar MUITAS propriedades torna mais lento, então manter isso no mínimo ajuda a acelerar as coisas.
Em seguida, usando o Show-Progress
script ao qual você vinculou, você tornará a execução do script consideravelmente mais lenta. Você realmente precisa de uma barra de progresso? Por que não simplesmente escrever as linhas com etapas de atividades diretamente no console?
Além disso, colocar tudo junto também não ajuda no departamento de velocidade.
$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
já retorna apenas objetos de usuário, portanto, não há necessidade do filtro LDAP (ObjectCategory=Person)(objectclass=user)
. Usar -Filter "EmployeeID -like '*'"
é provavelmente mais rápido
Esta resposta complementa a resposta útil de Theo e se concentra em mostrar o progresso durante a operação :
A função vinculadaShow-Progress , que é a mais recente no momento desta redação:
tem um bug total , em que não passa a entrada do pipeline (a linha relevante é acidentalmente comentada)
é conceitualmente defeituoso por não usar um
process
bloco, o que significa que toda a entrada do pipeline é coletada primeiro , antes de ser processada - o que frustra a ideia de uma barra de progresso.
Portanto, suas
Show-Progress
chamadas não mostrarão o progresso até que o comando anterior no pipeline produza toda a sua saída. Uma alternativa simples é dividir o pipeline em comandos separados e simplesmente emitir uma mensagem de progresso antes de cada comando, anunciando o próximo estágio de processamento (em vez de progresso por objeto) conforme mostrado na resposta de Theo.Geralmente, não há como mostrar o progresso do processamento interno do comando , apenas o progresso da saída de um comando (multi-objeto) .
A maneira mais simples de fazer isso por meio de uma ForEach-Objectchamada em que você liga
Write-Progress, mas que vem com dois desafios:Para mostrar uma barra de progresso de porcentagem concluída , você precisa saber quantos objetos haverá no total , o que deve ser determinado com antecedência , porque um pipeline não pode saber quantos objetos receberá; sua única opção é coletar toda a saída primeiro (ou encontrar alguma outra maneira de contá-la) e, em seguida, usar a saída coletada como entrada do pipeline, usando a contagem de objetos como base para calcular o valor a ser transmitido
Write-Progress -PerCentComplete
.Chamando
Write-Progress
para cada objeto recebido irá resultar em uma desaceleração significativa do processamento global; um compromisso é chamá-lo apenas para todos os N objetos, como mostrado nesta resposta ; a abordagem pode ser envolvida em uma função implementada adequadamente a laShow-Progress
que requer a passagem da contagem total de objetos como um argumento e executa o processamento de objeto de entrada de streaming adequado (por meio de umprocess
bloco); Dito isso, o mero ato de usar o código do PowerShell para passar objetos de entrada é caro.
Conclusão:
As exibições de progresso de porcentagem concluída têm dois problemas inerentes :
Eles exigem que você saiba o número total de objetos a serem processados de antemão (um pipeline não tem como saber quantos objetos passarão por ele):
Ou: Colete todos os objetos para processar na memória , de antemão , se possível; a contagem de elementos na coleção pode então servir como base para os cálculos de porcentagem completa. Isso pode não ser uma opção com conjuntos de entrada muito grandes.
Ou: Execute uma etapa de processamento extra antecipadamente que simplesmente conta todos os objetos, sem realmente recuperá-los. Isso pode não ser prático em termos de tempo de processamento adicional adicionado.
O processamento de objeto por objeto no código do PowerShell - por meio de ForEach-Objectum script / função avançada - é inerentemente lento.
- Você pode atenuar isso limitando as Write-Progresschamadas a todos os N objetos, conforme mostrado nesta resposta
No geral, é uma troca entre a velocidade de processamento e a capacidade de mostrar o progresso percentual completo ao usuário final.