Optimiser le filtre Get-ADUser
Dans AD, j'essaie d'identifier les comptes d'utilisateurs où la même valeur EmployeeID est renseignée dans 2 enregistrements ou plus. Voici mon morceau de code (crédit: j'utilise une Show-Progress
fonction définie ici ) et la Get-ADUser
commande seule a pris plus de 2 heures pour récupérer tous les enregistrements. Les autres étapes (2 à 5) ont été assez rapides. Pendant que j'ai terminé le travail, j'essaie de savoir si cela aurait pu être fait plus efficacement avec 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: J'ai également essayé d'exporter d'abord tous les comptes d'utilisateurs dans un fichier csv, puis de post-traiter avec Excel, mais j'ai dû froncer les sourcils à cause de la taille de l'ensemble de données et c'était à la fois un temps et une mémoire crunch.
Les suggestions sont grandement appréciées.
Réponses
Puisque nous ne savons pas ce qu'il y a dans $properties
ou $selectPropsList
, votre question est en réalité de savoir à quels utilisateurs le même EmployeeID a été émis, n'est-ce pas?
Par défaut, Get-ADUser renvoie déjà ces propriétés:
DistinguishedName
, Enabled
, GivenName
, Name
, ObjectClass
, ObjectGUID
, SamAccountName
, SID
, Surname
,UserPrincipalName
Donc, tout ce dont vous avez besoin en plus est le EmployeeID, je suppose. Essayer de collecter BEAUCOUP de propriétés ralentit, donc garder cela au strict minimum permet d'accélérer les choses.
Ensuite, en utilisant le Show-Progress
script auquel vous avez lié, vous ralentirez considérablement l'exécution du script. Avez-vous vraiment besoin d'une barre de progression? Pourquoi ne pas simplement écrire les lignes avec les étapes de l'activité directement sur la console?
De plus, tout assembler n'aide pas non plus dans le département de la vitesse.
$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
renvoie déjà uniquement les objets utilisateur, il n'est donc pas nécessaire d'utiliser le filtre LDAP (ObjectCategory=Person)(objectclass=user)
. L'utilisation -Filter "EmployeeID -like '*'"
est probablement plus rapide
Cette réponse complète la réponse utile de Theo et se concentre sur les progrès réalisés pendant l'opération :
La fonction liéeShow-Progress , qui est la dernière à ce jour:
a un bogue pur et simple , en ce qu'il ne passe pas l'entrée du pipeline (la ligne correspondante est accidentellement commentée)
est conceptuellement défectueux en ce qu'il n'utilise pas de
process
bloc, ce qui signifie que toutes les entrées du pipeline sont collectées en premier , avant d'être traitées - ce qui va à l'encontre de l'idée d'une barre de progression.
Par conséquent, vos
Show-Progress
appels n'indiqueront pas la progression tant que la commande précédente du pipeline n'aura pas sorti toute sa sortie. Une alternative simple est de diviser le pipeline en commandes séparées et d'émettre simplement un message de progression avant chaque commande, annonçant l' étape suivante du traitement (plutôt que la progression par objet) comme indiqué dans la réponse de Theo.Généralement, il n'y a aucun moyen d'afficher la progression du traitement interne à la commande , uniquement la progression de la sortie d'une commande (multi-objet) .
Le moyen le plus simple de le faire via un ForEach-Objectappel dans lequel vous appelez
Write-Progress, mais cela comporte deux défis:Pour afficher une barre de progression en pourcentage achevé , vous devez savoir combien d'objets il y aura au total , ce que vous devez déterminer à l' avance , car un pipeline ne peut pas savoir combien d'objets il recevra; votre seule option est de collecter d'abord toute la sortie (ou de trouver une autre façon de la compter), puis d'utiliser la sortie collectée comme entrée de pipeline, en utilisant le nombre d'objets comme base de calcul de la valeur à transmettre
Write-Progress -PerCentComplete
.L'appel
Write-Progress
pour chaque objet reçu entraînera un ralentissement significatif du traitement global; un compromis est de ne l'appeler que pour tous les N objets, comme le montre cette réponse ; l'approche pourrait être enveloppée dans une fonction correctement implémentée a laShow-Progress
qui nécessite de passer le nombre total d'objets en tant qu'argument et effectue un traitement d'objet d'entrée en continu approprié (via unprocess
bloc); Cela dit, le simple fait d'utiliser du code PowerShell pour transmettre des objets d'entrée est coûteux.
Conclusion:
Les affichages de progression en pourcentage terminé présentent deux problèmes inhérents :
Ils nécessitent que vous connaissiez au préalable le nombre total d'objets à traiter (un pipeline n'a aucun moyen de savoir combien d'objets le traverseront):
Soit: collecter tous les objets à traiter en mémoire , au préalable , si possible; le nombre d'éléments de la collection peut ensuite servir de base aux calculs du pourcentage achevé. Cela peut ne pas être une option avec de très grands ensembles d'entrées.
Ou: Effectuez au préalable une étape de traitement supplémentaire qui ne fait que compter tous les objets sans les récupérer. Cela peut ne pas être pratique en termes de temps de traitement supplémentaire ajouté.
Le traitement objet par objet dans le code PowerShell - via ForEach-Objectou via un script / une fonction avancé - est intrinsèquement lent.
- Vous pouvez atténuer cela quelque peu en limitant les Write-Progressappels à tous les N objets, comme indiqué dans cette réponse
Dans l'ensemble, il s'agit d'un compromis entre la vitesse de traitement et la possibilité d'afficher le pourcentage de progression achevé à l'utilisateur final.