How Can I Populate a List Box With the CNs of All the OUs in My Domain?

How Can I Populate a List Box With the CNs of All the OUs in My Domain?

  • Comments 1
  • Likes
Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I populate a list box with the CNs of all the OUs in my domain?

-- AK

SpacerHey, Scripting Guy! AnswerScript Center

Hey, AK. That’s an interesting question, AK, and we’ll do our best to answer it. Of course, we should point out that the United States soccer team has just finished losing to the Czech Republic in the World Cup, and we’re so broken-hearted and disconsolate that we can barely type, let alone write. Therefore, if this column is poorly-written or if it doesn’t make any sense, well, you understand why.

Note. OK, granted, the Scripting Guy who writes this column doesn’t exactly consider himself a soccer fan; in fact, he spent the weekend watching college baseball on TV rather than the World Cup. In fact, he’s just pretending to be broken-hearted; that way he has an excuse in case this turns out to be a less-than-stellar column. (Usually he tries to blame lousy columns on his editor, but she always edits those parts out. But seeing as how she is a soccer fan and she is disconsolate about the U.S. loss, well, maybe he can sneak this past her.)

At any rate, under the most trying of circumstances (like we said, AK, surely you - and our editor - understand), here’s what we came up with:

<SCRIPT Language="VBScript">
    Sub Window_onLoad
        On Error Resume Next

        Const ADS_SCOPE_SUBTREE = 2

        Set objConnection = CreateObject("ADODB.Connection")
        Set objCommand =   CreateObject("ADODB.Command")
        objConnection.Provider = "ADsDSOObject"
        objConnection.Open "Active Directory Provider"
        Set objCommand.ActiveConnection = objConnection

        objCommand.Properties("Page Size") = 1000
        objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 

        objCommand.CommandText = _
            "SELECT Name, ADsPath FROM 'LDAP://dc=fabrikam,dc=com'" & _
                " WHERE objectCategory='organizationalUnit' ORDER BY Name"  
        Set objRecordSet = objCommand.Execute

        objRecordSet.MoveFirst
        Do Until objRecordSet.EOF
            Set objOption = Document.createElement("OPTION")
            objOption.Text = objRecordSet.Fields("Name").Value
            objOption.Value = objRecordSet.Fields("ADsPath").Value
            AvailableOUs.Add(objOption)
            objRecordSet.MoveNext
        Loop
    End Sub

    Sub ShowOUPath
        Msgbox AvailableOUs.Value
    End Sub
</SCRIPT>

<body>
    <select size="10" name="AvailableOUs" style="width:300"></select><p>
    <input id=runbutton type="button" value="Show OU Path" onClick="ShowOUPath">
</body>

This is a bit longer than most of the scripts you see in this column, so let’s take care of the easy things first. To begin with, the body of the HTA consists of just two items, a list box named AvailableOUs and a button with the label Show OU Path:

<select size="10" name="AvailableOUs" style="width:300"></select><p>
<input id=runbutton type="button" value="Show OU Path" onClick="ShowOUPath">

Note. If you aren’t familiar with HTAs, well, too bad!

Um, we mean, check out the HTA Developers Center here in the Script Center. Sorry; you know, what with the U.S. losing and everything, well, we’re just not ourselves today.

Both of these controls are pretty rudimentary. The button has been configured to run a subroutine named ShowOUPath any time said button gets clicked. Meanwhile, we don’t do much with the list box other than configure it to show 10 items at a time (size="10") and to be 300 pixels wide (style="width:300"). As you can see, we didn’t even specify any items to appear in this list box. That’s by design: after all, we want to dynamically populate the list box with the names of the OUs in our domain.

Note. Yes, you did say you wanted to populate the list box with the CNs of all your OUs. We decided to go with Name simply because the CN is an optional attribute and won’t necessarily be configured for all your OUs. However, we’ll show you how to modify the code to use the CN if you so desire.

No, we’re not doing this because of the World Cup. We’re always nice like that!

As for the subroutine ShowOUPath, all we do there is echo back the ADsPath for the OU selected in the list box (as you’ll see, the corresponding ADsPath is assigned as the value for each OU in the list box):

Msgbox AvailableOUs.Value

That’s not particularly exciting, but it is useful: after all, if you know the ADsPath then you can bind directly to the OU in Active Directory.

That leaves us with the two most important aspects of this code: retrieving the list of OUs from Active Directory, and dynamically adding those OUs to a list box. We’ve included both of these tasks within a single subroutine named Window_onLoad. As you probably know, any time an HTA or Web page includes a subroutine named Winodw_onLoad, that subroutine is automatically invoked any time the HTA/page is loaded or refreshed. By placing these two tasks in Window_onLoad we ensure that each time we start the HTA we’ll conduct an Active Directory search and load an up-to-date set of OU names into the list box.

As we noted, to retrieve the list of OUs we do an Active Directory search; that’s what this big block of code is all about:

Const ADS_SCOPE_SUBTREE = 2

Set objConnection = CreateObject("ADODB.Connection")
Set objCommand =   CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection

objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 

objCommand.CommandText = _
    "SELECT Name, ADsPath FROM 'LDAP://dc=fabrikam,dc=com'" & _
        " WHERE objectCategory='organizationalUnit' ORDER BY Name"  
Set objRecordSet = objCommand.Execute

objRecordSet.MoveFirst

Incidentally, we won’t be able to discuss the details behind conducting an Active Directory search; that’s well beyond the scope of our daily column. If you need those whys and wherefores, take a look at our two-part Tales from the Script series Dude, Where’s My Printer?

Instead, the only thing we’ll make particular note of is the query we use to retrieve our information:

objCommand.CommandText = _
    "SELECT Name, ADsPath FROM 'LDAP://dc=fabrikam,dc=com'" & _
        " WHERE objectCategory='organizationalUnit' ORDER BY Name"

All we’re doing here is asking for the Name and the ADsPath of all the objects in the fabrikam.com domain that have an objectCategory equal to organizationalUnit. It probably goes without saying that any object with an objectCategory equal to organizationalUnit will be an OU. Oh: and because a list like this is much easier to deal with when the items are presented in alphabetical order, we’ve tacked on this clause to sort the returned data by name: ORDER BY Name.

Good point: suppose you do want to return the CN rather than the Name. That’s fine; just replace any reference to the Name attribute with the CN attribute instead:

objCommand.CommandText = _
    "SELECT CN, ADsPath FROM 'LDAP://dc=fabrikam,dc=com'" & _
        " WHERE objectCategory='organizationalUnit' ORDER BY CN"

And now - at long last - we get to the heart of the matter: adding the OUs to the list box. After we call the Execute method and run our query against Active Directory we get back a recordset consisting of the Name and ADsPath of each OU in the domain. To add these OUs to the list box we use the following block of code:

Do Until objRecordSet.EOF
    Set objOption = Document.createElement("OPTION")
    objOption.Text = objRecordSet.Fields("Name").Value
    objOption.Value = objRecordSet.Fields("ADsPath").Value
    AvailableOUs.Add(objOption)
    objRecordSet.MoveNext
Loop

What’s going on here? Well, to begin with, we set up a Do Until loop that which runs until we reach the end of the recordset (objRecordset.EOF). Inside that loop we use the createElement method to create a “blank” list box item. For this new list box item we set the Text property to the OU Name and the Value property to the OU ADsPath:

objOption.Text = objRecordSet.Fields("Name").Value
objOption.Value = objRecordSet.Fields("ADsPath").Value

Note. If you’ve decided to use the CN rather than the name, just change the code accordingly: objRecordSet.Fields("CN").Value.

If you aren’t familiar with HTML, the Text property is simply the text that appears in the list box; the Value property is, well, a value associated with the item. Although the Text and the Value can be identical, they don’t have to be. We chose to use the Name for the Text property because that gives us nice, easy-to-read names like this:

Administration

Finance

Human Resources

Research

Sales

We then use the ADsPath as the Value; that makes it easy for someone to choose an OU, click a button, and then bind to the selected OU. The ADsPaths will look something like this:

LDAP://ou=Administration,dc=fabrikam,dc=com

LDAP://ou=Finance,dc=fabrikam,dc=com

LDAP://ou=Human Resources,dc=fabrikam,dc=com

LDAP://ou=Research,dc=fabrikam,dc=com

LDAP://ou=Sales,dc=fabrikam,dc=com

As you can see, the ADsPath - while important to have - doesn’t make for very good labels within the list box. That’s why we use the Name as the text that appears in the list box.

After assigning values to the Text and Value properties we then call the Add method to add the new item to the list box:

AvailableOUs.Add(objOption)

Believe it or not, that’s all we have to do. We call the MoveNext method to move to the next item in the recordset and then repeat the process, adding the second OU to the list box. This continues until we’ve reached the end of the recordset and - not coincidentally - added all the OUs to the list box.

And now we really should get to work on our next column. Of course, that’s not going to be easy, knowing that the U.S. likely needs to beat Ghana and Italy in order to advance. But, hey, the show must go on, right? Seeing as how it’s annual review time, we hope our manager will appreciate how we soldier on even though we’ve been emotionally devastated.

No check that: the emotional devastation usually comes after we’ve had our review.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • It isn't working :(

    "Msgbox" ?? Are you sure? It is for VBA, isn't it?

    Tom