Optimalkan filter Get-ADUser

Nov 29 2020

Di AD, saya mencoba untuk mengidentifikasi akun pengguna di mana nilai EmployeeID yang sama diisi dalam 2 atau lebih catatan. Di bawah ini adalah potongan kode saya (Kredit: Saya menggunakan Show-Progressfungsi yang ditentukan di sini ) dan Get-ADUserperintahnya sendiri telah memakan waktu lebih dari 2 jam untuk mengambil semua catatan. Langkah lainnya (2 hingga 5) cukup cepat. Sementara saya telah menyelesaikan pekerjaan, saya mencoba untuk mengetahui apakah ini dapat dilakukan dengan lebih efisien dengan 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: Saya juga mencoba untuk mengekspor semua akun pengguna ke file csv dan kemudian pasca-proses dengan Excel tetapi saya harus cemberut karena ukuran dataset dan itu adalah waktu dan memori yang berderak.

Setiap saran sangat dihargai.

Jawaban

2 Theo Nov 29 2020 at 16:20

Karena kami tidak tahu apa yang ada di dalam $propertiesatau $selectPropsList, pertanyaan Anda sebenarnya hanya tentang mencari tahu pengguna mana yang telah diberikan EmployeeID yang sama, bukan?
Secara default, Get-ADUser sudah mengembalikan properti ini:

DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname,UserPrincipalName

Jadi yang Anda butuhkan tambahan adalah EmployeeID. Mencoba mengumpulkan BANYAK properti memang memperlambat, jadi menjaga ini seminimal mungkin membantu mempercepat pekerjaan.

Selanjutnya, dengan menggunakan Show-Progressskrip yang telah Anda tautkan, Anda akan sangat memperlambat eksekusi skrip. Apakah Anda benar-benar perlu memiliki bilah kemajuan? Mengapa tidak langsung menulis baris dengan langkah-langkah aktivitas ke konsol?

Selain itu, menyatukan semuanya juga tidak membantu dalam departemen kecepatan ..

$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-ADUsersudah mengembalikan objek pengguna saja, jadi filter LDAP tidak diperlukan (ObjectCategory=Person)(objectclass=user). Menggunakan -Filter "EmployeeID -like '*'"mungkin lebih cepat

1 mklement0 Nov 29 2020 at 22:27

Jawaban ini melengkapi jawaban Theo yang sangat membantu dan berfokus untuk menunjukkan kemajuan selama operasi :

  • The terkait Show-Progressfungsi , yang terbaru saat tulisan ini:

    • memiliki bug langsung , yaitu tidak melewatkan masukan pipeline (baris yang relevan tidak sengaja dikomentari)

    • secara konseptual cacat karena tidak menggunakan processblok, yang berarti bahwa semua input pipeline dikumpulkan terlebih dahulu , sebelum diproses - yang mengalahkan gagasan tentang bilah kemajuan.

  • Oleh karena itu, Show-Progresspanggilan Anda tidak akan menampilkan kemajuan hingga perintah sebelumnya dalam pipeline mengeluarkan semua keluarannya. Alternatif sederhana adalah dengan memecah pipeline menjadi perintah terpisah dan hanya memancarkan satu pesan kemajuan sebelum setiap perintah, mengumumkan tahap pemrosesan berikutnya (bukan kemajuan per objek) seperti yang ditunjukkan dalam jawaban Theo.

  • Secara umum, tidak ada cara untuk menunjukkan kemajuan pemrosesan internal perintah , hanya kemajuan keluaran perintah (multi-objek) .

    • Cara termudah untuk melakukan ini melalui ForEach-Objectpanggilan yang Anda panggil
      Write-Progress, tetapi itu memiliki dua tantangan:

      • Untuk menampilkan bilah kemajuan persen selesai , Anda perlu mengetahui berapa banyak objek yang akan ada , yang harus Anda tentukan sebelumnya , karena pipeline tidak dapat mengetahui berapa banyak objek yang akan diterimanya; satu-satunya pilihan Anda adalah mengumpulkan semua keluaran terlebih dahulu (atau mencari cara lain untuk menghitungnya) dan kemudian menggunakan keluaran yang dikumpulkan sebagai masukan pipa, menggunakan hitungan objek sebagai dasar untuk menghitung nilai yang akan diteruskan Write-Progress -PerCentComplete.

      • Memanggil Write-Progressuntuk setiap objek yang diterima akan mengakibatkan perlambatan yang signifikan dari keseluruhan pemrosesan; kompromi adalah dengan hanya memanggilnya untuk setiap N objek, seperti yang ditunjukkan dalam jawaban ini ; pendekatan di sana bisa dibungkus dalam fungsi yang diimplementasikan dengan benar a la Show-Progressyang mengharuskan melewatkan jumlah objek total sebagai argumen dan melakukan streaming pemrosesan objek input yang tepat (melalui processblok); yang mengatakan, tindakan menggunakan kode PowerShell untuk melewatkan objek input saja mahal.


Kesimpulan:

Tampilan kemajuan persen-selesai memiliki dua masalah yang melekat :

  • Mereka mengharuskan Anda untuk mengetahui jumlah total objek yang akan diproses sebelumnya (pipeline tidak memiliki cara untuk mengetahui berapa banyak objek yang akan melewatinya):

    • Salah satu: Kumpulkan semua objek untuk diproses dalam memori , sebelumnya , jika memungkinkan; jumlah elemen dalam koleksi kemudian bisa menjadi dasar untuk penghitungan persen-selesai. Ini mungkin bukan opsi dengan kumpulan masukan yang sangat besar.

    • Atau: Lakukan langkah pemrosesan tambahan sebelumnya yang hanya menghitung semua objek tanpa benar-benar mengambilnya. Ini mungkin tidak praktis dalam hal penambahan waktu pemrosesan.

  • Pemrosesan objek demi objek dalam kode PowerShell - baik melalui ForEach-Objectatau skrip / fungsi lanjutan - pada dasarnya lambat.

    • Anda dapat menguranginya dengan membatasi Write-Progresspanggilan ke setiap objek N, seperti yang ditunjukkan dalam jawaban ini

Secara keseluruhan, ini adalah tradeoff antara kecepatan pemrosesan dan kemampuan untuk menunjukkan kemajuan persen-lengkap kepada pengguna akhir.