RSS All Posts

RSS PowerShell Posts

Tags

2142 Active Directory Administrativia Announcements Battlefield Blogging Cricket Deployment Deployment4 Get-PSUGUK Group Policy HowTo Linux MDT MDT 2010 Microsoft Deployment Toolkit MSDN Music Permissions Personal PowerGui Power Lines PowerShell PowerShell Groups PowerShell Support PowerShell Tools PowerShell V2 Presentations PSUGAU Quick Tips Scripting SDDL Security Tech Talk Ubuntu User Groups Virtualisation VMware Infrastructure Client WAIK Weekly Poll Windows 7 Windows Automation Installation Kit Windows Server 2003 Windows Server 2008 XML

Archives

Meta


« | Main | »

Searching Active Directory with PowerShell

By Adam Bell | February 19, 2007

Being quite the lazy admin that I am, whenever I’m scripting for AD I like to use short names when referring to objects. As an example, if I want to add the user BellA into the Domain Admins group I don’t want to provide the distinguishedName for both objects .

The easy solution to this is to have enough information to hand, to allow a quick rummage through the Directory, and let the script
retrieve the details it needs. In fact being able to extract information from an AD search is pretty much the corner stone of any AD scripting tool box, as you’ll see over the next couple of posts.

I’ll show you a couple of ways of searching and then mention a few points to be aware of regarding performance, and your impact on the servers whose workload you’re adding to.


# ---------------------------------------------------------------------------------------------------
function Find-DN
# ---------------------------------------------------------------------------------------------------
{
Param (
  $sADobjectName,
  $sADobjectType
  )
  $query = new-object system.directoryservices.directorysearcher
  $root = [adsi]""
 
  $query.SearchRoot = $root
  $query.filter = "(&(ObjectClass=$sADobjectType)(cn=$sADobjectName))"
  $query.SearchScope = "subtree"
  $result = $query.findOne()
  
  if ($result -eq $null)
  {
    return $null
  }
  else
  {
    $ADobject = $result.GetDirectoryEntry()
    return $ADobject.distinguishedName
  }
}

There isn’t a lot of specific PowerShell code in the function above. Its pretty LDAP orientated. We have a simple function that returns you the distinguishedName of an object when you provide the objectClass and cn.
So to find the DN of my test user account:


Find-DN "BellA" "user"

The $result variable is a System.DirectoryServices.SearchResult which we cast into System.DirectoryServices.DirectoryEntry with the GetDirectoryEntry() method. I like this as it gives us a much nicer way of retrieving the objects properties.

The other method of searching AD I have seen, is similar, but with a PS slant to it:


# ---------------------------------------------------------------------------------------------------
function Find-DN2
# ---------------------------------------------------------------------------------------------------
{
Param (
  $sADobjectName,
  $sADobjectType
  )
  $query = new-object system.directoryservices.directorysearcher
  $root = [adsi]""
  
  $query.SearchRoot = $root
  $query.SearchScope = "subtree"
 
  $ADobject = $query.findAll() | where-object {$_.properties.objectclass -eq $sADobjectType `
    -and $_.properties.cn -eq $sADobjectName}
  $props = $ADobject.Properties
  
  if ($ADobject -eq $null)
  {
    return $null
  }
  else
  {
    foreach ($prop in $props)
    {
      return $prop.distinguishedname
    }  
  }
}

Straight away you can see some PowerShell in the function above. Rather then use the $query.filter, we pipe the results into a where-object and then filter for our search criteria. We also handle the properties returned differently.

This function would be called exactly the same as our previous example:


Find-DN2 "BellA" "user"

Now I’ve had problems with this function. I’ve found it worked fine for “some” objects, but I have had trouble retrieving others. Despite the SearchScope being set to “SubTree” I’ve had it fail to retrieve objects that are a few OU’s deep. I suspect this is a flaw with my function as apposed to anything flawed in PS ;)

Search Tips
So now we’ve seen the basics, I thought it worth mentioning a couple of ways to improve on performance, and speed:

[1] Scope: If you can, narrow your SearchScope. There’s three options: “Base”, “OneLevel”, and “SubTree”. If you’re not sure, there’s a good explanation on MSDN here.

[2] Filter: Always try and provide a filter to reduce how much data you’re trying to retrieve. There’s obviously not much point retrieving every user account in your domain and enumerating through them all just for the one you want. Think about the Filter() method, and use the relevant FindOne() FindAll() methods.

I’m not sure how our second examples works with this. My hunch is that it retrieves ALL the objects and trawls through each with the where-object. I could be wrong here of course ;)

[3] Properties: If you’re only after say, the distinguishedName it would be a good idea to reduce the properties returned by using the PropertiesToLoad method.

The Microsoft Scripting Guy’s, have an example here

Topics: Active Directory, PowerShell | 2 Comments »

2 Responses to “Searching Active Directory with PowerShell”

  1. Chris Warwick Says:
    February 19th, 2007 at 9:20 pm

    Hi Adam,

    Thanks for the posts! I have a couple of comments on the Directory Searcher samples you show in this post:

    Your first sample is the correct way to search. It’s ALWAYS more efficient to filter in the provider (whether you’re using directorySearcher() or get-childitem – whatever). When you do a findAll in the second sample you may be trying to retrieve 1000’s of objects in any reasonably sized directory.

    Secondly, this is probably the reason your second sample gives inconsistent results. The search will only return a fixed amount of information in one chunk – even if more items match your query. If the entry you need happens to be in the chunk returned then your code will work. If it’s not then the code will fail. The way around this is to do a paged query (set the pagezise attribute to “1000″ – the actual value is fairly irrelevent, but 1000 is the value used by convention). This will ensure you get ALL the results back (could be SLOW!). Alternatively – and preferably – you can get around this by filtering in the provider as in the first sample. [I've just read your Search Tips and you allude to this there:-)]

    Thirdly (a small point) – findone() under the covers actually does a findall() and returns the first result. I find this misleading so I always explicitly use findall() because it’s clear what’s going on. You might think findone() is quicker, but actually it’s not!

    Finally, you’re right – it’s more functional to return a directoryEntry rather than a searchresult – but retrieving the directoryEntry always results in ANOTHER LDAP query – so if efficiency is important you should only call GetDirectoryEntry() when you really need too.

    HTH!

    Chris

    PS. I only know this stuff because I read “The .NET Developer’s Guide to Directory Services Programming” by Kaplan & Dunn. An excellent book which I recommend to people whenver I get the chance!

  2. AdamBell Says:
    February 20th, 2007 at 9:50 am

    HI Chris,

    Thanks for the thoughtful comment.

    I am aware of page sizing when retrieving result from AD. This isn’t the cause of problems with the second example as I am using a very small test domain in VMware to sandbox my testing from live. I’m glad you raised the point though. I had been deliberatly keeping my examples simple.

    I have read a couple of developers rating the book you mention highly. I should really add it to my Amazon Wishlist.

    While we’re on the topic of books, if anyone is interested in good AD books then the two I highly recemend are:

    Active Directory Forestry
    by John Craddock and Sally Storey
    # ISBN-10: 0954421809
    # ISBN-13: 978-0954421809

    Active Directory Cookbook for Windows Server 2003 and Windows 2000
    By Robie Allen
    # ISBN-10: 0596004648
    # ISBN-13: 978-0596004644

    They are no related to PowerShell, howerver the AD content of both is excellent!

    Cheers

    Adam

Comments