Basic Active Directory Services Interface (ADSI) Scripting

ADSI is a set of COM objects that are used to programmatically manipulate a namespace. A namespace could be anything from the Active Directory to an application such as MS Exchange Server.


Since ADSI is based on COM, it may be scripted using any language with COM bindings (any language that supports accessing the COM subsystem). This means that you may access ADSI with C\C++, Visual Basic, Java, Perl or many other languages. You could even embed ADSI scripts into an MS Office macro using VBA as part of a workflow process. The possibilities are almost limitless.

ADSI Concepts

It is necessary to understand some terminology before delving into ADSI programming.
Component Object Model (COM) – A technology that allows the creation of binary compatible software components. This simply means that a COM component is a chunk of software that may be accessed anywhere a COM system is available. This is regardless of location, language or even operating system. It helps to think of this in a similar fashion to the Java system. Java may run wherever a Java Virtual Machine (JVM) is available. COM is accessible wherever a COM subsystem is available. But do not confuse the purposes of COM and Java. They are two very different technologies with different purposes.
Interface – A set of functions that are packaged and grouped together according to purpose. COM components provide a number of Interfaces that are available for use in applications. For example later in this article I will demonstrate using the IADsContainer interface (the leading I designates this as an interface) to access an LDAP directory.
Provider – A provider is the software that services requests to some external resource. The resource may be the Active Directory, an LDAP director or an application such as Internet Information Services.
Binding – Binding is the process of connecting a provider to a physical instance of a resource. Simply put, if I want to manipulate objects in my AD domain called Johnson, I would first bind to the Johnson domain. Then I could create, modify, update, search for or delete objects in the Johnson AD at will.
[forums/incl/ad_inline.htm]

Using ADSI

In order to manipulate anything in a namespace with ADSI, you must first bind to it. After binding, you may perform any operations on objects in the namespace you wish.
A typical ADSI application follows this format. Note that this is not real code and is for illustration purposes only:

  1. declare variables: var1, var2, etc
  2. bind to a resource: var1 = bind to AD(Johnson)
  3. manipulate the resource
    1. var1->createuser
    2. var1->delete another user
    3. var1->enumerate all users in the Johnson domain
  4. End

Notice how the process of binding returns an object that is stored in a variable. The object returned is actually an interface variable. This interface variable is used as the tool to do the actual manipulation of the bound resource. That’s about all there is to it. If you can understand this simple example, you are well on your way to using ADSI.

ADSI Examples

LDAP Example
This is an example of connecting to a Sun One Directory Server and enumerating the users in a branch of the directory:

  1. option explicit
  2. dim Container ‘ as IADsContainer
  3. dim Entry ‘as IADs
  4. dim Where ‘as String
  5. dim Filter ‘as Variant
  6. Where = “/ou=People”
  7. set Container = GetObject (“LDAP://192.168.1.105:59822/DC=NET/DC=COMCAST/DC=IL/DC=HSD1” & Where)
  8. for each entry in Container
  9. wscript.echo entry.name
  10. wscript.echo entry.get(“mail”)
  11. next

Line 1 turns on explicit variable declaration. This makes it easier to catch typos in variable names.
Lines 2-5 declare variables for use within the script. This script is written using VBScript (vbs) and therefore does not use typed variables. Every variable is a variant (can hold a value of any type). However, I show the variable type as a comment for documentation purposes. It is also useful when moving a script between vbs and its big brother Visual Basic.
Line 6 sets the Where variable to the location in the directory where enumeration will begin.
Line 7 is where the interesting stuff begins. Here is where the provider is bound to my test directory. Notice that the ldap port use is 59822 instead of 389. Also the contents of the Where variable is concatenated to the connection string to form the full LDAP URL. The GetObject function returns object of type IADsContainer, which is a collection of directory entries.
Lines 8 and 11 form a loop around two console output statements that print the desired directory information.
Line 9 is an example of a property of the interface variable. Each directory entry interface object has a name property that corresponds to the name of the entry in the ldap directory.

Line 10 is an example of a property on the directory entry itself, not the interface variable. These types of properties must be accessed via the get method on the interface variable. This example gets the mail property which is typically an rfc822 email address.
Executing this script against my test LDAP server produces the following output:

​C:\scripts>SunOneDirServerUserDump
uid=KJohnson
[email protected]
uid=RJohnson
[email protected]
uid=tadmin
C:\scripts\SunOneDirServerUserDump.vbs(12, 2) Active Directory: The directory property cannot be found in the cache.

Notice the error message on the last lines. This is because the mail field is undefined for the tadmin user.
Active Directory Example 1
This example shows how to enumerate objects in the Active Directory. This script takes up to two arguments. The first argument is a starting location in the directory to use for enumeration. The second is a filter to limit the returned results to a particular object type. To run this script, simply type the name of the script into a command prompt. adexa1.vbs

  1. ‘adexa1.vbs
  2. option explicit ‘Always use explicit variable declaration!!!
  3. dim RootDSE ‘as IAD
  4. dim Container ‘as IADsContainer
  5. dim Entry ‘as IADs
  6. dim Location ‘as String
  7. dim Filter ‘as string
  8. if(WScript.Arguments.Count > 0) then
  9. Location = WScript.Arguments(0)
  10. end if
  11. if(WScript.Arguments.Count > 1) then
  12. Filter = WScript.Arguments(1)
  13. end if
  14. set RootDSE = GetObject(“LDAP://RootDSE”)
  15. set Container = GetObject(“LDAP://”& Location & “,” & RootDSE.get(“DefaultNamingContext”))
  16. if(Filter “”) then
  17. Container.Filter = Array(Filter)
  18. end if
  19. For Each Entry in Container
  20. wscript.echo Entry.Name
  21. Next
  22. Wscript.Quit

Line 1 is a comment. It contains the name of the script.
Line 2 turns on explicit variable declaration.
Lines 3-7 declare variables for use within the script.
Lines 8 – 10 check to see if at least one command line argument was specified. If so, its value will be used as the starting directory location for enumeration.
Lines 11-13 check to see if at least two command line arguments were specified. If so, the second argument will be used as the value of the container filter. This filter will determine which object classes in the container will be available for manipulation.
Line 14 gets the RootDSE of the directory. This is a standard LDAP v3 object that contains information about the directory.
Line 15 is where binding occurs. However, this time the LDAP connection string is constructed by concatenating the container with the Default Naming Context (the name of the domain) obtained via the RootDSE object.
Lines 16-18 set the filter if one was supplied on the command line.
Lines 19-21 print each entry in the container.
Line 22 explicitly ends the script.
Running this script on one of my test Windows 2003 Server machines produces the following output:

​C:\scripts>adexa1.vbs cn=users user
CN=Administrator
CN=Guest
CN=IUSR_SERVER1
CN=IWAM_SERVER1
CN=krbtgt
CN=SUPPORT_388945a0

Windows NT Example
Yes that’s right, Windows NT. I know you still remember it. This is just to show you that it did work in Windows NT. Not that any NT 4 boxes still exist to actually test this out…

  1. Dim Container ‘as IOleDsContainer
  2. Dim NewUser ‘as IOleDsUser
  3. Set Container = GetObject(“@WinNT!JOHNSON”)
  4. Set NewUser = Container.Create(“User”, “JSomebody”)
  5. NewUser.AccountRestrictions.SetPassword(“password”)
  6. NewUser.SetInfo

Closing Remarks

There is one more thing worth mentioning for budding ADSI scripters, the EzAD Scriptomatic. This is a little tool available from Microsoft that allows you to select operations from a couple of drop down text lists and then generate a corresponding script. This is pretty cool if you are just learning ADSI scripting and want to see how to do a few common ADSI scripting tasks. This tool is available from the Microsoft Technet script center.
 
ADSI scripting is very powerful, and this article shows a small sampling of what can be accomplished with ADSI scripting. Use the links below to find out more about ADSI technology.

For More Information

Use the following resources to learn more about ADSI scripting.
Microsoft Technet Script Center
Windows 2000 Widows Script Host by Tim Hill Windows NT\2000
ADSI Scripting for System Administrators by Thomas Eck
Got a question? Post it on our Active Directory Forums!