Use ADSI to Search Users in Active Directory

Other Posts in this Series:

This post provides a simple example of how we can use 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
}      

Use ADSI to Set and Clear Active Directory Attributes

Other Posts in this Series:

This post provides an example of how we can use ADSI to set and clear Active Directory Attributes.  We search for a user called “alkaneuser” and set or clear the department attribute.

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(sAMAccountName=alkaneuser))'
	
$colProplist = "samaccountname","department"
foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) | out-null } 
	
$colResults = $objSearcher.FindOne()	
 
if ($colResults -ne $null)
{   	  
    $user = [adsi]($colResults.Properties).adspath[0]

    #set attribute
    $user.Put("department", "Example department2"); 
    $user.setinfo();
        
    #clear attribute - uncomment as required
    #$user.PutEx(1, "department", 0); 
    #$user.setinfo();   	
}

Use ADSI and FromFileTime to Convert Datetime Attributes in Active Directory

Other Posts in this Series:

This post explains how we can use ADSI and FromFileTime to convert datetime attributes in Active Directory to a human-readable date and time.

You’ll notice when you return attributes such as lastlogon, lastlogontimestamp and lastpwdset that the format of the results is something like: 132586443741396519

What the heck does that mean??  Well it’s known as the ‘Windows NT time format’ and represents the Universal Time Coordinated (UTC) of the number of 100-nanosecond intervals that have elapsed since the 0 hour on January 1, 1601.

In this post we search for all enabled users in AD and print out their lastlogontimestamp value, in the format dd/MM/yyyy.

$objSearcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))'
$objSearcher.PageSize = 200
	
$colProplist = "samaccountname","lastlogontimestamp"
foreach ($i in $colPropList) { $objSearcher.PropertiesToLoad.Add($i) | out-null } 
	
$colResults = $objSearcher.FindAll()
	
$count = 0

foreach ($objResult in $colResults)
{
   $username = ($objResult.Properties).samaccountname     
   $tdt = [int64]($objResult.Properties).lastlogontimestamp[0].ToString()
   $dt = [datetime]::FromFileTime($tdt).ToString('dd/MM/yyyy')
   write-host $dt
}

 

 

Use ADSI to List Nested Members of an AD Group

Other Posts in this Series:

This post includes an example of how we can use ADSI to list nested members of an AD group.  In other words, if the group contains nested groups, it will iteratively search all the members of those nested groups too.

$global:found = 0;

function Find-Enabled-Members
{
    Param
    (
       [string]$DN
    )

    if (!([adsi]::Exists("LDAP://$DN"))) {
        write-host "$DN does not exist"
        return     
    }

    $group = [adsi]("LDAP://$DN")

    ($group).member | ForEach-Object {

        $groupObject = [adsisearcher]"(&(distinguishedname=$($_)))"  
        $groupObjectProps = $groupObject.FindOne().Properties
             
        if ($groupObjectProps.objectcategory -like "CN=group*") { 
            #search nested group
            Find-Enabled-Members "$_"
        } else {
            $userenabled = ($groupObjectProps.useraccountcontrol[0] -band 2) -ne 2

            #which properties to choose from (these are case-sensitive!)
            #write-host $groupObject.FindOne().Properties.PropertyNames
            if ($userenabled) {
                write-host "Found $($groupObjectProps.samaccountname) in $($group.Name)"
                $global:found += 1
            }              
        }

    }
}

Find-Enabled-Members "CN=GroupToSearch,OU=Applications,DC=alkanesolutions,DC=co,DC=uk"
write-host "Total members incl nested members is $($global:found)"
write-host "Total direct members is $($group.member.count)"

 

 

Use ADSI to Modify an AD Group

Other Posts in this Series:

This post provides a simple example of how we can use ADSI to modify an AD group.  In this example, we modify the description attribute of an AD group.  You can also use ADSI to clear the attributes for an AD group.

#OU containing the AD group
$adGroupOU="OU=Application,OU=Groups,DC=alkanesolutions,DC=co,DC=uk"

#AD group name
$addADGroup = "CN=alkane_ad_group"

#Full distinguished name of AD group		
$distinguishedName = "$addADGroup,$adGroupOU"

#check if exists
$group = ([ADSISearcher] "(distinguishedName=$distinguishedName)").FindOne()

if ($group -ne $null)
{		
    #modify AD group description
    $adGroupObj = [ADSI]("LDAP://$($group.Properties.distinguishedname)")
    $adGroupObj.put('description',"Alkane description") 
    $adGroupObj.SetInfo()
}

Use ADSI to Create an AD Group

Other Posts in this Series:

This post provides a simple example of how we can use ADSI to create an AD group.

$adGroupType = @{
    Global      = 0x00000002
    DomainLocal = 0x00000004
    Universal   = 0x00000008
    Security    = 0x80000000
}

#OU containing the AD group
$adGroupOU="OU=Application,OU=Groups,DC=alkanesolutions,DC=co,DC=uk"

#AD group name
$addADGroupName = "alkane_group"

#Full distinguished name of AD group		
$distinguishedName = "CN=$addADGroupName,$adGroupOU"

#check if exists
$group = ([ADSISearcher] "(distinguishedName=$distinguishedName)").FindOne()

if ($group -eq $null)
{	
    #group doesn't exist

    #get OU
    $adsiADGroup = [adsi]("LDAP://$adGroupOU")

    #create group in OU
    $newGroup = $adsiADGroup.Create('group', "CN=$addADGroupName")

    #Make it a global security group
    $newGroup.put('grouptype',($adGroupType.Global -bor $adGroupType.Security))
    $newGroup.put('samaccountname',$addADGroupName)
    $newGroup.SetInfo()	
   
}

Use ADSI to Delete an AD Group

Other Posts in this Series:

This post provides a simple example of how we can use ADSI to delete an AD group.

#OU containing the AD group
$adGroupOU="OU=Application,OU=Groups,DC=alkanesolutions,DC=co,DC=uk"

#AD group name
$addADGroup = "CN=alkane_ad_group"

#Full distinguished name of AD group		
$distinguishedName = "$addADGroup,$adGroupOU"

#check if exists
$group = ([ADSISearcher] "(distinguishedName=$distinguishedName)").FindOne()

if ($group -ne $null)
{		
    #delete AD group
    $adGroupObj = [ADSI]"LDAP://$adGroupOU"
    $adGroupObj.Delete("Group",$addADGroup)
}

The Difference Between ADSI and ADSISearcher

Other Posts in this Series:

This post will examine the difference between 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 Migrate AD Group Members

Other Posts in this Series:

This post provides a function which enables us to use ADSI to migrate AD group members. We can also specify whether to copy or move the group members.


function Migrate-ADGroup
{
    Param
    (
       [string]$sourceDN, 
       [string]$targetDN,
       [bool]$move
    )
   
   if (!([adsi]::Exists("LDAP://$sourceDN"))) {
        write-host "$sourceDN does not exist"
        return     
   }
 
   if (!([adsi]::Exists("LDAP://$targetDN"))) {
        write-host "$targetDN does not exist"
        return
   }

    $sourceDNADSI = [ADSI]"LDAP://$sourceDN"
    $targetDNADSI = [ADSI]"LDAP://$targetDN"

    try {
        $sourceDNADSI.member | ForEach-Object {
    
            $groupObject = [adsisearcher]"(distinguishedname=$($_))"   

            if ($move) {
                write-host "Moving $($groupObject.FindOne().Properties.name)"
                try { $targetDNADSI.Add("LDAP://$_") } catch {}
                try { $sourceDNADSI.Remove("LDAP://$_") } catch {}
            } else {
                write-host "Copying $($groupObject.FindOne().Properties.name)"
                try { $targetDNADSI.Add("LDAP://$_") } catch {}
            }
        }
    } catch {
        write-host $_.Exception.Message
    }
}

$sourcegroup = "CN=application1,OU=Apps,DC=alkanesolutions,DC=co,DC=uk"
$targetgroup = "CN=application2,OU=Apps,DC=alkanesolutions,DC=co,DC=uk"

#source group to migrate from, target group to migrate to, false (copy members) or true (move members)
Migrate-ADGroup $sourcegroup $targetgroup $false

Use ADSI to Find Logon Workstations in Active Directory

Other Posts in this Series:

This post provides an example of how we can use ADSI to find logon workstations in Active Directory.

I recently needed to search through all users in Active Directory and find logon workstations for those accounts that had them.  Logon workstations for a user account essentially restricts what workstations a specific user account can log on to.

A lot of this code example is based on using the ADSI Searcher to find user accounts in Active Directory.

$Root = [ADSI]"LDAP://OU=users,DC=alkanesolutions,DC=co,DC=uk"
$Searcher = new-object System.DirectoryServices.DirectorySearcher($Root)
$Searcher.filter = "(&(objectCategory=person)(objectClass=user))"
$Searcher.PageSize = 200

$Searcher.FindAll() | % {
  
    $user = [adsi]$_.Properties.adspath[0]

    $ErrorActionPreference = "silentlycontinue"
    If (($user.get("userWorkstations")) -ne $null)
    {
        $workstations = $user.get("userWorkstations")
        $workstationsArray = $workstations.split(",")
        foreach($ws in $workstationsArray) {
            write-host $samaccount $ws
        }
    }
}