ADSI Open Interfaces for Managing and Using Directory Services

This paper outlines the Microsoft plan for integrating multiple directory services through a well-defined, open set of interfaces: Microsoft Active Directory Service Interfaces (ADSIs). The availability of a standard, open directory service administration and programming model for Windows–based platforms will encourage the inclusion of directory services in a wide range of commercial and customer-developed applications.

On This Page

Introduction
Directory Services Today
Active Directory Service Interfaces
ADSI Architecture
ADSI Standard Objects
ADSI Programming Model
Using ADSI
Using ADSI for Schema Management
Conclusion

Introduction

One of the challenges of working within a large distributed computing environment is identifying and locating resources, such as users, groups, print queues, and documents. A directory service is part of a distributed computing environment that provides a way to locate and identify the users and resources available in the system. A directory service is like a phone directory. Given a name for a person or a resource, it provides the information necessary to access that person or resource. You do not have to use specific binding information to access a resource on the network.

Most enterprises already have many different directories in place. For example, network operating systems, electronic mail systems, and groupware products all have their own directories. Many issues arise when a single enterprise deploys multiple directories. These issues include usability, data consistency, development cost, and support cost, among others.

Active Directory Service Interfaces addresses these issues by providing a single, consistent, open set of interfaces for managing and using multiple directories.

What You Should Already Know

This document assumes that readers have a working knowledge of OLE, the Component Object Model, and directory services. Example code appears in the Microsoft Visual Basic development system.

Directory Services Today

It is common to find a variety of directoriesmany playing an administrative rolethat are deployed within a single organization. These include network resource directories, such as an LDAP-based directory, Active Directory, Banyan StreetTalk, Microsoft Windows operating system Directory Service, and Novell Directory Services, as well as application-specific directories, such as Lotus Notes, cc:Mail, or Microsoft Mail. Although a single directory for an entire organization is desirable, no product available today can fill this very large requirement.

Bb742578.w2kads01(en-us,TechNet.10).gif

Figure 1: The directory challenge

Multiple directories in the organization pose complex challenges to users, administrators, and developers. These problems have limited wide-directory deployment. End users face multiple logons and a variety of interfaces to information across multiple directories. Administrators face the complexity of managing multiple directories. End users and administrators want application developers to use an existing administrative directory, but developers face a dilemmawhich one should they use? Each directory offers unique application interfaces. Developers must choose a specific directory implementation or support multiple versions of their application. As a result, developers seldom use existing directory services.

Microsoft has a strategy for helping to solve these customer problems : the Active Directory Service Interfaces (ADSI). ADSI is a set of COM programming interfaces that make it easy for customers and independent software vendors (ISVs) to build applications that register with, access, and manage multiple directory services with a single set of well-defined interfaces.

One of the most familiar open programming APIs is Open Data Base Connectivity (ODBC). ODBC provides open interfaces for relational databases, thus allowing developers to write applications and tools that work with any database that supports ODBC. Because of the thriving ODBC development community, every major relational database now supports ODBC. ADSI is the equivalent of ODBC for directory services.

ADSI gives developers access to multiple directory service providers through an open set of interfaces. Applications written to ADSI work with any directory service that offers an ADSI provider. ADSI addresses the problems outlined above.

Bb742578.w2kads02(en-us,TechNet.10).gif

Figure 2: The open solution

This document presents the concepts, features, benefits, and architecture of ADSI and provides examples of ADSI usage.

Active Directory Service Interfaces

ADSI abstracts the capabilities of directory services from different network providers to present a single set of directory service interfaces for managing network resources. The standard ADSI objects are those found within multiple name spaces**.** The typical name spaces for ADSI are directory services for various network operating systems. Administrators and developers can use ADSI services to enumerate and manage the resources in a directory service, no matter which network environment contains the resource.

ADSI makes it easier to perform common administrative tasks, such as adding new users, managing printers, and locating resources throughout the distributed computing environment.

ADSI makes it easy for developers to enable their applications to connect with the central directory. Administrators and developers deal with a single set of directory service interfacesregardless of the installed directory services.

ADSI is one component of the Windows Open Services Architecture (WOSA) Open Directory Service Interfaces (ODSI).

ADSI Users

Network Administrators will use ADSI to automate common administrative tasks, such as adding users and groups, managing printers, and setting permissions on network resources.

Independent software vendors (ISVs) and end-user developers will use ADSI to enable their products and applications to connect with the central directory. Services can publish themselves in a directory, clients can use the directory to find the services, and both can use the directory to find and manipulate other objects of interest. Because ADSI is independent of the underlying directory service(s), the directory-enabled products and applications operate successfully in multiple network and directory environments.

Benefits of ADSI

Feature

Benefit

Open

Any directory provider can implement an ADSI provider; users gain freedom of choice in directory services without sacrificing manageability.

DS-independent

Administrative applications are not tightly bound to a vendor's directory service. The same application can work on multiple directories. Development time and support costs are reduced.

Java support

ADSI objects provide easy access to directory services for Java applets and programs through Java COM.

Security

ADSI supports both authentication and authorization programming models.

Simple programming model

Administrative and other directory-enabled applications can be developed with no need to understand vendor-specific directory APIs.

OLE Automation Server

Any OLE Automation Controller (for example, Visual Basic, Perl, Rexx, C/C++, and others) can be used to develop directory service applications. Administrators and developers can use the tools they already know. Productivity is enhanceddevelopment time and support costs are reduced.

Functionally rich

ISVs and sophisticated end users can develop serious applications, using the same ADSI models that are used for simple scripted administrative applications.

Extensible

Directory providers, ISVs, and end users can extend ADSI with new objects and functions to add value or meet unique needs.

ADSI Architecture

The ADSI object model consists of ADSI objects and dependent objects. Clients manipulate objects with interfaces. ADSI providers implement the ADSI objects and their interfaces.

ADSI Objects

Active Directory Service Interfaces objects are COM objects that represent persistent objects in an underlying directory service. An Active Directory Service Interfaces object is manipulated using one or more COM interfaces.

ADSI objects are divided into two groups: directory service leaf objects, and directory service container objects. A container object can contain other ADSI objects. A leaf object cannot contain ADSI objects.

The division of an object type into a host and one or more dependent objects implements a logical grouping of properties and methods. This division does not necessarily reflect the structure of the underlying directory. The host and dependent object relationship should not be confused with the container and contents relationshipthe former is a characteristic of ADSI, the latter a characteristic of the underlying directory.

ADSI Provider

An ADSI provider contains the implementation of ADSI objects and dependent objects for a particular name space. Figure 3 below shows how clients are concerned only with getting and using interfaces on an object, and not with the details of where and how the software of an object is implemented.

Bb742578.w2kads03(en-us,TechNet.10).gif

Figure 3: Provider architecture

Schema Management

ADSI provides predefined objects so that directory service manipulation can be uniform across name spaces. However, an ADSI object in any directory might have more functionality than that specified by ADSI. A directory might also contain objects that are not defined at all by ADSI. In addition, there are extensible directory services that allow their base schema to be modified and their objects to be arbitrarily extended by administrators and independent software vendors.

Object extensions are handled by Schema Management ADSI objects. These objects are used to:

  • Browse the definitions of objects.

  • Extend the definitions of objects.

Schema Management of ADSI Objects

Schema management objects can be used to browse and modify the schema of a name space. These objects are:

  • Schema container object, which contains a given schema.

  • Class container object, which defines an object class.

  • Property object, which describes a property.

  • Syntax object, which describes a syntax that can be used in a property definition.

These objects are different from directory service objects like the User component, in that their properties are not subdivided into functional sets.

Schema Container Object

The schema container object is used to attach a set of object definitions to a part of a directory tree. Typically, each instance of a directory has its own schema. ADSI represents this by placing a schema container as a child of the directory root.

Figure 4: The schema container

Figure 4: The schema container

Figure 4 shows the typical layout. However, ADSI does not limit schema containers to this level of the tree. A complex directory might allow multiple schemas to exist in a directory instance. In that case, schema containers might be found in other parts of a tree. As shown in Figure 5, there can only be one schema container in any given ADSI container.

Figure 5: Schema hierarchy

Figure 5: Schema hierarchy

The schema container itself is a tree that contains class, functional set, property, and syntax definitions. New classes and functional sets can be created in the container to expand the schema.

Functional sets are defined separately from classes so that they can be used in multiple class definitions.

Class Container Object

The class container object is used to define a class of objects that can be created in the directory. New classes can be derived from existing classes using the ADSI model.

Bb742578.w2kads06(en-us,TechNet.10).gif

Figure 6: Creating a class

Figure 6 illustrates how a class container object relates to other class objects, property objects, and syntax objects to create a definition of a class. A class object points to property objects, which point to the syntax that the property supports.

Caching

All ADSI objects provide two methodsGetInfo and SetInfoto provide simple caching for properties. Operations that involve getting and setting properties occur in the cache.

A caller can obtain a property value from an ADSI object at any time after obtaining the object. The caller need not call GetInfo first. If the property has not been previously retrieved, the provider is responsible for retrieving and caching it to satisfy the request. Subsequent requests are satisfied from the cached value.

GetInfo is called to explicitly refresh the object's cached properties from the underlying directory. By calling GetInfo, the caller ensures that the property values are current as of the time of the GetInfo call. If the GetInfo method is executed after changes are made to the local object's cache but before the SetInfo method is executed, the changes are discarded. GetInfo allows the client to provide hints about which properties it uses so that the provider can optimize network access.

SetInfo is called to write an object back to the underlying directory. No changes are made to an object's properties within the directory until the SetInfo method is called.

Names

Objects that reside within a given name space are identified by a unique name. For example, files stored on a computer disk drive reside in the file system name space. The unique name of a file is based on where it is stored in the file system name space, for example:

C:\Public\Documents\Adsi\Adsi_spec_v3.doc  

Directory service name spaces also identify the objects that they contain by unique names, which are usually based on the location in the directory where the object can be found. For example, in an Active Directory, a given object might have a name like this:

CN=JSmith, OU=Sales, DC=ArcadiaBay, DC=Com 

Different directory services use different forms for naming the objects that they contain. This makes dealing with different name spaces challenging, especially for developers, considering all of the different environments on which the code might be running.

A goal of ADSI is to minimize the code's knowledge of an object's path so that programs can be name-space-portable.

ADSI defines a naming convention that can uniquely identify an ADSI object in a heterogeneous environment. These names are called AdsPath strings. AdsPath consists of a provider's moniker (for example LDAP:, WinNT:)and the provider's specific path.

Examples of ADsPath strings are:

WinNT://REDMOND/jsmith, user see previous comment re: "WinNT"

Identifies jsmith as a user in the Redmond Domain

WinNT://REDMOND/comp1, computer

Identifies comp1 in the Redmond Domain

WinNT://REDMOND/comp1/alice

Identifies alice as a local user on comp1 computer.

LDAP://OU=Sales, DC=ArcadiaBay, DC=COM

Identifies an organizational unit in the Arcadia Domain

LDAP://exch01/O=Microsoft

Identifies an exchange object on an exchange server

ADSI provides easy navigation models through the IADsContainer interface. In addition, a caller can use this interface to filter object types.

Example: Showing all objects in a given organizational unit

Set ou = GetObject("LDAP://host1/OU=Sales, DC=ArcadiayBay,DC=COM") 
For each obj in ou 
Debug.Print  obj.Name 
Next 

Example List all users in ArcadiaBay domain:

Set dom = GetObject("WinNT://ArcadiaBay") 
dom.Filter = Array("user") 
For each usr in dom 
Debug.Print usr.Name 
Next 

Searching

Searching is one of most frequently used tasks in most directories. In ADSI, you can use either the OLE Distributed Query (DB) interfaces, or you can search directly using Active Directory Service Interfaces. For better control, these interfaces allow a caller to specify search page size, sort, size limit, search level, search scope and other options.

Since ADSI is also an OLE DB provider, with Distributed Query technology shipped with the Microsoft SQL Server, you can join one result set from ADSI and another result set from another OLE DB provider. For example, you can join sales data in SQL Server with user and contact information found in the Active Directory.

Example: Using ADO to query the Active Directory

Dim con As New Connection, rs As New Recordset  
Dim Com As New Command 
'Open a Connection object 
con.Provider = "ADsDSOObject" 
con.Open "Active Directory Provider" 
'Create a command object on this connection 
Set Com.ActiveConnection = con 
Com.CommandText = "select name from 'LDAP://DC=ArcadiayBay,DC=COM' where objectClass='*' ORDER BY NAME" 
'----------------------------------------- 
'Set the preferences for Search 
'-------------------------------------- 
Com.Properties("Page Size") = 1000 
Com.Properties("Timeout") = 30 'seconds 
Com.Properties("searchscope") = ADS_SCOPE_SUBTREE  
'-------------------------------------------- 
'Execute the query 
'-------------------------------------------- 
Set rs = Com.Execute 
'-------------------------------------- 
' Navigate the record set 
'---------------------------------------- 
While Not rs.EOF 
Debug.Print rs.Fields("Name").Value 
rs.MoveNext 
Wend 

Security

ADSI provides both authentication and authorization client access. If it is used to communicate with the Active Directory, a user can be authenticated using Kerberos or NTLM, depending on the environment. ADSI also allows you to manipulate the Security Descriptor.

Example: Log on as JSmith

Dim dso As IADsOpenDSObject 
Dim domain As IADsDomain 
Set dso = GetObject("WinNT:") 
Set domain = dso.OpenDSObject("WinNT://ArcadiaBay", "JSmith", "secret", ADS_SECURE_AUTHENTICATION) 

Example: Add ACE permission to an object

Dim Ace1 as new IADsAccessControlEntry 
Dim Ace2 As new IADsAccessControlEntry 
Dim Dacl as new IADsAccessControlList 
' Add the ACEs to the Disretionary ACL 
Dacl.AclRevision = 4 'DS ACL Revision 
' Set up the first ACE 
Ace1.AccessMask = -1 'Full Permission (Allowed) 
Ace1.AceType = ADS_ACETYPE_ACCESS_ALLOWED 
Ace1.AceFlags = ADS_ACEFLAG_INHERIT_ACE 
Ace1.Trustee = "ACTIVED\Administrator" 
' Add the ACEs to the Disretionary ACL 
Dacl.AddAce Ace1 
Dacl.AddAce Ace2 

Extension

ISVs or corporate developers can extend the object semantics by adding interfaces to the existing ADSI interfaces. ADSI combines the COM Aggregation model and directory technology to bring a powerful extension model. A backup and restore vendor, for example, can extend the computer object with vendor-specific methods or properties. By having these methods available, an administrator can write a script to back up all computers to an organizational location that he or she designates.

Example: Back up all computers in the Sales organizational unit

Set ou = GetObject("LDAP://OU=Sales, DC=ArcadiaBay, DC=COM") 
Ou.Filter = Array("computer") 
For each comp in ou 
comp.BackUpNow() ' this is extension method 
Next 

ADSI Standard Objects

ADSI define two kinds of objects. Clients use SchemaManagementObjects to browse and extend the schema. DirectoryObjects represent the objects in the underlying namespaces managed by the ADSI providers. Schema Management Objects are discussed above, in Schema Management.

ADSI defines a set of standard container and leaf objects that represent the most common objects found in network directories. Using the schema extension model, provider writers and application developers can add objects as needed.

Standard Container Objects

  • Name spaces

  • Country

  • Locality

  • Organization

  • Organizational unit

  • Domain

  • Computer

Standard Leaf Objects

  • User

  • Group

  • Alias

  • Service

  • Print queue

  • Print device

  • Print job

  • File service

  • File share

  • Session

  • Resource

ADSI Programming Model

ADSI objects are Component Object Model (COM) objects. Basically, programmers interact with COM objects by calling one of the standard OLE procedures to obtain a pointer to an object's IUnknown interface. They then call QueryInterface to obtain a pointer to the specific interface of interest.

Active Directory Service Interfaces can all be called using this standard COM model by using traditional compiled languages, such as C and C++. ADSI provides a helper function, ADsGetObject, to simplify the process of obtaining the desired interface pointer from a specified ADSI object.

Example: Create a User (C/C++)

IADsContainer *pContainer; 
IADs  *pNewObject; 
IADs  *pNewObject; 
IADsUser  *pUser; 
// 
// Bind to the known container. 
// 
ADsGetObject(TEXT"WinNT://MSFT", 
IID_IADsContainer, 
(void**)&pContainer); 
// 
// Create the new Active Directory Service Interfaces User object. 
// 
pContainer->Create(TEXT"User", 
TEXT"Jane", 
&pNewObject); 
// 
// Get the IADsUser interface from the user object. 
// 
pNewObject->QueryInterface(IID_IADsUser, &pUser); 
// 
// Set Jane's password. 
// 
pUser->SetPassword(TEXT"Argus"); 
// 
// Complete the operation to create the object. 
// 
pUser->SetInfo(); 
// 
// Cleanup. 
// 
pContainer->Release(); 
pNewObject->Release(); 
pUser->Release(); 

OLE Automation Interface (IDispatch)

ADSI objects are also OLE Automation Servers. All Active Directory Service Interfaces can be invoked through the IDispatch interface.

IDispatch is the OLE Automation interface for controllers that do not use COM interfaces directly. Accessing an object through IDispatch is called name-bound or late-bound access, since the connection between the client and the OLE object (server) occurs at run time, and not when the client program is linked.

OLE Automation controllers, like Visual Basic, hide the inner workings of OLE and call all of the necessary methods in IDispatch on behalf of the programmer. Programmers can concern themselves with the logic of their application, and not the low-level details of OLE.

Example: Creating a User Object (Visual Basic)

Dim Container as IADsContainer 
Dim NewUser as IADsUser 
' Bind to the known container. 
Set Container = GetObject("WinNT://MSFT") 
' Create the new Active Directory Service Interfaces User. 
Set NewUser = Container.Create("User", "Jane") 
' Set Jane's password. 
NewUser.AccountRestrictions.SetPassword("Argus") 
' Complete the operation to create the object in the directory. 
NewUser.SetInfo 

Using ADSI

The following sections describe how to use ADSI for administration.

Creating a List of Users

Building lists of users and their properties is a common need. In this example, a Visual Basic script extracts all of the users in the "WinNT" name space in ABX Compute Corporation's Manufacturing division. Here each user's name and known telephone numbers (as they appear in the directory) are passed to a PrintUser routine.

Example Create a list of users:

dim MyUserContainer as IADsContainer 
dim MyUser as IADsUser 
set MyUserContainer as GetObject("WinNT://ABX") 
for each MyUser in MyUserContainer 
PrintUser MyUser.Name, MyUser.TelephoneNumber 
next MyUser 

Adding Users to Groups

Adding users to groups for security purposes is a common and time-consuming activity for system administrators. In this example, the users from the preceding example are added to the Manufacturing Users group in the ABX organization, if they do not already belong.

Example: Adding users to groups

dim MyUserContainer as IADsContainer 
dim MyUser as IADsUser 
dim MyGroup as IADsGroup 
dim Filter as Variant 
Filter = Array("user"); 
set MyUserContainer = GetOBject("WinNT://ABX") 
MyContainer.Filter = Filter  ' filter out all objects except users 
set MyGroup = GetObject("WinNT://ABX/Manufacturing_Users") 
for each MyUser in MyUserContainer 
if not MyGroup.IsMember(MyUser) then 
MyGroup.Add(MyUser) 
end if 
next MyUser 

Starting and Stopping Services

In this example, John Smith is given the responsibility to manage services in the company. He wants to write a small script that stops all DHCP services.

Example 5: Start/stop services

Set dom = GetObject("WinNT://ABX") 
Dom.Filter = Array("Computer") 
For each comp in dom 
comp.Filter = Array("Service") 
For each svc in comp 
If ( svc.Name = Browser ) then 
svc.Stop 
End if  
Next 
Next 

With Windows 2000 Active Directory, a caller can also enumerate published services for a given organizational unit, search for all published services in a given enterprise and more.

Using ADSI for Schema Management

A useful feature of schema-based directory services is the ability of administrators to add properties to objects. For example, administrators at the ABX organization might want to create an ABXuser, based on the standard ADSI User object, which contains additional properties useful at ABX.

Assume that ABX wants to add a card key number to the user object, and call the new object type "ABXuser." ADSI makes this simple. First, a new class is created in the schema container and is marked as derived from the desired base class. New properties are added to the new class. The Visual Basic code to perform this extension appears below.

Example: Extend the schema

' get the Schema Path 
set ds = getobject("LDAP://RootDSE") ' and from that that the schema container 
set sch = getobject("LDAP://"&ds.get("schemaNamingContext"))  
' create the new attribute object 
set newatt = sch.create("attributeSchema","cn=CardKey") 
' set the desired values  
newatt.put "attributeId","1.2.840.113556.1.4.7000.16"  
newatt.put "oMSyntax",20 
newatt.put "attributeSyntax","2.5.5.4"  
newatt.put "isSingleValued",True 
' write it back to the DS  
newatt.setinfo 
'create a new class 
set newcls = sch.create("classSchema","cn=ABXUser") ' set the desired values 
newcls.put "cn","myClass"  
newcls.put "governsId","1.2.840.113556.1.5.7000.12" 
newcls.put "objectClassCategory",1 ' 1 = Structural Class 
newcls.put "subClassOf","user"  
newcls.put "possSuperiors","organizationalUnit"  
newcls.setinfo 
' add the new attibute (CardKey) to this class 
' Values for ::PutEx are defined in one of the IADS.H include file, found in the 
newcls.putex ADS_PROPERTY_APPEND,"mayContain", Array("1.2.840.113556.1.4.7000.16") 'It's Card Key's OID 
' write it back to the DS  
newcls.setinfo 

Conclusion

Most organizations have multiple directories in place. The presence of multiple directories within an organization poses complex challenges to users, administrators, and developers.

ADSI addresses these challenges by providing a single consistent, open set of interfaces for managing and using multiple directories.

ADSI and the associated components are an effective tool for simplifying the directory usage and management issues facing users and developers.