February 1999

Directory-Enable Apps with ADSI

The Active Directory Service Interfaces Set Allows You to Use a Key Feature of NT5 Today

by Dan Fox

Reprinted with permission from Visual Basic Programmer's Journal, Feb 1999, Volume 9, Issue 2, Copyright 1999, Fawcette Technical Publications, Palo Alto, CA, USA. To subscribe, call 1-800-848-5523, 650-833-7100, visit www.vbpj.com, or visit The Development Exchange at www.devx.com.

When Windows 2000—formerly NT 5.0—arrives, developers will have better access to the directory services information used and stored by NT through the much-talked-about Active Directory. This single source of information allows developers to more easily directory-enable their applications using directory data such as security, computer, printer, file, and user information.

What you need:
Windows NT 4.0 and VB4/32 or higher

ADO 2.0 (for the Orders project)
You might ask, "How does that help me in my current project?" Here's how: Microsoft has created the Active Directory Service Interfaces (ADSI), a set of COM interfaces you can use today in Windows NT 4.0 and NetWare to make your applications directory-enabled. I'll show you the basics of ADSI and how you can use it in a three-tier application within a COM component written in VB.

Developers often face the problem of how to retrieve and authenticate users in a networked environment. As companies become more distributed, the number of directories that store user information is multiplying. I know of one company that stores user information in the Novell Directory Services (NDS) for file and print services, Windows NT Directory Services (NTDS) for application services, Microsoft Exchange for LAN-based e-mail, and a directory for Internet access. This plethora of directories leads to administrative confusion and makes developers wary of integrating their applications with only one directory, as their companies change from one network OS to another.

Microsoft has addressed this problem by developing ADSI, a core set of COM interfaces designed to abstract the capabilities of any particular directory service and provide a consistent interface for developers. This concept parallels the effort by Microsoft to abstract the capabilities of data providers through the OLE DB interfaces. And, just as ActiveX Data Objects (ADO) allows VB developers to use OLE DB interfaces, the ADSI objects provide an automation interface allowing VB, VBA, and VBScript developers to use the ADSI interfaces.

At the highest level, ADSI defines namespaces through which you obtain access to a hierarchical directory service. Each namespace corresponds to an ADSI provider that acts as the interface into a directory service in the same way an OLE DB provider interfaces with a database. The current downloadable version—ADSI 2.0—includes ADSI providers for Microsoft NTDS, Microsoft Internet Information Server (IIS), LDAP, Novell NDS, and the Novell 3.x bindery (see Figure 1 and the Resources sidebar). After setting a reference to the ActiveDS type library in your project, you can generate a list of the namespaces accessed through the providers by querying the IADsNamespaces object using the GetObject function, passing it the string "ADS:":

Dim objNamespaces As IADsContainer
Dim objNamespace As IADs

Set objNamespaces = GetObject("ADS:")

For Each objNamespace In objNamespaces
   Debug.Print objNamespace.ADsPath
   Debug.Print objNamespace.Class
Next

  Windows 2000's Active Directory Supports Administration  
(Editor's Note: Although ADSI can make life easier for you today, the full potential won't be realized until the release of Windows 2000 and the Active Directory. To give you a look at some of the Active Directory's key features and architecture, we asked Jan Brandt to give you an overview. Jan is an MCSE, CNE, and NT instructor who has been evaluating the beta releases.)

Read sidebar...

You can reference two types of generic objects in ADSI: ADSI containers and ADSI objects. You dimension a container object as IADsContainer; it can contain other ADSI objects. In a directory that contains hierarchical information, any of these can be containers: domains, computers, and groups. An ADSI object is simply the generic object dimensioned as the generic ADSI object, IADs. It can reference either a container or objects within a container, which are referred to as "leaf objects."

Once you obtain a reference to the namespace object within the namespaces container, you can use the Name, ADsPath, and Class properties to print out the basic information about the namespace. The Name and Class properties identify the name and type of object that was returned, such as "WinNT:" and "Namespace", respectively. However, the ADsPath property is more interesting because it specifies the fully qualified name of the object within the directory hierarchy. In fact, using ADsPath, you can get a reference to any object in the directory using GetObject. To get a reference to the Windows NT domain MyDomain, call GetObject with the ADsPath of the domain:

Dim objDomain as IADs

Set objDomain = GetObject( _
   "WinNT://MyDomain")

Retrieving an object using the generic IADs interface does not preclude the object from also being a container. In the last example, an NT domain is also a container for computers that have joined the domain. To enumerate those computers, point the instance of objDomain to an instance of an IADsContainer and enumerate the computers:

Dim objComputers as _
   IADsContainer
Dim objComputer as IADs

Set objComputers = objDomain
For Each objComputer in _
      objComputers
      Debug.Print _
         objComputer.ADsPath
Next

 
Figure 1 Map the Namespace. Click here.

Manipulate the Objects

You're probably wondering how to manipulate these objects, not simply print out the directory structure. ADSI not only provides generic objects to query the directory structure, but it also implements a standard set of directory objects that include the most common objects found in a directory tree. Providers can then implement and augment these standard objects (see Table 1). You can use these directory objects to access objects and perform actions within the directory (see Figure 2).

Using the more specific objects, you can now view particular properties of the object instead of only its ADsPath and Name:

 
Figure 2 The NT Provider Implements Objects. Click here.

Dim objDomain As IADsDomain
Dim objComputer As IADsComputer

Set objDomain = GetObject( _
   "WinNT://MyDomain")
For Each objComputer In objDomain
   Debug.Print objComputer.Name
   Debug.Print _
      objComputer.OperatingSystem
   Debug.Print _
      objComputer.OperatingSystemVersion
   Debug.Print objComputer.Processor
Next

You should be aware that not all ADSI providers implement all the available properties and that the properties themselves might be mandatory or optional. For example, the Windows NT 4.0 provider does not implement the MemorySize and StorageCapacity properties of the IADsComputer object, among others. You can find the complete list of properties and methods supported by the NT 4.0 provider in the online help (see the Resources sidebar). You can also query the provider for what it supports using the Schema object and the standard object IADsClass:

   Dim objIADs As IADs
   Dim objClass As IADsClass
   Dim objProperty As Variant
 
   Set objIADs = GetObject( _
      "WinNT://workgroup/ssosa/" & _
      Administrators")
   Set objClass = _
      GetObject(objIADs.Schema)
    
   Debug.Print "Class: " & _
      objClass.Name
   
   Debug.Print _
   "Properties in this Class: "
   For Each objProperty In _
      objClass.MandatoryProperties
      Debug.Print "   "; objProperty
Next 
   For Each objProperty In _
      objClass.OptionalProperties
      Debug.Print "   "; objProperty
   Next 

The other benefit of exposing the directory objects: They might also contain methods that allow you to modify the directory. For example, you might create a new user and add the user to an NT group (see Listing 1).

In addition to these built-in objects, ADSI providers can extend the set of objects, methods, and properties or even allow administrators to augment the directory. This is an important feature of NT5 because it enables administrators to store all types of information in the Active Directory, all of it accessible through ADSI.

 
Figure 3 Directory-Enable Your Solution. Click here.

Applying ADSI

To provide an example of how you might use ADSI, I built a three-tier application that returns orders from a SQL Server database based on the region of the user, as determined by the user's NT group membership. First, the user interface tier of the application determines the user's NT credentials. Next, the business services tier—implemented as an ActiveX component—uses ADSI to determine which group and region the user is a member of. The component then passes the group information to SQL Server—the data services tier—which runs a stored procedure to return rows from the user's region (see Figure 3). This basic problem might have other solutions, but this ADSI implementation has the benefit of being directory-independent. It also performs row-level security while not requiring the additional SQL Server administration to implement views or use SQL Server's security.

The core of this solution is the class module that implements the component (see Listing 2). For this class, I created two public properties—UserAccount and UserDomain—that the GetUserGroup private function uses to query ADSI and determine which group the user is a member of. The GetUserGroup function uses a local array to store the possible regions and their IDs. It tests against four regions that exist as NT groups and that you must create using the User Manager utility on the NT Server. After obtaining a reference to the group through the IADsGroup interface, the function uses the IsMember method to determine which group the user resides in:

If objGroup.IsMember("WinNT://" & _
   mstrDomain & "/" & mstrUserAccount) _       Then
      'return the ID of the group
   GetUserGroup = i
   Exit Function
End If

If the method finds that the user is a member of one of the groups, the function exits without checking the others, because the user cannot be a member of more than one region. The ListOrders method calls GetUserGroup and uses ADO 2.0 to call the stored procedure GetOrders, passing the region ID to the procedure (see Listing 3).

 
Figure 4 View Customized Results. Click here.

You implement the user interface for the solution through a single form that retrieves the user account of the logged-on user through the Win32 API GetUserName (see Listing 4). You can also retrieve this information from properties of the Windows Scripting Host's (WSH) IWshNetwork_Class interface. The WSH allows easy access to script-based clients and does not require calling the Windows API directly. The form then instantiates the component and passes in the account and the domain where you search for membership. As the NT provider is implemented, the IADsDomain object can refer to either an actual NT Domain or a standalone NT Server in a workgroup. Finally, the form calls the ListOrders method and passes the returned recordset to an ADO Data Control that displays it in the OLE DB Grid Control (see Figure 4).

    Now It's All Windows 2000
Windows NT 5.0 became Windows 2000 in an announcement by Microsoft on Oct. 27, 1998. The company said it changed the name to make it clear that its 32-bit enterprise-oriented operating system is now "a mainstream product for all businesses on both the client (user desktop) and server." In addition, Microsoft intends to use the NT kernel as the basis of all the company's PC operating systems, from consumer PCs to high-performance servers.

Read sidebar...

I've covered only some of ADSI's aspects, but this gives you an idea of where to start and how you can integrate the ADSI component into your solutions. With the upcoming release of Windows 2000, these techniques will help integrate your solutions even more tightly with the Active Directory.








Dan Fox lives in Overland Park, Kan., and is a consultant and trainer for Solutech Inc. He is a Microsoft Certified Systems Engineer, Solution Developer, and Trainer who develops client/server and intranet solutions and teaches Visual Basic, SQL Server, and Visual InterDev. Reach Dan by e-mail at dfox@solutechinc.com.