Editor's Note: This article is the final part of a 12-part series about Active Directory Service Interfaces (ADSI). The series started in the January 1999 issue. Refer to previous installments for definitions and background information.
For the Windows 2000's (Win2K's) Active Directory (AD) store to hold an object, the AD store needs a blueprint detailing that object's classes, attributes, and syntaxes. The AD schema stores the blueprints for all the objects that the AD store can hold. In other words, the AD schema contains details about all the classes, attributes, and syntaxes in the AD store.
Each object in the AD store is an instance of one or more classes in the schema. For example, the user cn=LeeFlight exists as an instance of the User class. Schema attributes define the pieces of information that a class, and thus an instance of that class, can hold. Among the many attributes that the User class can contain are TelephoneNumber (specifies the user's business telephone number), LastName (specifies the user's surname), and sAMAccountName (specifies the name that represents the user to Windows NT clients and servers that are lower in the Win2K hierarchy). The schema denotes whether an attribute is mandatory (e.g., sAMAccountName) or optional (e.g., TelephoneNumber). Syntaxes define the type of data that you can place in an attribute. For example, if the schema defines an attribute with the Boolean syntax, you can store True and False data in that attribute.
Microsoft designed the AD store to hold the most commonly used classes, attributes, and syntaxes in a server system. Microsoft also designed the schema to be extensible. You can extend the schema by creating custom classes or attributes but not by creating custom syntaxes. You must use the syntaxes that Microsoft provides. This limitation is understandable because Win2K's AD is Microsoft's first foray into the Directory Service (DS) market. Extending AD with user-designed syntaxes is likely to be on the list of modifications for release after Win2K.
Although you can't create custom syntaxes, you can extend the Win2K's AD in countless ways. For example, you can add a class for finance users or add an attribute that stores data about the languages that users speak or the professional qualifications they've attained. Extending the schema isn't difficult, but first you need to obtain an Object Identifier (OID) namespace and bypass built-in safeguards.
Obtaining an OID Namespace
Microsoft based the Win2K AD on the X.500 definition, which the International Organization for Standardization's (ISO's) International Telecommunications Union Telecommunication Standardization Sector (ITU-T) created in 1988. The X.500 definition specifies that you can use a special identifying process to uniquely define individual object classes in an organization. The process takes into account that classes can inherit from one another and that organizations might need to design and export a custom object.
The special identifying process uses an OID to uniquely identify every object. An OID consists of two parts:
- A part that specifies the unique path to the branch holding the object in the X.500 treelike structure
- A part that specifies the unique object in that branch
The OID uses a notation in which integers represent each branch and object and periods separate each branch and object. For example, an object's OID might be 220.127.116.11.4.1.3385.12.497, in which 497 references the object and 18.104.22.168.4.1.3385.12 references the branch. The 22.214.171.124.4.1.3385.12 branch is in the 126.96.36.199.4.1.3385 branch, and so on.
The AD schema uses this notation. Thus, if you want to create a custom object in the AD store, you need to obtain a unique branch, or OID namespace, for your organization. Using this namespace as your root, you can then create branches and leaf objects as needed.
The Internet Assigned Numbers Authority (IANA) maintains the main set of OID namespaces. The Information Sciences Institute (ISI) of the University of Southern California (USC) operates IANA. You can request an OID namespace, which IANA calls a private enterprise number, from IANA. Private enterprise numbers are free and aren't specific to any country.
If you want to obtain an OID namespace that is specific to a country, you can contact that country's ISO member body. IANA allowed the ISO member bodies to obtain an OID namespace from which they can sell branches to organizations. The ISO Web page at http://www.iso.ch/ addresse/membodies.html contains contact information for each ISO member body. However, I haven't seen any benefits to purchasing a country-specific OID namespace when you can get one for free by completing a brief online form at http://www.isi.edu/cgi-bin/iana/enterprise.pl. (If this URL changes, you can navigate to the form from IANA's Web site at http://www.iana.org.)
You can find the entire list of private enterprise numbers that IANA has assigned at ftp://ftp.isi.edu/in-notes/iana/assignments/enterprise-numbers. This list changes every time IANA assigns a new private enterprise number.
Bypassing the Built-in Safeguards
Before you can modify the schema, you must bypass two safeguards that Microsoft built into the system to protect against unauthorized changes. First, you must become a member of the Schema Admins group because only Schema Admins group members can change the schema. To become a group member, add your user account to the Schema Admins group in the Forest-Root-Domain, which is the first domain in your forest.
Second, you need to change the Registry on the domain controller that currently has the Floating Single Master Operations (FSMO—pronounced fizz-moe) Schema Master role. The FSMO Schema Master role is one of five AD roles. When a server assumes this role, it hosts changes to make sure that conflicts don't occur—the only time AD doesn't use multimaster replication.
The fastest, safest way to change the Registry is to open the Schema Master Microsoft Management Console (MMC). Right-click the Schema container in the left pane, and select Change Operations Master from the drop-down context menu that appears. In the Change Operations Master window that Screen 1 shows, select the check box The Schema may be modified on this server.
Alternatively, you can use regedt32 or regedit to locate the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Services\NTDS\Parameters key on the domain controller assuming the FSMO Schema Master role. Create a new REG_DWORD value called Schema Update Allowed, and set the value to 1.
After you've placed your account in the Schema Admins group and modified the Registry on the domain controller acting as the FSMO Schema Master, the system will accept any changes you make to the schema on that domain controller. If you think that you might move the FSMO Schema Master role between servers and want to make sure that the system accepts your changes no matter which domain controller is the FSMO Schema Master, you need to modify the Registry on each domain controller that might assume the FSMO Schema Master role.
You must host the FSMO Schema Master role on a working domain controller for AD to operate correctly. If you're going to perform maintenance on a FSMO Schema Master, you must give that role to another domain controller before you take the server down.
Extending the Schema
After you finish the preparatory work, you can use ADSI to create a custom attribute or class. To create an attribute, you need to create an instance of the attributeSchema object. To create a class, you need to create an instance of the classSchema object. Both objects have mandatory and optional properties. The system automatically sets some of the mandatory properties for you. For example, for the attributeSchema object, the system automatically sets values for the mandatory properties of Schema-ID-GUID (schemaIDGUID), Attribute-Security-GUID (attributeSecurity-GUID), Extended-Chars-Allowed (extendedCharsAllowed), System-Only (systemOnly), Object-Class (objectClass), NT-Security-Descriptor (nTSecurityDescriptor), and Instance-Type (instanceType).The system either generates an appropriate value or uses the default value. For example, the system automatically generates a globally unique ID (GUID) for the Schema-ID-GUID property and sets the NT-Security-Descriptor property to the default for new objects in the Schema container.
You need to set the values for those mandatory properties that the system doesn't set. For example, for the attributeSchema object, you need to set the values for the six mandatory properties of Common-Name (cn), Attribute-ID (attributeID), Attribute-Syntax (attributeSyntax), OM-Syntax (oMSyntax), Is-Single-Valued (isSingleValued), and LDAP-Display-Name (lDAPDisplayName). In addition, you need to set the values of any optional properties you want the object to have.
For example, suppose you want to create a custom attribute that will contain data specifying the various languages users speak. You want the attribute to be multivalued so that it can hold an array of case-sensitive strings between 1 and 50 characters long. You also want to index the attribute in the AD store to facilitate rapid searches. Listing 1 contains code to create this attribute.
You begin this script by declaring two variables: adsAttribute (an attribute object) and adsSchemaContainer (a Schema container object). You then use the same fundamental interfaces to manipulate objects in the Schema container that you use to perform analogous tasks in the Domain container. Specifically, you use the GetObject method to bind to the Schema container and the Create method to create an instance of the attributeSchema object.
Next, you set the values for the six mandatory properties that the system doesn't automatically set.
Common-Name (cn). The attribute's Common-Name needs to begin with the company name (or a shortened version of it) so that other people will immediately know that your company created the attribute. In addition, when you browse the schema, all your company's custom attributes and classes will appear in one section. Because Mycorp is creating this custom attribute, you give the attribute the Common-Name of Mycorp-Languages-Spoken. (The Active Directory Programmer's Guide has more guidelines for creating Common-Names. You can download the guide from http://msdn.microsoft.com/developer/ windows2000/adsi/actdirguide.asp.)
Attribute-ID (attributeID). This property contains the attribute's OID. After you obtain your OID namespace, you need to develop a system to assign new OID numbers. For example, suppose Mycorp's schema manager has developed the following system to determine new OIDs:
- Mycorp's root OID namespace is 188.8.131.52.4.1.999999.
- Mycorp's new attributes use the value 184.108.40.206.4.1.999999.1.1.x, where x increments upward from 1.
- Mycorp's new classes use the value 220.127.116.11.4.1.999999.1.2.x, where x increments upward from 1.
Because the Mycorp-Languages-Spoken attribute will be the 28th new attribute that Mycorp creates, you give this attribute the OID of 18.104.22.168.4.1.999922.214.171.124.
Attribute-Syntax (attributeSyntax) and OM-Syntax (oMSyntax). When you create a new attribute, you must specify its syntax. To uniquely identify the syntax, you set a pair of values: the Attribute-Syntax and the OM-Syntax. Table 1 provides the value pairs for the AD's syntaxes. The Attribute-Syntax values are the X.500's OID syntax values. Table 1 gives the X.500 name of the syntax first and any special Microsoft chosen name following it in parentheses. Because Microsoft needed syntaxes that X.500 didn't provide, several syntaxes in Table 1 have the same OID. To uniquely distinguish these syntaxes, Microsoft introduced the second identifier, OM-Syntax.
Most of the syntaxes are standard programming types, so selecting the value pair isn't difficult. For example, because the Mycorp-Language-Spoken attribute will be a case-sensitive string, you give the Attribute-Syntax property a value of 126.96.36.199 and the OM-Syntax property a value of 27. If you're unsure which syntax to use, look at similar attributes to see whether you can find the appropriate syntax.
Is-Single-Valued (isSingleValued). You use this property to specify whether the attribute can have more than one value. If the attribute can have only one value, you can either specify True or not set this property. If the attribute can have more than one value, as is the case with Mycorp-Languages-Spoken, you specify False.
LDAP-Display-Name (lDAPDisplayName). Lightweight Directory Access Protocol (LDAP) clients use the LDAP-Display-Name to read and write to the attribute. You modify the Common-Name to get the LDAP-Display-Name. Typically, you lowercase the first letter in the first word and delete the hyphens. So, for example, the LDAP-Display-Name for Mycorp-Languages-Spoken is mycorpLanguagesSpoken.
After you use the IADs::Put method to set these six mandatory properties, you must use the SetInfo method to write the properties to the AD store. As I explained in "An ADSI Primer, Part 4: Manipulating the Property Cache" (April), you must set mandatory properties before you write the property cache to the AD store.
The next task is to set the optional properties. After you use the explicit IADs::GetInfo call to load all the adsAttribute object's properties into the property cache, you specify the object's description and set the appropriate values. In this case, you want to set the Range-Lower (rangeLower), Range-Upper (rangeUpper), and Search-Flags (searchFlags) properties. Because the Mycorp-Languages-Spoken attribute will hold case-sensitive strings between 1 and 50 characters long, you set Range-Lower to 1 and Range-Upper to 50. Because you want to index the Mycorp-Languages-Spoken attribute in the AD store, you set Search-Flags to True, which has a value of 1.
Finally, you use SetInfo to write the optional properties from the property cache to the AD store. To view the properties of the newly created attribute, double-click the attribute in the display pane of the Schema Master MMC. Screen 2 shows the properties of the Mycorp-Languages-Spoken attribute.
If You Don't Know the Domain's Name
Knowing that the schema exists as a container under the Configuration container within all the domains in a forest is great if you know the name of the domain. However, if you're writing code to modify the schema on potentially any domain (e.g., you're writing a program that you'll distribute to multiple organizations with different directories), you need to know how to reference the Schema container relative to the current domain without knowing the domain's name. You also need to know how to discover which server is currently the FSMO Schema Master so that you can make schema changes directly on it.
You can use the code at callout A in Listing 2 to find the full path to the Schema container. In this code, you first retrieve an object that points to the root of the DSA-Specific Entry, or DSE. (DSA is the acronym for directory service agent.) The DSE contains a list of all the naming contexts (NCs) that are available within the forest on a domain controller. You then obtain the ADsPath of the schema NC, pass the ADsPath to the GetObject method, and retrieve a reference to the domain controller's Schema container.
If you want to make changes to that domain controller's Schema container without forcing the FSMO role to the currently connected server, you need to connect to the server holding the FSMO Schema Master. This additional task requires a bit more code, as the rest of the code in Listing 2 shows.
You use the IADs::Get method to retrieve the fSMORoleOwner attribute of the Schema container. This attribute contains a string such as "cn=NTDS-Settings, cn=Moose, cn=Servers, cn=Main-Headquarters-Site, cn=Sites, cn=Configuration, dc=windows, dc=mycorp, dc=com". From this string, you extract the server's name and use it to connect to the FSMO Schema Master. For example, the resulting value of txtServerIPName from the string just given is moose.windows.mycorp.com.
Test Before You Apply
Creating an attribute with ADSI is easy. However, after you change the schema and create a custom attribute or class, you can't delete it. In addition, because schema modifications affect an entire forest, you need to thoroughly test your code in a test domain within a separate forest before you apply it to the AD store.
That's it for this series. I hope you've enjoyed my introduction to ADSI scripting.
This article is adapted from Alistair G. Lowe-Norris' forthcoming book Windows 2000 Active Directory (O'Reilly and Associates).