PowerShell Active Directory People Picker

This post contains a PowerShell Active Directory people picker, which is useful when we develop PowerShell forms that require searching for Active Directory users via their display name or username.

It’s a very similar concept to our PowerShell Active Directory Group Picker.  You can probably speed the search up by specifying an ADSI SearchRoot, this way it will only search a specific organisation unit in Active Directory as opposed to everywhere!  But for this example we’ll search everywhere.

We’ve also limited the results to 30 to speed up the search too.

cls
Add-Type -AssemblyName PresentationCore,PresentationFramework


function Select-ADObject
{  
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "Active Directory Search"
    $form.Size = New-Object System.Drawing.Size(340,320)
    $form.StartPosition = "CenterScreen"
        
    $SearchButton = New-Object System.Windows.Forms.Button
    $SearchButton.Location = New-Object System.Drawing.Point(260,10)
    $SearchButton.Size = New-Object System.Drawing.Size(50,23)
    $SearchButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $SearchButton.Text = 'Search'
    $SearchButton.Add_Click({

        $SearchButton.Enabled = $false
        $CancelButton.Enabled = $false

        #start progress bar
        $ProgressBar.Style="Marquee"
        $ProgressBar.MarqueeAnimationSpeed = 10;
      
        $searchVal = $SearchText.Text

        $job = Start-Job -ArgumentList $searchVal -ScriptBlock  {
            param($searchVal)                      
            if ($searchVal -ne $null -and $searchVal -ne "") {
                $objSearcher=[adsisearcher]"(&(objectCategory=person)(|(name=*$searchVal*)(samaccountname=*$searchVal*)))"
                $objSearcher.SizeLimit = 30

                $colProplist = "name"
                foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) | out-null } 
	
                $colResults = $objSearcher.FindAll()
            }
            return $colResults
        }
    
        while($job.State -eq 'Running') {
            [System.Windows.Forms.Application]::DoEvents()
        }
        $results = $job | Receive-Job -AutoRemoveJob -Wait
              
        $ListBox.Items.Clear()                

        if ($results -eq $null -or $results.Count -eq 0) {
            $SearchButton.Enabled = $true
            $CancelButton.Enabled = $true
            #needed to reset marquee to 0
            $ProgressBar.Style="Blocks"
            $ProgressBar.MarqueeAnimationSpeed = 0;
            $ProgressBar.Value = 0;
            $SelectButton.Enabled = $false
            [System.Windows.MessageBox]::Show("Search returned no results.")
            return
        }

        
        foreach ($objResult in $results)
        {          
            [void] $ListBox.Items.Add(($objResult.Properties).name[0])	
        }

        $SearchButton.Enabled = $true
        $CancelButton.Enabled = $true
        #needed to reset marquee to 0
        $ProgressBar.Style="Blocks"
        $ProgressBar.MarqueeAnimationSpeed = 0;
        $ProgressBar.Value = 0;
        return
    })

    $form.Controls.Add($SearchButton)

    $SearchText = New-Object System.Windows.Forms.TextBox
    $SearchText.Location = New-Object System.Drawing.Point(10,11)
    $SearchText.Size = New-Object System.Drawing.Size(245,20)
    $SearchText.Multiline = $false   
    $SearchText.AcceptsReturn = $true 
    $SearchText.Add_KeyUp({
        if ($_.KeyCode -eq [System.Windows.Forms.Keys]::Enter) {
            $SearchButton.PerformClick()
        }
    })

    $form.Controls.Add($SearchText)

    $SelectButton = New-Object System.Windows.Forms.Button
    $SelectButton.Location = New-Object System.Drawing.Point(205,245)
    $SelectButton.Size = New-Object System.Drawing.Size(50,23)
    $SelectButton.Text = "Select"
    $SelectButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $SelectButton.Add_Click({ 
        if ($ListBox.SelectedItems.Count -eq 0) {
            [System.Windows.MessageBox]::Show("No item selected.")
            return
        }
        $script:selectedADGroup = $ListBox.SelectedItem; 
        write-host $ListBox.SelectedText; 
        $form.Close() 
    })
    $SelectButton.Enabled = $false
    $form.Controls.Add($SelectButton)

    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Point(260,245)
    $CancelButton.Size = New-Object System.Drawing.Size(50,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $CancelButton.Add_Click({ $script:selectedADGroup = $null; $form.Close()})
    $form.Controls.Add($CancelButton)
      
    $ListBox = New-Object System.Windows.Forms.ListBox
    $ListBox.Location = New-Object System.Drawing.Point(10,40)
    $ListBox.Size = New-Object System.Drawing.Size(300, 20)
    $ListBox.Height = 200
    $ListBox.Add_SelectedIndexChanged({
        if ($ListBox.SelectedItems.Count -eq 1) {
            $SelectButton.Enabled = $true
        }
    })
    $form.Controls.Add($ListBox)

 
    $ProgressBar = New-Object System.Windows.Forms.ProgressBar
    $ProgressBar.Location = New-Object System.Drawing.Point(10, 246)
    $ProgressBar.Size = New-Object System.Drawing.Size(190, 21)
    $ProgressBar.Value = 0
    $ProgressBar.Style="Marquee"
    $ProgressBar.MarqueeAnimationSpeed = 0;
    $form.Controls.Add($ProgressBar)

    $form.TopMost = $true
    $form.Add_Shown({$form.Activate(); $SearchText.focus()})
    [void] $form.ShowDialog()

    return $script:selectedADGroup
}

$selectedObject = Select-ADObject

write-host $selectedObject

PowerShell Active Directory Group Picker

This post provides an example of a PowerShell Active Directory Group Picker.  You can also find a similar example of our PowerShell Active Directory People Picker.

When using one of my PowerShell GUI tools, we needed to select a valid Active Directory group.  I use the word ‘valid’ because merely prompting the user to enter free text created too much margin for error, so I needed to return a group that definitely existed.

There are ways to do this via PowerShell using the Remote Server Administration Tools (RSAT), but I don’t like my scripts relying on external tools and I like them to remain portable, whilst keeping the host machine clean.

Ultimately I ended up interfacing with Active Directory using PowerShell and ADSI.  The script basically invokes a function that launches a Windows Form.  It has a textbox to search for a group, and a ListBox to display them.  It then sets a variable in the script’s context with the selected group, which is accessible from the initial call.

cls
Add-Type -AssemblyName PresentationCore,PresentationFramework


function Select-ADObject
{  
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "Active Directory Search"
    $form.Size = New-Object System.Drawing.Size(340,320)
    $form.StartPosition = "CenterScreen"
        
    $SearchButton = New-Object System.Windows.Forms.Button
    $SearchButton.Location = New-Object System.Drawing.Point(260,10)
    $SearchButton.Size = New-Object System.Drawing.Size(50,23)
    $SearchButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $SearchButton.Text = 'Search'
    $SearchButton.Add_Click({

        $SearchButton.Enabled = $false
        $CancelButton.Enabled = $false

        #start progress bar
        $ProgressBar.Style="Marquee"
        $ProgressBar.MarqueeAnimationSpeed = 10;
      
        $searchVal = $SearchText.Text

        $job = Start-Job -ArgumentList $searchVal -ScriptBlock  {
            param($searchVal)                      
            if ($searchVal -ne $null -and $searchVal -ne "") {
                $objSearcher=[adsisearcher]"(&(objectCategory=group)(name=*$searchVal*))"
                $objSearcher.SizeLimit = 30

                $colProplist = "name"
                foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) | out-null } 
	
                $colResults = $objSearcher.FindAll()
            }
            return $colResults
        }
    
        while($job.State -eq 'Running') {
            [System.Windows.Forms.Application]::DoEvents()
        }
        $results = $job | Receive-Job -AutoRemoveJob -Wait
              
        $ListBox.Items.Clear()                

        if ($results -eq $null -or $results.Count -eq 0) {
            $SearchButton.Enabled = $true
            $CancelButton.Enabled = $true
            #needed to reset marquee to 0
            $ProgressBar.Style="Blocks"
            $ProgressBar.MarqueeAnimationSpeed = 0;
            $ProgressBar.Value = 0;
            $SelectButton.Enabled = $false
            [System.Windows.MessageBox]::Show("Search returned no results.")
            return
        }

        
        foreach ($objResult in $results)
        {          
            [void] $ListBox.Items.Add(($objResult.Properties).name[0])	
        }

        $SearchButton.Enabled = $true
        $CancelButton.Enabled = $true
        #needed to reset marquee to 0
        $ProgressBar.Style="Blocks"
        $ProgressBar.MarqueeAnimationSpeed = 0;
        $ProgressBar.Value = 0;
        return
    })

    $form.Controls.Add($SearchButton)

    $SearchText = New-Object System.Windows.Forms.TextBox
    $SearchText.Location = New-Object System.Drawing.Point(10,11)
    $SearchText.Size = New-Object System.Drawing.Size(245,20)
    $SearchText.Multiline = $false   
    $SearchText.AcceptsReturn = $true 
    $SearchText.Add_KeyUp({
        if ($_.KeyCode -eq [System.Windows.Forms.Keys]::Enter) {
            $SearchButton.PerformClick()
        }
    })

    $form.Controls.Add($SearchText)

    $SelectButton = New-Object System.Windows.Forms.Button
    $SelectButton.Location = New-Object System.Drawing.Point(205,245)
    $SelectButton.Size = New-Object System.Drawing.Size(50,23)
    $SelectButton.Text = "Select"
    $SelectButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $SelectButton.Add_Click({ 
        if ($ListBox.SelectedItems.Count -eq 0) {
            [System.Windows.MessageBox]::Show("No item selected.")
            return
        }
        $script:selectedADGroup = $ListBox.SelectedItem; 
        write-host $ListBox.SelectedText; 
        $form.Close() 
    })
    $SelectButton.Enabled = $false
    $form.Controls.Add($SelectButton)

    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Point(260,245)
    $CancelButton.Size = New-Object System.Drawing.Size(50,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Cursor = [System.Windows.Forms.Cursors]::Hand
    $CancelButton.Add_Click({ $script:selectedADGroup = $null; $form.Close()})
    $form.Controls.Add($CancelButton)
      
    $ListBox = New-Object System.Windows.Forms.ListBox
    $ListBox.Location = New-Object System.Drawing.Point(10,40)
    $ListBox.Size = New-Object System.Drawing.Size(300, 20)
    $ListBox.Height = 200
    $ListBox.Add_SelectedIndexChanged({
        if ($ListBox.SelectedItems.Count -eq 1) {
            $SelectButton.Enabled = $true
        }
    })
    $form.Controls.Add($ListBox)

 
    $ProgressBar = New-Object System.Windows.Forms.ProgressBar
    $ProgressBar.Location = New-Object System.Drawing.Point(10, 246)
    $ProgressBar.Size = New-Object System.Drawing.Size(190, 21)
    $ProgressBar.Value = 0
    $ProgressBar.Style="Marquee"
    $ProgressBar.MarqueeAnimationSpeed = 0;
    $form.Controls.Add($ProgressBar)

    $form.TopMost = $true
    $form.Add_Shown({$form.Activate(); $SearchText.focus()})
    [void] $form.ShowDialog()

    return $script:selectedADGroup
}

$selectedObject = Select-ADObject

write-host $selectedObject

List All User and Computer LDAP Attributes using PowerShell and ADSI

Other Posts in this Series:

There comes a time when we want to read or write an Active Directory attribute, yet we don’t know the name of the attribute we’re looking for!  This handy script will list all user and computer LDAP attributes using PowerShell and ADSI.

In order to do this we need an example user and computer to find the AD attributes for.  You will need to change those strings at the top of the script below.

$exampleUser = "exampleuser"
$exampleComputer = "examplecomputer"

$searcher=[adsisearcher]"(&(objectCategory=person)(objectClass=user)(SamAccountName=$exampleUser))"
$searcher.PageSize = 1
        
$user = $searcher.FindOne()
if ($user -ne $null) {
    write-host "********All user LDAP attributes********"
    $user.Properties.PropertyNames | Sort | foreach { new-object psobject -Property @{ AttributeName=$_;AttributeValue=$(($user.Properties).$_) }} | Format-Table AttributeName,AttributeValue
} else {
    write-host "Cannot find user"
}  

$searcher=[adsisearcher]"(&(objectClass=computer)(Name=$exampleComputer))"
$searcher.PageSize = 1
       
$computer = $searcher.FindOne()
if ($computer -ne $null) {
    write-host "********All comptuer LDAP attributes********"
    $computer.Properties.PropertyNames | Sort | foreach { new-object psobject -Property @{ AttributeName=$_;AttributeValue=$(($computer.Properties).$_) }} | Format-Table AttributeName,AttributeValue
} else {
    write-host "Cannot find computer"
} 

Once you’ve found what you’re looking for, you may want to look at other posts in this series such as how to use ADSI to set and clear Active Directory attributes.

LDAP Does Not Return All Active Directory Group Members

LDAP does not return all Active Directory group members if there are more than 1500 members in the group.  It will return the first 1500, but none thereafter.

LDAP Does Not Return All Active Directory Group Members

Luckily when a group has more than 1500 users, LDAP returns a ‘member range’ property that looks like this: member;range=0-1499.  This property doesn’t exist when groups have 1500 or less members,

We can use this to iterate through each range until there are no more members left.

function  Get-ADGroupMembers {
    param ([string]$adgroup)
      
    $searcher=[adsisearcher]""
    $searcher.Filter="(&(objectClass=group)(cn=$adgroup))"
    $searcher.PageSize=200
    
    #find group   
    $result=$searcher.FindOne()

    #if group found
    if ($result) {
                 
        #if range exists because group is large
        $moreThan1500Members = [bool]($result.Properties.PropertyNames | where { $_ -like "member;range=*" })

        #if group has more than 1500 members
        if($moreThan1500Members) {     
   
            #we need to recurse through ranges
            $iteratedAllRanges=$false
            $rangeBottom =0
            $rangeTop= 0
    
            while (!($iteratedAllRanges)) {

               $rangeTop=$rangeBottom + 1499

               #set new range
               $memberRange="member;range=$rangeBottom-$rangeTop"
               $searcher.PropertiesToLoad.Clear()
               [void]$searcher.PropertiesToLoad.Add("$memberRange")
               
               try {

                    #if range invalid, throw exception
                    $result = $searcher.FindOne()
                    $rangedProperty = $result.Properties.PropertyNames -like "member;range=*"
                    $members +=$result.Properties.item($rangedProperty)

                    if ($members.count -eq 0) { $iteratedAllRanges=$true }

               } catch {
                    $iteratedAllRanges=$true
               }

               #increment bottom of range for next iteration
               $rangeBottom+=1500

            }
        } else {
            #group member count is less than 1500
            $members += $result.properties.item("member")
        }

        $searcher.Dispose()
        return $members

    }

    return $false   
}

#return all members
$members = Get-ADGroupMembers "Your AD group name"

#iterate through ALL members
foreach ($mem in $members) {
    write-host $mem
}

#get member count
write-host $members.count

 

 

Search Active Directory using PowerShell ADSISearcher Filters

Other Posts in this Series:

This post discusses how we can search Active Directory using PowerShell ADSISearcher filters.  Using search filters can improve search performance significantly.

Consider the following where we create a default ADSISearcher to begin searching Active Directory (AD):

$objSearcher=[adsisearcher]""

If we used this default configuration, the ADSISearcher would search every object in every organisation unit (OU) in AD. We would then need to filter which records we want to process in some kind of iterative loop after the search results have been retrieved. This would be inefficient and extremely slow.

Luckily the ADSISearcher is a type accelerator for System.DirectoryServices.DirectorySearcher, which exposes a property called ‘Filter’.

We can use this to filter for only users like so:

$objSearcher.Filter = "(objectClass=user)"

or perhaps search for all disabled users:

$objSearcher.Filter = "(&(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"

If we want to search for a specific user:

$objSearcher.Filter = "(&(objectClass=user)(sAMAccountName=alkaneuser1))"

or maybe we only know part of the username, and need to use the asterisk as a wildcard like so:

$objSearcher.Filter = "(&(objectClass=user)(sAMAccountName=*lkaneus*))"

What you may notice in the above examples is that we can also filter using AND or OR logical operators.

The syntax for logical operators is an ampersand (&) for AND, and a pipe symbol (|) for OR.  And all of the expressions should be encased inside brackets.

Here we search where sAMAccountName is either alkaneuser1 OR alkaneuser2:

(|(sAMAccountName=alkaneuser1)(sAMAccountName=alkaneuser2))

Here we search where sAMAccountName is alkaneuser1 AND title is ‘Project Manager’:

(&(sAMAccountName=kt04ag)(title=Project Manager))

Here’s an example of how we could combine multiple logical operators to search for only user objects AND search where sAMAccountName is alkaneuser1 OR alkaneuser2:

$objSearcher.Filter = "(&(objectClass=user)(|(sAMAccountName=alkaneuser1)(sAMAccountName=alkaneuser2)))"

Of course, it’s not only users we can search for. In this example we filter our search by computer objects only AND where the computer is called alkanecomputer1:

$objSearcher.Filter = "(&(objectClass=computer)(Name=alkanecomputer1))"

The SearchRoot property can also optimise LDAP queries. By specifying this, we’re only searching in a specific OU as opposed to the whole of AD! We can specify this like so:

$objSearcher.SearchRoot = [ADSI]"LDAP://OU=Users,DC=alkanesolutions,DC=co,DC=uk"

Another important property to mention when searching AD is PageSize. If the data we are returning contains more than 1000 items, we must page the results otherwise you’ll likely run into ‘LDAP_SIZELIMIT_EXCEEDED’ errors. I typically set it to 200 like so:
$objSearcher.PageSize = 200

Another optimisation is to define which LDAP properties should be returned. There are many, many LDAP roperties and if we don’t require them all we can significantly improve the speed of LDAP queries by specifying only the properties we require like so:

$colProplist = "name","givenname","distinguishedname","description","displayname","samaccountname","title","mail","department"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i) | out-null }

Finally when we search AD using LDAP, we should already know if we’re expecting many results or just one result.  If we’re expecting many results we should use FindAll() and iterate through each result like so:

$allObjects = $objSearcher.FindAll()
foreach ($obj in $allObjects) {
    write-host ($obj.properties).name
    write-host ($obj.properties).displayname 
    write-host ($obj.properties).givenname
    write-host ($obj.properties).distinguishedname
    write-host ($obj.properties).description       
    write-host ($obj.properties).samaccountname
    write-host ($obj.properties).title
    write-host ($obj.properties).mail
    write-host ($obj.properties).department
}

However if we’re only searching for one result (maybe to return information for a single user) we should use FindOne() since it returns the first result only. We can use it like so:

$firstObject = $objSearcher.FindOne()
if ($firstObject -ne $null) {
     write-host ($firstObject.properties).name
    write-host ($firstObject.properties).displayname 
    write-host ($firstObject.properties).givenname
    write-host ($firstObject.properties).distinguishedname
    write-host ($firstObject.properties).description       
    write-host ($firstObject.properties).samaccountname
    write-host ($firstObject.properties).title
    write-host ($firstObject.properties).mail
    write-host ($firstObject.properties).department
}

You can play around with the full script here:

$objSearcher=[adsisearcher]""

$objSearcher.Filter = "(&(objectClass=user)(sAMAccountName=alkaneuser1))"
#$objSearcher.Filter = "(&(objectClass=user)(sAMAccountName=*lkaneus*))"
#$objSearcher.Filter = "(&(objectClass=user)(|(sAMAccountName=alkaneuser1)(sAMAccountName=alkaneuser2)))"
#$objSearcher.Filter = "(&(objectClass=computer)(Name=alkanecomputer1))"

$objSearcher.PageSize = 200

$colProplist = "name","givenname","distinguishedname","description","displayname","samaccountname","title","mail","department"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i) | out-null }

$objSearcher.SearchRoot = [ADSI]"LDAP://OU=Users,DC=alkanesolutions,DC=co,DC=uk"

$allObjects = $objSearcher.FindAll()
foreach ($obj in $allObjects) {
    write-host ($obj.properties).name
    write-host ($obj.properties).displayname 
    write-host ($obj.properties).givenname
    write-host ($obj.properties).distinguishedname
    write-host ($obj.properties).description       
    write-host ($obj.properties).samaccountname
    write-host ($obj.properties).title
    write-host ($obj.properties).mail
    write-host ($obj.properties).department
}


$firstObject = $objSearcher.FindOne()
if ($firstObject -ne $null) {
     write-host ($firstObject.properties).name
    write-host ($firstObject.properties).displayname 
    write-host ($firstObject.properties).givenname
    write-host ($firstObject.properties).distinguishedname
    write-host ($firstObject.properties).description       
    write-host ($firstObject.properties).samaccountname
    write-host ($firstObject.properties).title
    write-host ($firstObject.properties).mail
    write-host ($firstObject.properties).department
}

Use PowerShell ADSI to Search Groups in Active Directory

Other Posts in this Series:

This post provides a simple example of how we can use PowerShell ADSI to search groups in Active Directory.  You may wish to further optimise this by using LDAP filters.

#only groups
$objSearcher=[adsisearcher]'(&(objectCategory=group))'
$objSearcher.PageSize = 200

#specify properties to include
$colProplist = "name"
foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) | out-null } 
	
$colResults = $objSearcher.FindAll()

foreach ($objResult in $colResults)
{
    #group name
    $groupname = ($objResult.Properties).name    
    write-host $groupname  		
}

 

Use PowerShell ADSI to Search Users in Active Directory

Other Posts in this Series:

This post provides a simple example of how we can use PowerShell ADSI to search users in Active Directory.  You may wish to further optimise this by using LDAP filters.

$searcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user))'
$searcher.PageSize = 200

$colProplist = "samaccountname"
foreach ($i in $colPropList) { $searcher.PropertiesToLoad.Add($i) | out-null } 
        
$Users = $searcher.findall()
foreach ($user in $Users) {
       write-host ($user.Properties).samaccountname
}      

The Difference Between PowerShell ADSI and ADSISearcher

Other Posts in this Series:

This post will examine the difference between PowerShell ADSI and ADSISearcher when using PowerShell to query Active Directory.

ADSI and ADSISearcher are used to query Active Directory (AD) using Lightweight Directory Access Protocol (LDAP).

What is LDAP?

LDAP, as the name suggests, is a protocol that provides an interface to query AD directory services.  When we are querying AD we are returning one or more objects with unique identifiers (a Distinguished Name).  And we can do this using two type accelerators called ADSI and ADSISearcher.

What is a Type Accelerator?

A type accelerator is a simple alias to represent a .Net class.  We can return a list of type accelerators like so:

[System.Management.Automation.PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::Get

You’ll notice that one of the type accelerators is psobject which references the class System.Management.Automation.PSObject. This means we could simplify our command above by using a type accelerator to get a list of all the type accelerators like so!

[psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::Get

What is ADSISearcher?

[ADSISearcher] is a type accelerator for the class System.DirectoryServices.DirectorySearcher.  It is used to search for one or more objects based on a filter.

Consider this example, where we want to search all objects in AD and filter by user objects only:

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user))'

Or perhaps filter by only computers:

$objSearcher=[adsisearcher]'(&(objectCategory=computer))'

We can also get more complex by filtering for only enabled user objects in AD by adding a filter for the userAccountControl:

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))'

And if we wanted to loop through all the enabled users we could do this:

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))'

#specify page size to optimise caching memory required on DC
$objSearcher.PageSize = 200

#speed up the search by specifying only the properties we need
$colProplist = "distinguishedname"
foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) | out-null } 

#use FindAll since we will potentially return many results
$colResults = $objSearcher.FindAll()

if ($colResults -ne $null) { 
    foreach($colResult in $colResults) {
        write-host ($colResult.Properties).distinguishedname
    }
}

We could also search for a single, specific user like so:

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(sAMAccountName=alkaneuser))'

And we could perform a simple search for this single user like so:

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(sAMAccountName=alkaneuser))'

#use FindOne since we're only returning a single result
$colResults = $objSearcher.FindOne()

if ($colResults -ne $null) { 
    write-host ($colResults.Properties).distinguishedname
}

All of the above searches are used to query the whole of AD.  But what if we wanted to only query a single object?  For example, an AD group?

What is ADSI?

[ADSI] is a type accelerator that represents the class System.DirectoryServices.DirectoryEntry.  It is used to bind directly to objects such as an AD group, an AD user or an AD computer.  It can then be used to update the attributes for a particular object.

In the above example, when we perform a search using FindOne() or FindAll() it returns one or many System.DirectoryServices.SearchResults.  We cannot modify the attributes of a search result – we use search results to simply print out attributes that we’re interested in.

If we wanted to update an attribute for an object returned as a search result, we need to create an ADSI object from this search result like so:

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(sAMAccountName=alkaneuser))'
$colResults = $objSearcher.FindOne()

if ($colResults -ne $null) { 
    
    #get path of the object from search result
    $pathToObject = ($colResults.Properties).adspath[0]

    #convert/cast search result object path to an ADSI object
    $user = [adsi]($pathToObject)

    #update the department
    $user.PutEx(1, "department", "alkane dept");

    #save it
    $user.SetInfo()
}

In the above example, we update the department attribute for a user called alkaneuser and set it to “alkane dept”.

Use ADSI to Check if a Computer is a Member of an AD Group

Other Posts in this Series:

This post provides an example of how we can use ADSI to check if a computer is a member of an AD Group using the [ADSISearcher] type accelerator:

#remember that this is used as a regular expression (using -match), so escape any brackets etc with a back slash
$ADGroup = "Example_AD_Group"
$machineName = "AlkaneMachine"

#check if computer is a member of the group
$ADGroupObj = (([ADSISearcher] "(&(objectCategory=computer)(objectClass=computer)(cn=$machineName))").FindOne().Properties.memberof -match "CN=$ADGroup,")

if ($ADGroupObj -and $ADGroupObj.count -gt 0)
{
    #computer is a member - do something!
}

Use ADSI to Check if a User is a Member of an AD Group

Other Posts in this Series:

This post will provides an example of how we can use ADSI to check if a user is a member of an AD Group using the [ADSISearcher] type accelerator:

#remember that this is used as a regular expression (using -match), so escape any brackets etc with a back slash
$ADGroup = "Example_AD_Group"
$userName = "AlkaneUser"
    
#check if user is a member of the group
$ADGroupObj = (([ADSISearcher] "(&(objectCategory=person)(objectClass=user)(sAMAccountName=$userName))").FindOne().properties.memberof -match "CN=$ADGroup,")

if ($ADGroupObj -and $ADGroupObj.count -gt 0)
{
    #user is a member - do something!
}