VBScript Fundamentals for Windows Scripting – ADSI

The previous article introduced the fundamental concepts of VBScript. If you are new to scripting and have not yet read this article, you should take a few moments to go over the information contained therein as it explains important fundamentals. This article will take that initial foundation and build on it to illustrate how VBScript can be applied in Windows Scripting in an applicable manner. Although a thorough knowledge of COM (Component Object Model) is not required, it is beneficial to be familiar with the ADSI object model, components, and provider architecture to create more efficient scripts. The next several sections will introduce the ADSI architecture to build a foundation that will aid in ADSI scripting.

Interfaces

Active Directory Service Interfaces, or ADSI, is the collection of DLL interfaces that allows the manipulation of different directory objects. This collection is used not only in scripting, but also with the snap-in MMC’s such as the AD Users & Computers snap-in. The interface is what provides information about an object such as its path, name, path to the parent object, etc. The most common interface is called IADs which is used by ADSI to interact with objects.

Figure

In the above example we used the LDAP provider (explained a little later) to instantiate (or create) the object we called oUser. The LDAP provider is able to determine that oUser is a User object in Active Directory so it creates the appropriate COM object, which we named oUser, along with the appropriate interfaces for that object which are listed to the left of the object. The IADs interface is what allows us to use the Get method to read the information about our User object, JoeUser. IADsUser is simply another common interface that might be used with this object and IUnknown is a base for other interfaces to be attached to the object which in turn are used to provide additional information about the object. We’ll look at an example shortly after discussing providers in more detail.

Namespaces

Everything in the directory has a specific place. The directory service maintains this information and makes it available to user queries, application calls, etc. But, how does Active Directory know where to go when you double click on JoeUser in the Users and Computers snap-in? It looks up where the JoeUser object is located within the AD namespace. Think of a namespace as a location in the directory to which an object name can be resolved. DNS is the best example to use for namespace. It is a hierarchical method of maintaining domains in a tree-like structure and is scalable and efficient. Active Directory uses not only DNS namespacing, but also LDAP (Lightweight Directory Access Protocol) to keep object organized into OU’s and containers. In the example above, we used the LDAP moniker (LDAP://) to point to where the JoeUser object resides in the AD namespace.

Providers

ADSI uses providers to connect to and interact with the directory in scripts and GUI’s. More specifically, the providers allow ADSI to interact with the directory namespaces. To bind to an object (if you remember from the VBScript Fundamentals – Part 1 Article, binding is how we connect and interact with an object using scripting as well as declaring a variable name to be used with that object), ADSI uses a specific syntax that is part of the ADsPath. The ADsPath is simply the name provider combined with the path to the object as illustrated in the following examples:

LDAP://dc=lab,dc=mycompany,dc=com
LDAP://server01/dc=lab,dc=mycompany,dc=com

The LDAP provider facilitates access to LDAP based directories. The first example demonstrates the syntax for serverless binding. This method simply binds to the path in the directory and does specify a specific server. This is the ideal method to use when binding as it does not require an additional operation to first bind to a specific domain controller before binding to the object.

WinNT://MYCOMPANY
WinNT://MYCOMPANY/dc/JoeUser

The WinNT provider facilitates access to a domain controller path in the MYCOMPANY domain. The first example simply binds a domain controller within whatever domain the script is run in, while the second method access the JoeUser account on the dc domain controller in the MYCOMPANY domain. This method is preferred for use in NT domains. It can be used with AD; however, the script’s performance is much better when the LDAP provider is used.

GC://dc=lab,dc=mycompany,dc=com
GC://server01/dc=lab,dc=mycompany,dc=com

The GC provider facilitates access to Global Catalog server in the directory path. The first example is one that uses the serverless binding mentioned earlier while the second binds to a specific server in the specified path.

NDS://NetTree/o=org01/dc=com,dc=mycompany,dc=lab,cn=JoeUser

The NDS provider facilitates directory path access within Novell NetWare directory services. Here we are referencing JoeUser who’s user object resides in the org01 organizational unit of the intraNetWare tree named NetTree. Since intraNetWare is also LDAP compliant, you can use the LDAP provider paths as well to facilitate access.

NWCOMPAT://SERVER01/JoeUser

NWCOMPAT provider facilitates access to the Novell NetWare bindery. In this example we are binding to the JoeUser object that exists on SERVER01 in the bindery.

IIS://server01.mycompany.com
IIS://localhost/w3svc/1

The IIS provider facilitates access to IIS service paths. The first example binds to the server server01 in the mycompany.com domain, while the second example references the w3svc on the local server.  The IIS:// provider references information in the IIS metabase.

NOTE – The ADSI provider names listed above are case-sensitive. All must be specified in uppercase with the exception of the WinNT provider.

Now let’s take a look at how binding to an object in the directory using a provider would appear in an actual script.

Set objOU = GetObject("LDAP://ou=Finance,dc=mycompany,dc=com")

objOU.Filter = Array("user")

For Each objUser In objOU

Wscript.Echo objUser.Name

Next

In the first line, we are binding to the Finance organizational unit in the mycompany.com domain using the Get method and assigning the operation to the variable name objOU. Remember, it is the LDAP provider that creates the COM object (called objOU) and the appropriate IADs interfaces (allowing us to use the Get method to read directory information) to facilitate interaction with the newly created object based upon the object type.

The next line uses the Filter method of our newly created (instantiated) object to put the user objects within the Finance OU into a dynamic array. We then use the For…Next control loop to display each user’s name within the array.

Consider the following graphic to better visualize the different ADSI layers and the logical architecture flow:

Figure

We have already discussed the Directory Namespace layer which resides at the server level (on the domain controllers in the AD partitions) as well as the provider layer which allows access to the namespace layer via the COM interfaces created by the ADSI DLL’s also mentioned earlier. The top three layers are easier to explain as they are the layers that process all the information created by the script.

Router

The router does most of the work when it receives the scripted request. Take for example, the GetObject method in the above example. The router is the layer that receives the request, recognizes the provider used in the request (LDAP://), looks up the provider information in the registry, and then loads the provider into memory. Next, the router layer creates the object and sends the object information back to the provider layer for use in the script. Once the router has created the object (the binding process), items processed with the object in the script are performed at the Provider layer. There may also be information passed directly between the Application and the Provider layer when applications such as the MMC are used for information searches or user/group creation.

Property Cache

The cache is simply a place in memory on the machine from which the script was run that is used to hold the object after the binding operation in the script. The object is not downloaded to the machine, but rather a virtual copy of the object is maintained locally for use in the script. When we use the Get method (i.e. GetObject), it is here that the script looks first for the information. If it cannot locate it in the local memory, or property cache, then a GetInfo call is initiated to AD to read the property information into cache.

Applications

This is the top layer of the architecture where all interaction with AD starts. A good example of interaction within this layer is the Users and Computers MMC snap-in. The MMC initiates at this layer and then interacts with the rest of the ADSI layers depending on what you do within the MMC.

Interfaces

Every object in Active Directory is defined explicitly by attributes. To administer these objects and attributes, we use a set of methods contained within the ADSI interfaces. There are 6 core properties common to all objects in ADSI called IADs. IADs is the most basic interface in ADSI and the 6 core properties are as follows:

IADs::GUID – Represents the Globally Unique Identifier of an object returned as a string.
IADs::Class – Represents the schema class of the object returned as a string.
IADs::ADsPath – Represents the full path of the object within the current namespace directory. The path is returned as a string and identifies the objects location in the directory.
IADs::Name – Represents the object’s relative name. Name is returned as a string.
IADs::Parent – Represents the path to the parent object of the current object in the directory returned as a string.
IADs::Schema – Represents the path to the object and its schema class in the directory returned as a string.

As each of the above IADs methods are fairly vague by themselves, so let’s illustrate this in a small script:

Set objDomain = GetObject("LDAP://dc=internal,dc=testdom,dc=com")
WScript.Echo "ADsPath:" & objDomain.ADsPath
WScript.Echo "Class:" & objDomain.Class
WScript.Echo "GUID:" & objDomain.GUID
WScript.Echo "Name:" & objDomain.Name
WScript.Echo "Parent:" & objDomain.Parent
WScript.Echo "Schema:" & objDomain.Schema

The first line binds to the internal.testdom.com domain using the LDAP provider and the Get method to read information from the object as discussed at the beginning of this article. Each line thereafter simply displays the results in a pop-up window on the screen. Cut and paste the script into notepad, edit the domain information accordingly, and run the script. You will get the following results tailored for your domain:

ADsPath: