.netpowershelllinqhashtabledirectorysearcher

Quickest way to do a [adsisearcher] and store in a hashtable or dictionary collection list


I am using [adsisearcher] to grab AD User info because it's way faster than get-aduser. I am also trying to figure out how to add it into a hashtable or dictionary using LINQ or perhaps any other alternative that is fast. For me I'm trying to get the best performance/most efficiency because long term goal is importing the data into a contact list.

Here is what I currently have, and this works well, but I'm curious if there is a quicker way to organize the data?

$Start = Get-Date

$searcher=[adsisearcher]""
$searcher.Sort.PropertyName = "sn"
$searcher.Filter = "(&(extensionAttribute2=customValue)(|(mobile=*)(telephonenumber=*)))"
$colProplist = "givenname","extensionattribute2","initials","mobile","telephonenumber","sn","displayname","company","title","mail","department","thumbnailphoto","samaccountname"
foreach ($i in $colPropList){$searcher.PropertiesToLoad.Add($i) > $NULL }

$End = Get-Date

Write-Host "User info took $($Start - $End) seconds"
Write-Host ""

#----------------------------------------------------

Write-Host "Creating User Hashtable"
$Start = Get-Date

$Users=@{}

$Users = $searcher.FindAll() | ForEach-Object{
    New-Object -TypeName PSCustomObject -Property @{

        'FirstName' = $_.properties.givenname -join ''
        'MiddleName' = $_.properties.initials -join ''
        'LastName' = $_.properties.sn -join ''
        'DisplayName' = $_.properties.displayname -join ''
        'SamAccountName' = $_.properties.samaccountname -join ''
        'Email' = $_.properties.mail -join ''
        'Mobile' = $_.properties.mobile -join ''
        'TelephoneNumber' = $_.properties.telephonenumber -join ''
        'Title' = $_.properties.title -join ''
        'Dept' = $_.properties.department -join ''
        'Company' = $_.properties.company -join ''
        'Photo' = $_.properties.thumbnailphoto -join ''
        'ExtensionAttribute2' = $_.properties.extensionattribute2 -join ''

     } | Select-Object -Property FirstName, MiddleName, LastName, DisplayName, SamAccountName, Email, Mobile, TelephoneNumber, Title, Dept, Company, Photo, ExtensionAttribute2
 }

$End = Get-Date

Write-Host "User HashTable took $($Start - $End) seconds"
Write-Host ""

I heard using LINQ is very quick, but I get stuck in properly using the syntax and I can't seem to find good documentation or examples of how to use it.


Solution

  • So, as in my comments, I personally don't see much room for improvement to your script. I have modified some minor things. You should test with measure-command and see if the code runs faster with a classic foreach loop or with a foreach-object loop.

    I also changed the -join '' for strong typed [string], which I'm not sure, but might be faster.

    To answer your question, why the -join '' is there, this is because each property is of the type ResultPropertyValueCollection

    IsPublic IsSerial Name                          BaseType                                 
    -------- -------- ----                          --------                                 
    True     False    ResultPropertyValueCollection System.Collections.ReadOnlyCollectionBase
    

    By doing a -join '' you're converting the value of each property to a string:

    PS /> ($z.samaccountname -join '').GetType()
    
    IsPublic IsSerial Name   BaseType
    -------- -------- ----   --------
    True     True     String System.Object
    
    PS /> ([string] $z.samaccountname).GetType()
    
    IsPublic IsSerial Name   BaseType
    -------- -------- ----   --------
    True     True     String System.Object  
    

    But why is this important?
    Well, very basically, if you attempt to export the data without converting those values to string to a Csv for example you would end up with something like this (you would see the type of each value instead):

    enter image description here

    $start = Get-Date
    $end   = Get-Date
    
    $searcher = [adsisearcher]::new()
    $searcher.Sort.PropertyName = "sn"
    $searcher.Filter = "(&(objectcategory=person)(objectclass=user)(extensionAttribute2=*)(|(mobile=*)(telephonenumber=*)))"
    
    $colProplist = @(
        'givenname', 'extensionattribute2'
        'initials', 'mobile', 'telephonenumber'
        'sn', 'displayname', 'company'
        'title', 'mail', 'department'
        'thumbnailphoto', 'samaccountname'
    )
    $searcher.PropertiesToLoad.AddRange($colProplist)
    
    Write-Host "User info took $($Start - $End) seconds"
    Write-Host ""
    Write-Host "Creating User Hashtable"
    
    $users = foreach($user in $searcher.FindAll()) {
        $i = $user.properties
    
        [pscustomobject]@{
            FirstName           = [string] $i.givenname
            MiddleName          = [string] $i.initials 
            LastName            = [string] $i.sn 
            DisplayName         = [string] $i.displayname 
            SamAccountName      = [string] $i.samaccountname 
            Email               = [string] $i.mail 
            Mobile              = [string] $i.mobile 
            TelephoneNumber     = [string] $i.telephonenumber 
            Title               = [string] $i.title 
            Dept                = [string] $i.department 
            Company             = [string] $i.company 
            Photo               = [string] $i.thumbnailphoto 
            ExtensionAttribute2 = [string] $i.extensionattribute2
        }
    }
    $End = Get-Date
    
    Write-Host "User HashTable took $($Start - $End) seconds"
    Write-Host ""