Windows 2000 Active Directory (AD) offers a sea of data-management possibilities. "Diving into the AD Schema," September 2001, helps you wade through the AD schema's somewhat complicated collection of terms and relationships: classSchema and attributeSchema objects (and associated attributes) and the relationships among abstract, auxiliary, and structural classes and attributes. Now, you're ready to consider heading into the deep end of the pool to extend the schema.
First, decide whether an extension is truly necessary and think about how it will affect your AD forest. Keep in mind that you can't remove AD extensions under Win2K, so you must understand the schema's restrictions and requirements, and you must carefully plan and test an extension before you implement it in your production environment. Create an extension object successfully in a test environment, then transfer the new object to your production AD.
To Extend or Not to Extend?
The best way to start a schema extension project is to consider whether the data you want to add even belongs in AD. Not all information is suitable for AD storage.
Publish only data that's of global network interest. AD isn't an appropriate storage place for data that only one server or one user accesses.
Avoid publishing data that changes frequently. Win2K distributes and replicates the directory on a regular basis; if the data is likely to be obsolete before AD can replicate data across the enterprise, the information doesn't belong in AD. A good rule of thumb is to publish data that has a useful lifetime longer than twice AD's replication latency (i.e., the time AD takes to replicate information updates or changes across the entire enterprise). Tools such as NetPro's DirectoryAnalyzer can help you monitor your network's replication latency.
Consider the bandwidth that data replication will require; the bandwidth will depend on how often the information changes and the size of the data object. For example, you might not want to store employees' pictures in AD if those pictures are high resolution and have 16 million colors.
If you do need to store a piece of information in AD but the data object is excessively large or changes frequently, you can store a pointer to the information to save replication bandwidth and AD storage space. You can store the data in a Microsoft SQL Server database and publish an ODBC connection string (with the SQL SELECT statement) as a dedicated attribute in AD. Or you can store the data on a Web server and publish a URL that refers users to the information. Of course, AD-enabled applications must be able to interpret the pointer correctly.
If you're confident that your new data belongs in AD, consider whether you can tweak the current schema to meet your needs. Perhaps you can modify an existing class instead of creating a new class. An extensive understanding of the schema in general and of your organization's needs in particular is invaluable and is the key to making the right choice to adapt or extend the base schema. (To perfect your schema knowledge, see "Diving into the AD Schema." You can also study the Microsoft Developer Network—MSDN—AD overview at http://msdn.microsoft.com/certification/schema/default.asp.)
No Turning Back Now
When you've decided to move ahead with a schema extension, keep in mind that only one schema exists for an entire AD forest. Win2K globally replicates the Schema naming context (NC), so any change you make to the Schema NC affects the entire forest.
Moreover, schema extensions aren't reversible. If you add a new classSchema or attributeSchema object, you can't delete it. You can disable some classes and attributes to work around this prohibition, but you can't disable an attribute of an active class, and you can't disable or rename Category 1 classes or attributes. (Objects that are part of the base Directory Information Tree—DIT—are Category 1 objects. See "Diving into the AD Schema" for details about schema object categories.)
You can, however, make certain changes to active schema objects. You can add a new class to a Category 1 or Category 2 classSchema object's possSuperiors attribute. You can add a new attribute to a Category 1 or Category 2 classSchema object's mayContain attribute. You can add a new auxiliary class to a Category 1 or Category 2 classSchema object's auxiliaryClass attribute. You can change a Category 2 classSchema or attributeSchema object's lDAPDisplayName attribute. (Changing the Lightweight Directory Access Protocol—LDAP—name lets you work around a mistake rather than rebuild the whole Win2K infrastructure or live with the mistake forever.)
But you can't add an attribute to or delete an attribute from an object's mustContain attribute, either directly, through an auxiliary class, or through inheritance from a parent class (superclass). Many Win2K components depend on Category 1 objects, so you can't modify these objects' rangeLower, rangeUpper, attributeSecurityGuid, defaultObjectCategory, objectCategory, or lDAPDisplayName attributes.
The complexity of these conditions clearly highlights the importance of carefully planning a schema modification before you start out. I strongly suggest that you choose one person to be in charge of planning, making, and verifying schema changes. This arrangement helps avoid confusion. For simplicity's sake, this article assumes that you'll function as schema manager.
The wise schema manager sets up a dedicated environment to play with before he or she even thinks of touching a production system. Use a separate test system on which you can uninstall and reinstall Win2K without affecting your production forest. I suggest that on the test system you configure one AD domain controller (DC), running one forest. Then, you can demote the system to remove schema changes and promote the system to retrieve the base AD schema.
In preparation, make sure that the user account you're using is a member of the Schema Admins group. You also need to locate the production Win2K DC that owns the schema Flexible Single-Master Operation (FSMO) role.
In your one-DC test environment, this machine will of course be your single DC. However, when you're ready to proceed to your production environment, you must have access to the schema FSMO machine because you must modify the registry on the schema FSMO system to switch the schema NC to read/write mode. To modify the registry, open a registry editor, go to the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters subkey, and set the Schema Update Allowed value (of type REG_DWORD) to 1.
Use a test environment. I can't repeat this recommendation too many times. When you follow careful planning and testing procedures, you dramatically increase your odds of completing a trouble-free schema-extension project.
Obtain an Object Identifier
Another prerequisite for creating an extension is to obtain an Object Identifier (OID) for the extension. (As I explain in "Diving into the AD Schema," each AD class and attribute is a unique object that must have a unique OID, which you build upon base OIDs.) Your new object's attributeID attribute will hold the OID.
You can use the Microsoft Windows 2000 Server Resource Kit's Oidgen (oidgen.exe) tool to obtain two base OIDs—one for classSchema objects and one for attributeSchema objects. (Using Oidgen is faster than going through the more lengthy International Organization for Standardization—ISO—process. However, your OIDs will then fall under the Microsoft arc, so you might prefer to apply to the ISO for your base OIDs.) Run the tool once only, then assign OIDs based on the generated roots. For example, Figure 1 shows sample output from an Oidgen run. Given the generated base OIDs that the figure shows, the OID for your first new attributeSchema object could be
1.2.840.1135188.8.131.5200.233 .28688.28684.8.450094.294295 .2081771.1774679.1
and the OID for your first new classSchema object could be
1.2.840.1135184.108.40.20600 .111.28688.28684.8 .331888.572581.472599 .1295831.1
To register your newly generated base OIDs with Microsoft, send an email message to firstname.lastname@example.org. Include your company's name, a contact name, an email address, and the OID base numbers for classes and attributes.
What's Your Name?
Next, your extension object needs a unique name. Applications that participate in the Windows Logo program must follow certain schema object naming rules. You should observe these rules for consistency's sake, even if your company isn't a program participant. The schema naming conventions concern two object attributes: the common name (cn) attribute and the LDAP display name (lDAPDisplayName) attribute.
A new object's cn attribute takes the form company_nameunique_object_name. The prefix, or first token, is your company's name, Internet domain name, acronym, or other company identifier. You can hyphenate this name—for example, to specify the application or department that will use the extension. The second token is a hyphen. The third token is a descriptive and unique name for the class or attribute that you're creating.
For example, suppose you're the schema manager for a company named DogFood, which has the Internet domain name df.com. The company's human resources (HR) department plans to deploy a new global, AD-enabled application that manages employee badge numbers. This plan requires the creation of a new AD schema object to hold each employee's badge number. In this situation, a good name for the new object's cn attribute would be DF-HR-Badge-Number. The first token (i.e., DF-HR) combines the company's domain name and the group to which the new object relates. The third token (i.e., Badge-Number) specifically and uniquely identifies the object's purpose.
To form the object's lDAPDisplayName attribute, lowercase the first character of the cn attribute name. Then, capitalize the first character following each hyphen and remove all hyphens after the second- token hyphen. Thus, DogFood's new badge-number object would have the following lDAPDisplayName attribute: dF-HR-BadgeNumber.
For more details about schema object naming conventions, read the MSDN Certified for Windows Program's naming rules (http://msdn.microsoft.com/certification/namingrules.asp) or download the program's Server Specification (v 1.3) documentation (http://msdn.microsoft.com/certification/download.asp) and refer to Chapter 4, "Active Directory." To guarantee that no other company uses the same prefix as your organization, you can register your prefix at http://msdn.microsoft.com/certification/ad-registration.asp.
After you assign an OID and a name to your extension object, you have several decisions to make regarding the type of schema object to create. To illustrate this decision-making process, I'll continue with the example of the DogFood HR department's badge-number extension.
Each employee has a unique badge number, so the logical step is to make the new object an attribute (i.e., the dF-HR-BadgeNumber attributeSchema object) that you can associate with the user class (i.e., the user classSchema object).
The next decision is whether this new attribute will be mandatory or optional. You want to make sure that the attribute is included in each new user instance, so you should make the new attribute mandatory, right? Wrong. Here's where the restrictions I discussed earlier come into play. An object's mustContain attribute defines the object's mandatory attributes. But you can't modify an existing Category 1 object's mustContain attribute—and the user class, which is part of the base DIT, is a Category 1 object.
You might think that a logical workaround would be to create a new user class, associate your new attribute as a mandatory attribute, then use the new user class (rather than the standard user class) to create new user instances. But Microsoft has developed—and doubtless will continue to develop—many tools that work with and depend on AD's use of the standard user class (e.g., all the management applications that come with Win2K). Therefore, abandoning that class would turn your schema-extension project into a Win2K management-tool development project. The only solution, then, is to associate the badge-number attribute to the user class as an optional attribute.
Your next decision is how to implement the association. Avoid the temptation to associate the attribute by inheritance. This tactic would involve associating the attribute to an abstract class such as Top, Person, or organizationalPerson. These classes are well-defined X.500 classes, and such a change would disrupt the class definitions' compatibility with the X.500 definitions.
You could directly associate the attribute to the user class, but if you eventually create many extension attributes to serve a variety of applications, you'll have difficulty determining the origin or purpose of attributes you associated through this method. For this reason, the best way to associate the new attribute to the user class is to create a new auxiliary classSchema object, dF-HR-EmployeeBadge, and associate the new attribute to the auxiliary class. As Figure 2 shows, you can then use this auxiliary class to include the new badge-number attribute in the user class. An auxiliary class offers a structured way to group extension attributes according to application or meaning.
When you create a new auxiliary class, you can derive the class from an existing class, which you specify as a superclass. In our example, however, the new class is simply a means to associate one new attribute to the user class, so dF-HR-EmployeeBadge doesn't need to derive any attributes from an existing class. You can simply create a new auxiliary class without specifying a superclass.
Now you have a specific idea of what to do. Create an auxiliary classSchema object (dF-HR-EmployeeBadge) with an optional attributeSchema object (dF-HR-BadgeNumber), and add the new class to the user class's auxiliaryClass attribute.
Define the Extension
First, you need to create the attributeSchema object dF-HR-BadgeNumber. You must define the characteristics for this attribute through other associated attributes. (See "Diving into the AD Schema" for a detailed explanation of this somewhat confusing arrangement, as well as a list of mandatory and optional attributes.) You've already determined the contents of the attributeID attribute, which holds the object's OID; the cn attribute, which holds the object's CN; and the lDAPDisplayName attribute, which holds the object's LDAP display name. For the badge-number example, other important attributes are those that define the new object's syntax, range, indexing status, value type (i.e., single-valued or multivalued), globally unique ID (GUID), and Global Catalog (GC) publication status.
The attributeSyntax attribute defines the syntax. The nature of the information you want to store in the attribute (e.g., a string, a number) determines its syntax.
The rangeLower and rangeUpper attributes define the range, which determines the attribute's minimum and maximum size (for attributes that use string syntax) or value (for attributes that use numeric syntax). The range attributes don't specify a fixed length; rather, they define rules that AD enforces when it sets an attribute value. Therefore, you can simply change the rules to easily shorten or lengthen the range.
The searchFlags attribute defines the new attributeSchema object's indexing status. If you want an application to be able to perform LDAP search operations by using the new attribute as a keyword, you should index the new attribute to speed up the search process. However, be aware that the system takes more time to create object instances from classes with many indexed attributes. Thus, you should index a new attribute only when you're sure that you'll need to perform frequent search operations on it.
The isSingleValued attribute defines whether an attribute is single-valued or multivalued. In the case of the DogFood badge-number object, the new attributeSchema object will be single-valued because each employee has only one badge number.
The schemaIDGUID attribute specifies the object's GUID. To generate the schemaIDGUID value, you can use the Microsoft Platform Software Development Kit's (SDK's) Uuidgen (uuidgen.exe) tool. Manually generating a GUID ensures that the new class or attribute will have an identical schemaIDGUID attribute across various AD forest installations, which facilitates consistent references to the object. (If you don't specify this attribute, AD will automatically create a GUID for the new object. However, if you create the attribute in different forest installations, the schemaIDGUID value will be different in each installation.)
The isMemberOfPartialAttributeSet attribute defines whether the new attributeSchema object will replicate to the GC. You must publish an attribute to the GC when you want the attribute to be available throughout the forest (rather than only in the domain in which you create a user object).
Replicating to the GC isn't necessarily a good idea in other cases, though. Like indexing, publishing attributes to the GC creates additional replication traffic. Also, when you set the isMemberOfPartialAttributeSet attribute to publish the new object in the GC, you start a full synchronization of all the GCs in the AD forest. If you have a huge forest with a lot of GCs, synchronization causes massive replication traffic on the network. (For information about this documented behavior, see the Microsoft article "How to Modify Attributes That Replicate to the Global Catalog" at http://support.microsoft.com/support/kb/articles/q248/7/17.asp.)
Now that you've collected the information you need to define the new attribute, you're ready to create your schema extension in your test AD. At first glance, the most obvious tool to use to modify the schema is the Microsoft Management Console (MMC) Active Directory Schema snap-in, which gives you access to the Create New Attribute dialog box. However, I don't recommend using this snap-in to create your new object. The dialog box doesn't expose all the attributes that you might want to define for the schema extension (e.g., schemaIDGUID is missing). Also, you must manually enter the extension's OID into the snap-in, and you can easily mistype the number. Most important, after you've tested the extension on your test system, you must reenter the information into the production-system snap-in; this requirement introduces the risk of making a mistake that you didn't make during your test.
Instead, I recommend that you use a script to modify the schema on your test system. You can then use the same script on your production systems, or you can simply use Win2K Server's LDAP Data Interchange Format Data Exchange (Ldifde) tool to transfer the perfected extension to your production AD. (If you insist, however, on using the snap-in capabilities to modify the schema, keep the potential problems in mind.)
Script the Solution
To modify your test schema programmatically, you can use Microsoft Active Directory Service Interfaces (ADSI), a Microsoft-provided COM abstraction layer that sits over LDAP. (For details about using ADSI, see Bob Wells, Scripting Solutions, "Easy Active Directory Scripting for Systems Administrators, Part 1," September 2000, or read the Microsoft white paper "Active Directory Service Interfaces" at http://www.microsoft.com/windows2000/techinfo/howitworks/activedirectory/adinterface.asp.) You can use ADSI with Visual Basic (VB) 6.0, VBScript, JScript, or C.
Listing 1, page 64, shows selected parts of a VBScript script (createdogfoodschema.vbs) that I wrote to create the badge-number extension for the DogFood company. An ellipsis (...) represents missing lines. (If you download the complete listing, be sure to read the included README file before you run the code.) For other code samples and more information about extending the schema, see the Platform SDK's Active Directory Programmers Guide, which you can access at the MSDN Web site (http://msdn.microsoft.com/library/default.asp?url=/library/psdk/adsi/glschemex_5py9.htm).
As Listing 1 shows, the script first defines the constants that it will use for the new attributeSchema and classSchema objects. The script then connects to the RootDSE object to get the location of the schema NC in the current forest.
Next, the script sets the various attributes necessary to create the new attributeSchema object. To convert the schemaIDGUID attribute octet string to a binary octet string (a process that's necessary to set the schemaIDGUID attribute), the script uses the WSHOcx object, which exposes the StringGUIDToBinaryGUID method. This method is an ActiveX COM object and is instantiated in the hidden lines of the script. (The WSHOcx object is included with the downloadable 22540.zip file available on the Windows 2000 Magazine Web site.)
After the script creates the new badge-number attribute in the AD Schema NC, it creates the class that will hold the attribute. This process follows the same steps as attribute creation, except that the script initializes a different set of attributes to create the classSchema object. The governsId attribute defines the class's OID. Because this class is an auxiliary class that you're using to include a new attribute in the user class, the objectClassCategory attribute defines the category as 3. The lDAPDisplayName and the schemaIDGUID attributes hold the auxiliary class object's LDAP display name and GUID, respectively.
After the script creates the auxiliary class, it must associate the new attribute with the new class. Callout A in Listing 1 shows the code that executes the association in the mayContain attribute.
The script's next operation updates the user class to include the new auxiliary class. Note that the script must manipulate the user class's auxiliaryClass attribute as multivalued, or the script will remove any existing auxiliary classes associated to the user class. (For example, the script might accidentally remove associated Microsoft Exchange 2000 Server auxiliary classes from the user class.) Callout B shows the code that executes this definition.
Your schema changes don't take effect immediately. For performance reasons, AD holds a copy of the schema in memory. A refresh of the in-memory copy occurs automatically 5 minutes after a schema modification, but you can force an immediate update. To do so, the script uses the RootDSE object's functional schemaUpdateNow attribute.
Alternatively, you can update the cache through the Active Directory Schema snap-in. Or you can use a third option: You can use Ldifde to load the LDIF file that Listing 2 shows. (An LDIF file is simply an ASCII version, with binary values encoded in base 64, of exported directory content. For more information about Ldifde and LDIF files, see the Microsoft article "Using LDIFDE to Import/Export Directory Objects to the Active Directory" at http://support.microsoft.com/support/kb/articles/q237/6/77.asp.)
Import to Production
After you've perfected your schema extension in a test environment, you must reproduce the extension in your Win2K production AD forest. You can use the script that you developed in the lab; simply adapt any parameters that differ between your test and production environments. Or you can use Ldifde to create an LDIF file that contains the extension attribute and class objects, export the LDIF file, and import the file into your production forest. For example, to create the dogfoodschema.ldf file, which Figure 3 shows, type
LDIFDE -f DogFoodSchema.ldf -d "cn=Schema,cn=Configuration, dc=MyW2KRootDomain,dc=com" -r "(cn=df-HR*")
This LDIF file contains the extensions as they exist in your test forest. Before you use Ldifde to import the file into your production forest, you should review the file's contents and make any necessary changes to match your production systems' specifications.
Wait for Replication
After you load the schema update to your production system, you should ensure rather than assume that AD has replicated the update across the forest. This procedure is especially important when your update's purpose is to prepare for the installation of an application such as Exchange 2000 because you must wait to start the software installation until the update properly replicates to every DC. To verify that replication is complete, connect to a remote DC through the Active Directory Schema snap-in and determine whether the extension is present in the Schema NC. You should also monitor the Directory Service (DS) event log for replication errors. (Products such as DirectoryAnalyzer can help you monitor AD replication.)
Manage New Objects
After you've successfully created your extension, you still have some work to do. You need a way to manage the new attributes and classes that you added to the schema. You can develop a Web interface or client-side application to do the job, or you can create an MMC extension snap-in. However, the standard MMC Active Directory Users and Computers snap-in doesn't show extension attributes by default. You must first use display specifiers to extend the console. Display specifiers are attributes that reside in the AD Configuration NC's cn=DisplaySpecifiers container and that contain data that relates to a specific class, as well as define how to display that data. A complete explanation of display specifiers and how to use them is outside the scope of this article, but you can read the Microsoft white paper "Active Directory Display Specifiers" (http://www.microsoft.com/windows2000/techinfo/howitworks/activedirectory/addisplay.asp) for details.
Secure Your Extensions
Each classSchema object has a default security descriptor that protects objects you instantiate from that class. If you need more specific control over access to your new extensions, AD provides a way to define new rights and dedicate those rights to a new object. (For our DogFood company example, you might want to create a right that lets users read, but not change, the badge-number attribute.) These rights, called extended rights, reside in the Configuration NC's cn=Extended-Rights container and contain information about the attributes to be protected and how to protect them. Like display specifiers, extended rights are outside the scope of this article; for information about creating and implementing these rights, refer to the Platform SDK on the MSDN Web site (http://msdn.microsoft.com/library/default.asp?url=/library/psdk/adsi/glsecur2_3cvn.htm).
Look, then Leap
To successfully modify the AD schema, you must understand the steps you need to take and commit to following them exactly. The most vital requirement is a well-defined plan that includes adequate testing. This point is extremely important because schema modifications are irreversible under Win2K. As schema manager, you must be sure to validate all the tests and configurations before you decide to implement the schema extension in your production forest. When you respect these basic rules, you can enjoy AD's flexibility and ability to meet your data needs—and you won't feel as though you're in over your head.