Searching the Active Directory using PowerShell

In my last blog, I provided a sample PowerShell script that shows how to use the System.DirectoryServices namespace from .NET Framework to bulk create users. Now I will show you can you can use the same namespace to search the Active Directory.

We have a lot of customers wanting to get the last time a user has logged on the system. Since there is no way to create an LDAP query to give you the last time a user has logged on, I figured that it is best to get all the users into memory and then filter them using PowerShell’s handy cmdlets to do the work.

First, I have to create the function that enumerates all the users. I did this by using the System.DirectoryServices.DirectoryEntry class and System.DirectoryServices.DirectorySearcher class. The section of code below shows how to do this:

  1: #'===================================================================
  2: #' This function will get all the users in an OU &; its sub OUs
  3: #'===================================================================
  4: function FindAllUsers()
  5: {
  6:     $searchFilter = "(&(objectCategory=user)(objectClass=user))" 
  7:     $deBaseDN = new-object System.DirectoryServices.DirectoryEntry $BaseDN, $domAdmin, $domPass
  8:     $deBaseDN.RefreshCache()
  9:     
  10:     #start-sleep 10
  11:     $deSearcher = new-object System.DirectoryServices.DirectorySearcher 
  12:     $deSearcher.SearchRoot = $deBaseDN
  13:     $deSearcher.SearchScope = "subtree"
  14:     $deSearcher.Filter = $searchFilter
  15:     $deSearcher.PageSize = 1000
  16:     $hr  = $deSearcher.PropertiesToLoad.Add("distinguishedname")
  17:     $hr  = $deSearcher.PropertiesToLoad.Add("lastlogontimestamp")
  18:     $src = $deSearcher.FindAll()
  19:     
  20:     return $src
  21: }

You will need to set some of the variables ahead of time that I normally put at the beginning of the script:

  1: #'===================================================================
  2: #' Specify the your environment specific settings
  3: #'===================================================================
  4: $domain   = "YOURDOMAIN"
  5: $searchDN = "DC=yourdomain,DC=com"
  6: $BaseDN   = "LDAP://" + $domain + "/" + $searchDN
  7: $domAdmin = "YOURDOMAIN\Administrator"
  8: $domPass  = "!Password!"

I then save all the users found and store it in a variable called $allusers, which is the unfiltered list of users.

  1: $allusers = FindAllUsers

I need to filter down the list and only include users that do have logged into the system. If a user has not logged into the system, the lastLogonTimestamp does not exist, so I filtered it using the command below and stored it in a variable called $withlastlogons:

  1: $withlastlogons = $allusers | where-object {$_.Properties["lastlogontimestamp"] -ne $null }

I then created a new variable called $filterusers where it is a modified version of $withlastlogons. I did this because the lastLogonTimestamp property value needed to be converted so I can compare it with date values that are native to PowerShell.

  1: $filterusers = $withlastlogons | select-object @{Name="LastLogonTimeStamp"; Expression = {[datetime]::fromfiletime($_.Properties["lastlogontimestamp"][0])}}, @{Name="DN"; Expression = {$_.Properties["distinguishedname"][0]}}

The last thing I had to do is to filter it relative to the current date. If I want to pull all the users that has logged and it has been over 10 days since they last logged on, I would use the filter:

  1: $filterusers | where-object {$_.lastlogontimestamp -lt (get-Date).AddDays(-10)}

Below is the final code along with some other samples on how to pull the data:

  1: #'===================================================================
  2: #' Specify the your environment specific settings
  3: #'===================================================================
  4: $domain   = "YOURDOMAIN"
  5: $searchDN = "DC=yourdomain,DC=com"
  6: $BaseDN   = "LDAP://" + $domain + "/" + $searchDN
  7: $domAdmin = "YOURDOMAIN\Administrator"
  8: $domPass  = "!Password!"
  9:   
  10:  
  11: #'===================================================================
  12: #' This function will get all the users in an OU &; its sub OUs
  13: #'===================================================================
  14: function FindAllUsers()
  15: {
  16:     $searchFilter = "(&(objectCategory=user)(objectClass=user))" 
  17:     $deBaseDN = new-object System.DirectoryServices.DirectoryEntry $BaseDN, $domAdmin, $domPass
  18:     $deBaseDN.RefreshCache()
  19:     
  20:     #start-sleep 10
  21:     $deSearcher = new-object System.DirectoryServices.DirectorySearcher 
  22:     $deSearcher.SearchRoot = $deBaseDN
  23:     $deSearcher.SearchScope = "subtree"
  24:     $deSearcher.Filter = $searchFilter
  25:     $deSearcher.PageSize = 1000
  26:     $hr  = $deSearcher.PropertiesToLoad.Add("distinguishedname")
  27:     $hr  = $deSearcher.PropertiesToLoad.Add("lastlogontimestamp")
  28:     $src = $deSearcher.FindAll()
  29:     
  30:     return $src
  31: }
  32:  
  33: #'===================================================================
  34: # This illustrates how to call the function "FindAllUsers" and then
  35: # processes ALL users &; only add to the collection if the
  36: # lastlogontimestamp exist. The collection is named $withlastlogons 
  37: #'===================================================================
  38: $allusers = FindAllUsers
  39: $withlastlogons = $allusers | where-object {$_.Properties["lastlogontimestamp"] -ne $null }
  40:  
  41: #'===================================================================
  42: # This outputs the lastlogontimestamp in raw format
  43: #'===================================================================
  44: $withlastlogons | % { $_.Properties["lastlogontimestamp"][0] }
  45:  
  46: #'===================================================================
  47: # This outputs the lastlogontimestamp in date format
  48: #'===================================================================
  49: $withlastlogons | % { [datetime]::fromfiletime($_.Properties["lastlogontimestamp"][0]) }
  50:  
  51: #'===================================================================
  52: # This illustrates how to get the lastLogonTimeStamp in date format &;
  53: # stuff it in a variable name $dates
  54: #'===================================================================
  55: $dates = $withlastlogons | % { [datetime]::fromfiletime($_.Properties["lastlogontimestamp"][0]) }
  56:  
  57: #'===================================================================
  58: # This creates a collection of users w/ the LastLogonTimeStamp &; DN
  59: # and retrieve the users that have not logged in for over 10 days
  60: #'===================================================================
  61: $filterusers = $withlastlogons | select-object @{Name="LastLogonTimeStamp"; Expression = {[datetime]::fromfiletime($_.Properties["lastlogontimestamp"][0])}}, @{Name="DN"; Expression = {$_.Properties["distinguishedname"][0]}}
  62: $filterusers | where-object {$_.lastlogontimestamp -lt (get-Date).AddDays(-10)}

Have fun!