Ottimizza il filtro Get-ADUser
In AD, sto cercando di identificare gli account utente in cui lo stesso valore EmployeeID è popolato in 2 o più record. Di seguito è riportato il mio pezzo di codice (Credito: sto usando una Show-Progressfunzione definita qui ) e il Get-ADUsercomando da solo ha impiegato più di 2 ore per recuperare tutti i record. Gli altri passaggi (da 2 a 5) sono stati piuttosto rapidi. Mentre ho completato il lavoro, sto cercando di sapere se questo avrebbe potuto essere fatto in modo più efficiente 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
PS: Ho anche provato a esportare prima tutti gli account utente in un file csv e poi a post-elaborazione con Excel, ma ho dovuto aggrottare le sopracciglia a causa delle dimensioni del set di dati ed era sia il tempo che la memoria.
Ogni suggerimento è molto apprezzato.
Risposte
Dal momento che non sappiamo cosa c'è in $propertieso $selectPropsList, la tua domanda in realtà riguarda solo scoprire a quali utenti è stato rilasciato lo stesso EmployeeID, giusto?
Per impostazione predefinita, Get-ADUser restituisce già queste proprietà:
DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname,UserPrincipalName
Quindi tutto ciò di cui hai bisogno in più è EmployeeID immagino. Cercare di raccogliere MOLTE proprietà rallenta, quindi mantenerlo al minimo aiuta ad accelerare le cose.
Successivamente, utilizzando lo Show-Progressscript a cui ti sei collegato, rallenterai notevolmente l'esecuzione dello script. Hai davvero bisogno di una barra di avanzamento? Perché non scrivere semplicemente le righe con i passaggi dell'attività direttamente sulla console?
Inoltre, collegare tutto insieme non aiuta neanche nel reparto velocità.
$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-ADUserrestituisce già solo oggetti utente, quindi non è necessario il filtro LDAP (ObjectCategory=Person)(objectclass=user). L'utilizzo -Filter "EmployeeID -like '*'"è probabilmente più veloce
Questa risposta completa l'utile risposta di Theo e si concentra sul mostrare i progressi durante l'operazione :
La funzione collegataShow-Progress , che è l'ultima al momento della stesura di questo documento:
ha un bug definitivo , in quanto non passa l'input della pipeline (la riga rilevante viene accidentalmente commentata)
è concettualmente difettoso in quanto non utilizza un
processblocco, il che significa che tutti gli input della pipeline vengono raccolti prima , prima di essere elaborati, il che vanifica l'idea di una barra di avanzamento.
Pertanto, le
Show-Progresschiamate non mostreranno lo stato di avanzamento finché il comando precedente nella pipeline non avrà prodotto tutto il suo output. Una semplice alternativa è suddividere la pipeline in comandi separati e emettere semplicemente un messaggio di avanzamento prima di ogni comando, annunciando la fase successiva dell'elaborazione (piuttosto che l'avanzamento per oggetto) come mostrato nella risposta di Theo.In genere, non è possibile mostrare l'avanzamento dell'elaborazione interna al comando , ma solo l'avanzamento dell'output (multioggetto) di un comando .
Il modo più semplice per farlo tramite una ForEach-Objectchiamata in cui chiami
Write-Progress, ma che comporta due sfide:Per mostrare una barra di avanzamento percentuale di completamento , è necessario sapere quanti oggetti ci saranno in totale , che è necessario determinare in anticipo , perché una pipeline non può sapere quanti oggetti riceverà; l'unica opzione è raccogliere prima tutto l'output (o trovare un altro modo per contarlo) e quindi utilizzare l'output raccolto come input della pipeline, utilizzando il conteggio degli oggetti come base per il calcolo del valore a cui passare
Write-Progress -PerCentComplete.La chiamata
Write-Progressper ogni oggetto ricevuto comporterà un significativo rallentamento dell'elaborazione complessiva; un compromesso è chiamarlo solo per ogni N oggetti, come mostrato in questa risposta ; l'approccio potrebbe essere racchiuso in una funzione propriamente implementataShow-Progressche richiede di passare il conteggio totale degli oggetti come argomento ed esegue un'adeguata elaborazione in streaming dell'oggetto in ingresso (tramite unprocessblocco); Detto questo, il semplice atto di utilizzare il codice PowerShell per il passaggio di oggetti di input è costoso.
Conclusione:
Le visualizzazioni di avanzamento della percentuale di completamento hanno due problemi intrinseci :
Richiedono di conoscere in anticipo il numero totale di oggetti da elaborare (una pipeline non ha modo di sapere quanti oggetti passeranno attraverso di essa):
O: raccogliere tutti gli oggetti da elaborare in memoria , in anticipo , se possibile; il conteggio degli elementi nella raccolta può quindi servire come base per i calcoli di completamento percentuale. Questa potrebbe non essere un'opzione con set di input molto grandi.
Oppure: eseguire in anticipo un passaggio di elaborazione aggiuntivo che si limita a contare tutti gli oggetti senza recuperarli effettivamente. Ciò potrebbe non essere pratico in termini di tempo di elaborazione aggiuntivo aggiunto.
L'elaborazione oggetto per oggetto nel codice di PowerShell , tramite ForEach-Objecto tramite uno script / funzione avanzata , è intrinsecamente lenta.
- Puoi mitigarlo in qualche modo limitando le Write-Progresschiamate a ogni N oggetti, come mostrato in questa risposta
Nel complesso è un compromesso tra la velocità di elaborazione e la capacità di mostrare all'utente finale l' avanzamento percentuale completo .