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:LDAP://dc=internal,dc=testdom,dc=com
Class:domainDNS
GUID:718b9aa6e578ab49b954f4afa230b9d2
Name:dc=internal
Parent:LDAP://dc=testdom,dc=com
Schema:LDAP://schema/domainDNS
You may be wondering, “With all this discussion surrounding IADs, I didn’t see it listed anywhere in the script”. This is because IADs is not an actual property or method, it’s an interface. So referring back to the script above, the objDomain variable becomes our IADs container after the binding process. For further visual illustration, you could view the above script as:
Set objDomain = GetObject("LDAP://dc=internal,dc=testdom,dc=com")
WScript.Echo "ADsPath:" & IADsContainer.ADsPath
WScript.Echo "Class:" & IADsContainer.Class
WScript.Echo "GUID:" & IADsContainer.GUID
WScript.Echo "Name:" & IADsContainer.Name
WScript.Echo "Parent:" & IADsContainer.Parent
WScript.Echo "Schema:" & IADsContainer.Schema
NOTE – This is NOT proper syntax and will not work. It is only for illustration of the topic.
Viewing the above, you can see that once the binding process has completed, the objDomain object essentially becomes our IADs container. I have substituted this in the second example for better visual representation of the concept.
IADsContainer Methods
We just learned the concept of IADsContainers and how in ADSI, once we bind to an object, it then becomes the IADsContainer. Let’s go even one step further to look at the methods used with IADsContainers to obtain results and functionality within the script.
IADsContainer::Filter (reading values)
This one is most commonly used methods when returning objects from an enumeration into an array. For example:
Set objOU = GetObject("LDAP://OU=MIS,dc=internal,dc=testdom,dc=net")
ObjOU.Filter = Array("user")
For each strObject in objOU
Wscript.Echo strObject.cn
Next
The first line is the binding process with the Get method. The second line is where we utilize the Filter method on the IADsContainer (objOU) to read the information into a dynamic array. The next three lines use a For…Next loop to display items in the array returned by the Filter method.
IADsContainer::Create
The Create method, as the name so intuitively implies, is used to create objects in the directory. Consider the following example to create a user object in the MIS Organization Unit:
Set objOU = GetObject("LDAP://ou=MIS,dc=internal,dc=testdom,dc=net")
Set objUser = objOU.Create("user", "cn=UserA")
objUser.Put "sAMAccountName", "UserA"
objOU.SetInfo
As you are getting used to by now, the first line is the binding process. The second line uses the Create method on the IADsContainer (objOU) to create a user object and assign it the name “UserA”. Since the sAMAccountName is a mandatory attribute for a user object (every user must have a logon account name), we set that attribute value in the third line. The fourth line commits the account to the Active Directory.
NOTE – Be careful when using SetInfo as this writes the results of the script directly into the Active Directory.
IADsContainer::Delete
The Delete method is just the opposite of the Create method in that it deletes objects from the directory. The next example will delete the user object we just created:
Set objOU = GetObject("LDAP://ou=MIS,dc=internal,dc=testdom,dc=net")
Set objUser = objOU.Delete "user", "cn=UserA"
IADsContainer::Filter (writing values)
Another method used with Filter is writing values to objects in the directory as demonstrated here:
Set objUser = GetObject("LDAP://cn=UserA,ou=MIS,dc=internal,dc=testdom,dc=net")
objUser.Put “description”, “MIS Dude”
objUser.SetInfo
In this example we initiate the binding process, add a description to the user object UserA using Put, and then use SetInfo to commit to Active Directory.
IADsContainer::MoveHere and IADsContainer::GetObject
These are two more methods which may be utilized in IADsContainer. The GetObject method should be quite familiar now as this is the method we have used continuously to bind to directory objects in a single step. The MoveHere method can be extremely involved and usually entails using ADO (ActiveX Data Objects) to connect to the directory and retrieve records for better performance. This method is beyond the scope of this article; however, I wanted to list it as it is a method of the IADsContainer.
Conclusion
ADSI is a very powerful tool in scripting and this article only touches the surface of the possibilities of the versatility of the interfaces. You can find additional information regarding ADSI online at http://www.microsoft.com/technet/treeview/default.asp?url=/technet/scriptcenter/scrguide/sas_ads_overview.asp. The more you work with ADSI and become familiar with it, the more functional and robust scripts you will be able to write. Remember, robust doesn’t mean long and drawn out. Only a few paragraphs earlier, we deleted a user object with only two lines of code.
My next article will cover WMI (Windows Management Instrumentation) use in VBScript.