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
Directory Services Today
Active Directory Service Interfaces
ADSI Standard Objects
ADSI Programming Model
Using ADSI for Schema Management
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.
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.
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).
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
Any directory provider can implement an ADSI provider; users gain freedom of choice in directory services without sacrificing manageability.
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.
ADSI objects provide easy access to directory services for Java applets and programs through Java COM.
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.
ISVs and sophisticated end users can develop serious applications, using the same ADSI models that are used for simple scripted administrative applications.
Directory providers, ISVs, and end users can extend ADSI with new objects and functions to add value or meet unique needs.
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.
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.
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.
Figure 3: Provider architecture
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 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
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.
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.
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.
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:
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
Identifies comp1 in the Redmond Domain
Identifies alice as a local user on comp1 computer.
LDAP://OU=Sales, DC=ArcadiaBay, DC=COM
Identifies an organizational unit in the Arcadia Domain
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 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
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
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
Standard Leaf Objects
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
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.1135220.127.116.1100.16" newatt.put "oMSyntax",20 newatt.put "attributeSyntax","18.104.22.168" 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.113522.214.171.12400.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.1135126.96.36.19900.16") 'It's Card Key's OID ' write it back to the DS newcls.setinfo
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.