Optimizar el filtro Get-ADUser
En AD, estoy tratando de identificar cuentas de usuario donde el mismo valor de EmployeeID se completa en 2 o más registros. A continuación se muestra mi fragmento de código (crédito: estoy usando una Show-Progress
función definida aquí ) y el Get-ADUser
comando solo ha tardado más de 2 horas en recuperar todos los registros. Los otros pasos (2 a 5) han sido bastante rápidos. Mientras he completado el trabajo, estoy tratando de saber si esto podría haberse hecho de manera más eficiente con 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
PD: También intenté exportar primero todas las cuentas de usuario a un archivo csv y luego postprocesar con Excel, pero tuve que fruncir el ceño debido al tamaño del conjunto de datos y fue tanto tiempo como memoria.
Cualquier sugerencia es muy apreciada.
Respuestas
Dado que no sabemos qué hay en $properties
o $selectPropsList
, su pregunta es realmente solo sobre averiguar a qué usuarios se les ha emitido el mismo EmployeeID, ¿verdad?
De forma predeterminada, Get-ADUser ya devuelve estas propiedades:
DistinguishedName
, Enabled
, GivenName
, Name
, ObjectClass
, ObjectGUID
, SamAccountName
, SID
, Surname
,UserPrincipalName
Entonces, todo lo que necesita adicional es el EmployeeID, supongo. Tratar de recolectar MUCHAS propiedades se ralentiza, por lo que mantener esto al mínimo ayuda a acelerar las cosas.
A continuación, al utilizar el Show-Progress
script al que se ha vinculado, ralentizará considerablemente la ejecución del script. ¿Realmente necesitas tener una barra de progreso? ¿Por qué no escribir simplemente las líneas con los pasos de la actividad directamente en la consola?
Además, conectar todo junto tampoco ayuda en el departamento de velocidad.
$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
ya devuelve solo objetos de usuario, por lo que no es necesario el filtro LDAP (ObjectCategory=Person)(objectclass=user)
. Usar -Filter "EmployeeID -like '*'"
es probablemente más rápido
Esta respuesta complementa la útil respuesta de Theo y se enfoca en mostrar el progreso durante la operación :
La función vinculadaShow-Progress , que es la más reciente a la fecha de este escrito:
tiene un error absoluto , ya que no pasa la entrada de la tubería (la línea relevante se comenta accidentalmente)
es conceptualmente defectuoso porque no usa un
process
bloque, lo que significa que toda la entrada de la canalización se recopila primero , antes de procesarse, lo que anula la idea de una barra de progreso.
Por lo tanto, sus
Show-Progress
llamadas no mostrarán el progreso hasta que el comando anterior en la canalización haya generado toda su salida. Una alternativa simple es dividir la canalización en comandos separados y simplemente emitir un mensaje de progreso antes de cada comando, anunciando la siguiente etapa de procesamiento (en lugar del progreso por objeto) como se muestra en la respuesta de Theo.Generalmente, no hay forma de mostrar el progreso del procesamiento interno del comando , solo el progreso de la salida de un comando (multi-objeto) .
La forma más sencilla de hacerlo es mediante una ForEach-Objectllamada en la que llamas
Write-Progress, pero eso conlleva dos desafíos:Para mostrar una barra de progreso de porcentaje completo , necesita saber cuántos objetos habrá en total , lo que debe determinar con anticipación , porque una canalización no puede saber cuántos objetos recibirá; su única opción es recopilar primero toda la salida (o encontrar otra forma de contarla) y luego usar la salida recopilada como entrada de canalización, utilizando el recuento de objetos como base para calcular el valor a pasar
Write-Progress -PerCentComplete
.Llamar
Write-Progress
por cada objeto recibido resultará en una ralentización significativa del procesamiento general; un compromiso es llamarlo solo para cada N objetos, como se muestra en esta respuesta ; el enfoque podría estar envuelto en una función correctamente implementada a laShow-Progress
que requiere pasar el recuento total de objetos como un argumento y realiza un procesamiento de entrada de objetos de flujo adecuado (a través de unprocess
bloque); Dicho esto, el mero hecho de usar código PowerShell para pasar objetos de entrada es costoso.
Conclusión:
Las pantallas de progreso de porcentaje completo tienen dos problemas inherentes :
Requieren que conozca el número total de objetos a procesar de antemano (una canalización no tiene forma de saber cuántos objetos pasarán a través de ella):
O bien: recopile todos los objetos para procesar en la memoria , de antemano , si es posible; el recuento de elementos de la colección puede servir como base para los cálculos de porcentaje completo. Puede que esta no sea una opción con conjuntos de entrada muy grandes.
O: Realice un paso de procesamiento adicional de antemano que simplemente cuente todos los objetos sin recuperarlos realmente. Esto puede no ser práctico en términos del tiempo de procesamiento adicional agregado.
El procesamiento objeto por objeto en el código de PowerShell , ya sea mediante ForEach-Objectun script / función avanzada o mediante un script o función avanzada , es inherentemente lento.
- Puede mitigar eso un poco limitando las Write-Progressllamadas a cada N objetos, como se muestra en esta respuesta
En general, es una compensación entre la velocidad de procesamiento y la capacidad de mostrar el progreso porcentual completo al usuario final.