TenBrink Tech Technology | Life

9Jan/08

Today’s Fun with PowerShell

On this episode of Fun with PowerShell, we dive into the realm of retrieving account information from various sources using the DirectoryEntry object. If you are familiar with the ADSI interfaces, this should give you a good idea of how to transfer knowledge from that medium to the PowerShell/.NET way of thinking.

All the code examples are based on work I've done today, but as always YMMV. There's always a better, faster, more creative way to do coding, and I highly encourage that thinking.

So, what is a DirectoryEntry object? Simply an instance of that class, borrowed from the .NET framework. You can use a DirectoryEntry to access any of the ADSI service providers, so LDAP, IIS, NDS, and WinNT. I am going to touch on LDAP and WinNT for this article.

Let's start with the basics. You have an Active Directory domain. You want to find all servers in that domain (those that have checked in, at least). First, you need a starting point for your search.

PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://dc=company,dc=com")

Could I specify a particular domain controller? Sure!

PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://domainctrl1.company.com:389")

What if we wanted to start further down the tree?

PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://ou=something,ou=somewhere,dc=company,dc=com")

or

PS C:\> $ad = New-Object DirectoryServices.DirectoryEntry("LDAP://domainctrl1.company.com:389/ou=something,ou=somewhere,dc=company,dc=com")

Can we make it even shorter? If your workstation is a member of the domain you are looking for, then we can.

PS C:\> $ad = [ADSI]

This is very nifty. At the lowest level, we are just selecting a particular object in the directory. For the purposes of our search, this happens to be a root or OU level of the directory. You could pluck out a particular individual object and manipulate from there. Try it with your user account.

PS C:\> $user = New-Object DirectoryServices.DirectoryEntry("LDAP://cn=Dillon TenBrink,ou=geeks,dc=company,dc=com") | fl *

Now that we have our $ad variable, representing our search root, we need a searcher.

PS C:\> $searcher = New-Object DirectoryServices.DirectorySearcher($ad)

Once we have a searcher, we have a selection of settings to flip on it to execute our query.

PS C:\> $searcher

CacheResults             : True
ClientTimeout            : -00:00:01
PropertyNamesOnly        : False
Filter                   : (objectClass=*)
PageSize                 : 0
PropertiesToLoad         : {}
ReferralChasing          : External
SearchScope              : Subtree
ServerPageTimeLimit      : -00:00:01
ServerTimeLimit          : -00:00:01
SizeLimit                : 0
SearchRoot               : System.DirectoryServices.DirectoryEntry
Sort                     : System.DirectoryServices.SortOption
Asynchronous             : False
Tombstone                : False
AttributeScopeQuery      :
DerefAlias               : Never
SecurityMasks            : None
ExtendedDN               : None
DirectorySynchronization :
VirtualListView          :
Site                     :
Container                :

From here, we go to work defining our filter and the properties we would like to get back.

PS C:\> $searcher.Filter = "(&(objectClass=Computer)(operatingSystem=*Server*))"

PS C:\> $searcher.SizeLimit = 1000

PS C:\> $searcher.PropertiesToLoad.Add("cn")

PS C:\> $searcher.PropertiesToLoad.Add("operatingSystem")

You could define more options if you needed. Check the documentation on the DirectorySearcher class for more information.

From here you can execute the .FindOne() or .FindAll() methods to return the results of your query.

Let's move on to a different animal using the DirectoryEntry class to show some versatility. This time we'll use the WinNT ADSI interface.

The WinNT interface is still, in my opinion, the best way to get information on local accounts and groups remotely on a Windows box. Here we are going to use it to get a list of the local users on a server PDX01. First, like before, we create our DirectoryEntry.

PS C:\> $Sys = New-Object DirectoryServices.DirectoryEntry("WinNT://PDX01,computer")

If you performed a $Sys | fl * at this point, you can see all the properties of this object, just like your user account from AD above when we're using the LDAP provider. What we're after though, is the members, or "children", of this computer.

PS C:\> $Sys.children

Executing this command will return users, groups, and services. Since we're just after the users, let's filter it down.

PS C:\> $Sys.children | where {$_.SchemaClassName -match "User"}

Much better. Now we have our list of users. If we wanted to save this for later, dump it into a variable.

PS C:\> $Users = $Sys.children | where {$_.SchemaClassName -match "User"}

And then pipe all the details out to the console.

PS C:\> $Users | fl *

There you go.

From both of these starting blocks, you can then use your scripting prowess to harness the results of these queries to do your bidding. Take the AD server query, drop it into a loop with the local account query, and you have a quick and powerful local account audit script. Extend it a little more and take the local users, filter by their password age attribute and you have a functional password age checking script for local accounts. The possibilities are endless once you can retrieve the data sets.

Is this easier in vbScript or VB.NET?

My personal preference is to write these particular queries in VB.NET because I typically have more data analysis to do against other systems. Just my preference. That's not to say I couldn't write those interfaces and analysis in PowerShell; just that I haven't found a need quite yet. The PowerShell way is very useful though, and quicker, when just plain, raw data is required and I use it frequently in those scenarios.

Benefits over vbScript? You can walk though this line by line, in real time, and adjust as you go to get your results exactly the way you want them. You wouldn't want to write that script, execute, correct, and run again from the start over and over until you got what you wanted, right? PowerShell provides the advantage of having feedback line-by-line as you progress. Then, save your refined statements as a .ps1 file and reuse it later or call it in other scripts.

Happy scripting!

  • Share/Bookmark
Comments (1) Trackbacks (0)
  1. This is all good stuff and absolutely required when working with member servers and desktops. For those who weren’t aware, Quest software has a free set of cmdlets for working with Active Directory. This makes working with AD incredibly easy. For those interested, there is more information on this topic at turbochargead.org

    Jeffery Hicks
    Microsoft PowerShell MVP
    http://blog.sapien.com
    follow me: http://www.twitter.com/JeffHicks

    “Those who forget to script are doomed to repeat their work.”

    Now Available: Managing Active Directory With Windows PowerShell: TFM


Leave a comment


No trackbacks yet.