Hey, Scripting Guy! Question

Hey, Scripting Guy! We created some groups that were used by our student interns. The student interns have now returned to college (yeah, it was a while ago). To be honest, I had actually forgotten about these groups, but the other day I was doing some work in Active Directory Users and Computers and I happened across one of these student intern groups. That reminded me that I had actually created several different groups for the students. Luckily, I had placed the word intern in the groups' description property. Is there a way I can easily find all of the intern groups, so I can look at them and make a decision as to their final disposition?

- RH

SpacerHey, Scripting Guy! Answer

Hi RH,

I am listening to "Lost Someone" by James Brown on my Zune. It’s the recording that was made live from the Apollo Theatre back in 1963. Man, what a voice. It is easy to see why he was called the hardest working man in show business. Anyway, it seems that the song is apropos because it seems you have lost several groups. Luckily, we can use Windows PowerShell to search Active Directory and return a listing of the missing groups.

This week we are talking about searching Active Directory. The Active Directory Script Center Hub has links to a number of resources related to working with Active Directory. You will also find in this section of the Script Center Script Repository a good collection of scripts that illustrate searching Active Directory. There are several scripts in the Community-Submitted Scripts Center that also illustrate searching Active Directory. The “Hey, Scripting Guy!” Active Directory Archive is also an excellent source of information for searching Active Directory. Not to be outdone, the Scripting Guide has an ADSI Scripting Primer.

We decided to write a script named SearchGroupDescriptions.ps1. This script connects to the default domain and finds all the groups that have a description that contains the word “intern” in it. A similar script written in VBScript is available in the Script Center Script Repository. The SearchGroupDescriptions.ps1 script is seen here:

$Filter = "(&(ObjectCategory=group)(Description=*intern *))"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Filter)
$Searcher.Findall() | 
ForEach-Object `
  -Begin { "Results of $Filter query: " } `
  -Process { 
                    $_.properties.item("DistinguishedName")
                    $_.properties.item("Description")
                    "`r"
                   } `
  -End { [string]$Searcher.FindAll().Count + " $Filter results were found" }

If we look in Active Directory Users and Computers, we find we have an organizational unit named Students. In there, we find a group that has a description that contains the word “intern” in it. This is seen here:

Image of the Students organizational unit

 

Perhaps the hardest thing about searching Active Directory is configuring the appropriate search filter. If you would like some refresher information about the LDAP dialect search filter syntax, refer to yesterday’s “Hey, Scripting Guy!” article.

In our search filter, we are using two Active Directory attributes: the ObjectCategory and the Description. If we were to use a search filter that returned all groups, it would look like the following:

"ObjectCategory=group"

Of course there are lots of groups in Active Directory—both those we created and those created by the system. On the other hand, if we were to look for objects that had a description that contained the word “intern” in them, the query would look like this one:

"Description=*intern*"

When we put the two together, we need to add some parentheses and an ampersand. The query now looks like the one seen here:

"(&(ObjectCategory=group)(Description=*intern*))"

On our computer, when we run the script with the above query, our results are not exactly what we were expecting. This is because an additional group is returned. This is one that we do not use, but it was created by the system for use by the Internet Information Services. D'oh! This is seen here:

Image of the query results with one additional group

 

No problem. From looking at the results seen in that image, it seems the match was the word “Internet”; all we need to do is to include a space after the letter "n" in the word “intern”. The quickly revised query is seen here:

$Filter = "(&(ObjectCategory=group)(Description=*intern *))"

The easiest way to search Active Directory when using Windows PowerShell 1.0 is to create an instance of the System.DirectoryServices.DirectorySearcher. To do this we use the New-Object cmdlet and tell it we want to create the System.DirectoryServices.DirectorySearcher class, and we feed it the filter we arrived at from our earlier experimentation. We store the returned DirectorySearcher object in the $Searcher variable as seen here:

$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Filter)

We use the FindAll method from the DirectorySearcher object to find everything that matches our query. We pipeline the resulting object as seen here:

$Searcher.Findall() |

Next we use the ForEach-Object cmdlet to allow us to work our way through objects as they come across the pipeline. The ForEach-Object cmdlet is documented on the Script Center. We use the back-tick line continuation character (`) because we want to line up the -Begin, -Process, and -End parameters. This is seen here:

ForEach-Object `

The -Begin parameter contains information that will run once for all the stuff that comes across the pipeline. We use it here to provide a bit of feedback to the user running the script. Because we display the query that is contained in the $Filter variable, this can actually provide a bit of troubleshooting information in the case that the script returns unexpected results. This is shown here:

-Begin { "Results of $Filter query: " } `

The -Process block is executed once for each object that comes across the pipeline. In this script, it is executed once for each group that was identified as a match for our query. The -Process block opens a set of curly brackets as seen here:

-Process {

Now we decide which information we want our script to return. In this script, we are interested in the DistinguishedName attribute because it will tell us exactly where the group was found. We also want the group description so that we can ensure the match was legitimate. We also decide it would be nice to include a carriage return to separate the groups from each other. The code that prints out the two attributes is seen here:

$_.properties.item("DistinguishedName")
                    $_.properties.item("Description")
                    "`r"
                   } `

Now we need to print out confirmation that the script has completed running. To do this, we use the -End parameter of the ForEach-Object cmdlet. We convert the number that represents the count of the items returned by the query into a string so that we can concatenate it with another string that tells us how the results were found. This is seen here:

-End { [string]$Searcher.FindAll().Count + " $Filter results were found" }

The display of the matching groups is seen here:

Image of the display of the matching groups

 

Well, SL, this concludes our discussion about searching Active Directory for a listing of all computers in the domain. Join us tomorrow as Searching Active Directory Week continues. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys