Classes and Attributes

Microsoft® Windows® 2000 Scripting Guide

Each Active Directory forest contains a schema. The schema is the library from which all objects are created in Active Directory. You can think of the schema as a template repository whose templates are categorized as either classes or attributes. Classes are used by Active Directory to create objects in the forest. Classes contain references to the attributes in the schema; thus, the objects created from classes contain attributes. Objects created from classes are logical representations of objects in the network, such as a user or a printer. Figure 5.6 shows the relationship between attributes and classes in the schema and the objects they create (instantiate) in Active Directory.

Figure 5.6 Attribute, Class, and Instantiated Object Relationships

sas_ads_006c

The process of creating an object from a class is called instantiation, and an object created from a class is called an instance. Each class defines a type of object that can be created in the forest. Attributes contained in a class serve to describe the class. For example, the computer class contains a cn attribute whose value is Computer and an lDAPDisplayName attribute whose value is computer. Each attribute of a class has a purpose in the directory. For instance, as you have seen, the LDAP provider uses the lDAPDisplayName attribute in ADSI scripts to access objects.

Mandatory and Optional Attributes

Classes also include the systemMustContain and systemMayContain attributes. These attributes define which attributes an object of this class must have (mandatory attributes) and which additional attributes it can have (optional attributes) when it is instantiated. Optional attributes can be set at the same time as the mandatory attributes (when an object is created) or later.

Listing 5.46 shows a script that reads any mandatory and optional attributes assigned to the computer class. To carry out this task, the script performs the following steps:

  1. Use the On Error Resume Next statement.

  2. Set the E_ADS_PROPERTY_NOT_FOUND constant equal to the ADSI error code generated if either the systemMustContain or systemMayContain attribute is not found in the local property cache (used in lines 10 and 21).

  3. Use rootDSE to determine the value of the schemaNamingContext attribute.

    For more information about rootDSE, see "Root Directory Service Entry" earlier in this chapter.

  4. Bind to the computer class in the schema container using the GetObject function and the LDAP provider.

  5. Initialize the arrMandatoryAttributes variable with the systemMustContain attribute (line 9).

    • If initializing the variable returns the error code equal to E_ADS_PROPERTY_NOT_FOUND, echo to the command window that no mandatory attributes were found.

    • Otherwise, use a For Each statement to read and display the name of each mandatory attribute in the computer class.

  6. Initialize the arrOptionalAttributes variable with the systemMayContain attribute (line 20).

    • If initializing the variable returns the error code equal to E_ADS_PROPERTY_NOT_FOUND, echo to the command window that no optional attributes were found.

    • Otherwise, use a For Each statement to read and display the name of each optional attribute in the computer class.

Listing 5.46 Reading the Mandatory and Optional Attributes of the Computer Class

  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
On Error Resume Next
Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D
strClassName = "cn=Computer"
Set objRootDSE = GetObject("LDAP://rootDSE")
Set objSchemaClass = GetObject("LDAP://" & strClassName & "," & _
 objRootDSE.Get("schemaNamingContext"))
arrMandatoryAttributes = objSchemaClass.GetEx("systemMustContain")
If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then
 Wscript.Echo "No mandatory attributes"
 Err.Clear
Else
 Wscript.Echo "Mandatory (Must-Contain) attributes"
 For Each strAttribute in arrMandatoryAttributes
 Wscript.Echo strAttribute
 Next
End If
arrOptionalAttributes = objSchemaClass.GetEx("systemMayContain")
If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then
 Wscript.Echo "No optional attributes"
 Err.Clear
Else
 Wscript.Echo VbCrLf & "Optional (May-Contain) attributes"
 For Each strAttribute in arrOptionalAttributes
 Wscript.Echo strAttribute
 Next
End If

When the script runs, it echoes a message indicating that there are no mandatory attributes and then it echoes a list showing the entries in the systemMayContain attribute of the computer class, as shown in the following abbreviated list:

No mandatory attributes
Optional (May-Contain) attributes
volumeCount
siteGUID
rIDSetReferences
...
dNSHostName
defaultLocalPolicyObject
cn
catalogs

When you create a computer object you must specify a value for the sAMAccountName attribute because it is a mandatory attribute of the computer class. However, running the preceding script suggests that the computer class does not contain any mandatory attributes. The script is not incorrect. The computer class, like many other classes, inherits attributes from other classes by means of direct inheritance or through the Active Directory class hierarchy. The sAMAccountName attribute is mandatory, but is not defined in the computer class.

Class Inheritance and Categorization

Not only is the Active Directory hierarchical in structure (domains are interconnected by trees and trees are interconnected to form a forest), but so are the classes in the schema. Inheritance from one class to another means that some attributes defined for a class are inherited, whereas others are applied directly to the class. Ultimately, the object that is instantiated from the class can contain all of the attributes of the class.

Understanding the differences between class categories will help you determine how objects are used in the forest. In the case of the computer object, the object inherits attributes from the user class. Moving up the class hierarchy, attributes are inherited from the organizationalPerson, person, and top classes. Notice that the user class contains attributes directly applied by the mailRecipient and securityPrincipal classes. The securityPrincipal class contains the sAMAccountName attribute, which is mandatory for all classes that inherit this attribute. Thus, the computer class contains the sAMAccountName attribute through inheritance.

Other distinctions about objects can be drawn by understanding the class hierarchy and inheritance. For example, the contact class is not a securityPrincipal because the securityPrincipal auxiliary class is not applied to the contact class and the contact class does not inherit the attributes of this auxiliary class. Therefore, if you want to create a user account type that is a security principal, you know from the class relationship that a contact object is not a security principal.

Classes are categorized as abstract, structural, auxiliary, or 88. This categorization is stored as an integer in the objectClassCategory attribute of each class.

  • Abstract classes. Classes of this type provide attributes that flow through the hierarchy, but they cannot be used to instantiate an object. The objectClassCategory attribute for this type of class has the value 2.

  • Auxiliary classes. Classes of this type provide attributes that extend a structural class, but they cannot be used to form a structural class by themselves or instantiate an object. The objectClassCategory attribute for this type of class has the value 3.

  • Structural classes. Classes of this type can be instantiated into objects and can contain additional attributes that are not inherited from the other class types. The objectClassCategory attribute for this type of class has the value 1.

  • 88. Classes of this type were defined before there was a specification to classify class categories. Therefore, they are not required to be categorized as abstract, auxiliary, or structural and instead are assigned the value 88. Classes assigned this value behave like structural classes in that they can be instantiated into objects. However, you should view 88 classes as abstract classes because the objects that they create are not typically used to perform Active Directory tasks or to populate the Active Directory store. The objectClassCategory attribute for this type of class has the value 0.

Active Directory complies with the X.500 standard, and therefore must maintain these class categories as defined in the X.500 1993 specification. Any classes defined before the 1993 specification comply with the X.500 1988 specification, which does not contain a categorization requirement. Thus, the 88 category includes all classes defined before there was a categorization requirement.

The script shown in Listing 5.47 reads the objectClassCategory attribute of the classes Top, Mail-Recepient, Security-Principal, Person, Organizational-Person, Contact, User, Computer, and Organizational-Unit. To perform this task the script must:

  1. Initialize a variable with an array containing the common names of the classes.

    The common name of each class, not the value of the lDAPDisplayName attribute, is provided to the part of the GetObject function appearing on line 9. GetObject uses the common name of the attribute to build the distinguished name for binding to a class in the schema container.

  2. Use rootDSE to determine the value of the schemaNamingContext attribute.

    For more information about rootDSE, see "Root Directory Service Entry" earlier in this chapter.

  3. Use a For Each statement to bind to each class in the schema container using the GetObject function and the LDAP provider.

  4. Initialize the intClassCategory variable with the integer value of the objectClassCategory attribute.

  5. Use a Select Case statement to test the value of the intClassCategory variable and display a message indicating the class type of each class.

Listing 5.47 Reading the objectClassCategory Attribute of Several Classes

  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
arrClassNames = Array _
 ("cn=top","cn=mail-Recipient", "cn=security-Principal", _
 "cn=person", "cn=Organizational-Person", _
 "cn=contact", "cn=user", "cn=computer", "cn=organizational-Unit")
 
Set objRootDSE = GetObject("LDAP://rootDSE")
For Each ClassName in arrClassNames
 Set objSchemaClass = GetObject("LDAP://" & ClassName & "," & _
 objRootDSE.Get("schemaNamingContext"))
 intClassCategory = objSchemaClass.Get("objectClassCategory")
 WScript.STDOUT.Write ClassName & " is "
 Select Case intClassCategory
 Case 0
 Wscript.Echo "88"
 Case 1
 Wscript.Echo "structural"
 Case 2
 Wscript.Echo "abstract"
 Case 3
 Wscript.Echo "auxiliary"
 End Select
Next

When the script runs, it echoes the class categories of selected classes to the command window, as shown in the following list:

cn=top is abstract
cn=mail-Recipient is auxiliary
cn=security-Principal is auxiliary
cn=person is 88
cn=Organizational-Person is 88
cn=contact is structural
cn=user is structural
cn=computer is structural
cn=organizational-Unit is structural

The systemAuxiliaryClass attribute of each class lists any auxiliary classes applied directly to a class. The script in Listing 5.48 reads the systemAuxiliaryClass attribute of several classes:

  1. Specify the On Error Resume Next statement, and set the E_ADS_PROPERTY_NOT_FOUND constant to determine later in the script whether the systemAuxiliaryClass is empty.

  2. Initialize a variable with an array containing the common names of the classes used in the previous task.

  3. Use rootDSE to determine the value of the schemaNamingContext attribute.

  4. Use a For Each statement to bind to each class in the schema container, using the GetObject function and the LDAP provider.

  5. Initialize the arrSystemAuxiliaryClass variable with the systemAuxiliaryClass attribute (lines 15 and 16).

    • If initializing the variable returns the error code equal to E_ADS_PROPERTY_NOT_FOUND, echo to the command window that no system auxiliary classes are applied directly to the class.

    • Otherwise, use a For Each statement to read and display a list of auxiliary classes assigned directly to the class.

Listing 5.48 Reading the systemAuxiliaryClass Attribute of Several Classes

  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
On Error Resume Next
Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D
arrClassNames = Array _
 ("cn=top","cn=mail-Recipient", "cn=security-Principal", _
 "cn=person", "cn=Organizational-Person", _
 "cn=contact", "cn=user", "cn=computer", "cn=organizational-Unit")
Set objRootDSE = GetObject("LDAP://rootDSE")
For Each ClassName in arrClassNames
 Set objSchemaClass = GetObject("LDAP://" & ClassName & "," & _
 objRootDSE.Get("schemaNamingContext"))
 arrSystemAuxiliaryClass = _
 objSchemaClass.GetEx("systemAuxiliaryClass")
 If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then
 Wscript.Echo "No auxiliary classes" & _
 " assigned to " & ClassName
 Err.Clear
 Else
 Wscript.Echo "Auxiliary classes in " & ClassName & ":"
 For Each strAuxiliaryClass in arrSystemAuxiliaryClass
 Wscript.Echo vbTab & strAuxiliaryClass
 Next
 Wscript.Echo
 End If
Next

When this script runs it echoes a message indicating that the mailRecipient auxiliary class is applied to the contact and user classes and that the securityPrincipal auxiliary class is also applied to the user class, as shown in the following list:

No auxiliary classes assigned to cn=top.
No auxiliary classes assigned to cn=mail-Recipient.
No auxiliary classes assigned to cn=security-Principal.
No auxiliary classes assigned to cn=person.
No auxiliary classes assigned to cn=Organizational-Person.
Auxiliary classes in cn=contact:
        mailRecipient
Auxiliary classes in cn=user:
        securityPrincipal
        mailRecipient
No auxiliary classes assigned to cn=computer.
No auxiliary classes assigned to cn=organizational-Unit.

Understanding class inheritance will help you write search scripts that return the expected set of objects. For example, if you perform a search for all objects whose objectClass attribute contains user, you might be surprised to find that objects instantiated from the computer class are returned in the result set. The computer class inherits the attributes of the user class and all other classes above it in the hierarchy. Therefore, you have to modify the search to exclude computer objects from the result set. For more information about searching using ADSI, see "Searching Active Directory" earlier in this chapter.

You can use a script to view class relationships. The subClassOf attribute shows the parent class from which a class is derived. The steps for reading an attribute are similar to the preceding two listings, so scripting steps are not included here. Listing 5.49 shows how to determine the parent class of the Computer class.

Listing 5.49 Reading the subClassOf Attribute of a Class

  
1
2
3
4
5
6
7
8
9
strClassName = "cn=computer"
Set objRootDSE = GetObject("LDAP://rootDSE")
Set objSchemaClass = GetObject("LDAP://" & strClassName & "," & _
 objRootDSE.Get("schemaNamingContext"))
 
strSubClassOf = objSchemaClass.Get("subClassOf")
Wscript.Echo "The " & strClassName & _
 " class is a child of the " & strSubClassOf & " class."

When this script runs, it echoes a message indicating that the user class is the parent of the computer class, as shown:

The cn=computer class is a child of the user class.

Snap-ins For Viewing and Configuring User Account Attributes, Classes, and Objects

As demonstrated in the preceding section, scripts can be used to view information about classes and attributes. You can also examine all of the classes and attributes in the schema using the MMC Active Directory Schema snap-in or the ADSI Edit snap-in. Knowing how to use these tools will make the process of writing scripts easier to achieve. The Active Directory Schema snap-in provides a graphical interface to the classes and attributes in the schema. The ADSI Edit snap-in provides a graphical interface to all objects in Active Directory, including the schema.

Active Directory Schema snap-in

The command window tree in the Active Directory Schema Snap-in provides two lists:

  • Class list, which contains all the classes in an Active Directory schema

  • Attributes list, which contains all the attributes in the schema

From the Class list, you can learn which attributes are contained in a particular class and the hierarchical relationships of the class, such as the parent class, auxiliary classes, and possible superiors of the class. The attributes of a class include information such as whether the attribute is mandatory or optional and the lDAPDisplayName of the attribute. The lDAPDisplayName is the name you will use to identify most attributes in an ADSI script.

From the Attribute list, you can learn about the characteristics of an attribute, such as whether an attribute is replicated to the Global Catalog, whether it is single or multivalued, and whether the attribute is a Unicode string (string), integer, or other data type. Knowing the characteristics of an attribute is critical to managing the objects that contain the attributes derived from the associated class.

To load the Active Directory Schema snap-in

  1. From a command prompt, type regsvr32 schmmgmt to register the snap-in.

    This step must be completed only once on each computer where the snap-in will be loaded.

  2. Start MMC by typing MMC at the command prompt or from the Run menu.

  3. From the File menu, click Add/Remove Snap-in, and then click Add.

  4. From the Add Standalone Snap-in dialog box, click Active Directory Schema, and then click Add.

ADSI Edit snap-in

The ADSI Edit snap-in lets you view information about the objects created in Active Directory and lets you view the schema. However, many find the presentation of the schema provided by the Active Directory Schema Snap-in easier to use.

After loading the ADSI Edit snap-in, you can choose where in Active Directory to connect. The default connection points are:

  • LDAP:// domain_controller_name /Domain. This view provides information about the objects in the domain. Use this view to read information about attributes that are or can be assigned to Active Directory objects in the domain.

  • LDAP:// domain_controller_name /Configuration. This view provides information about the objects that are used to construct Active Directory, such as the sites and services of the domain.

  • LDAP:// domain_controller_name /RootDSE. This view provides information about the attributes contained in the RootDSE object. For information about how to use this object, see "Root Directory Services Entry" earlier in this chapter.

  • LDAP:// domain_controller_name /Schema. This view provides information about the objects and classes in the Active Directory schema.

Note

  • You can also establish a connection with the Global Catalog from this snap-in and view information about the attributes replicated to the Global Catalog.

To load the ADSI Edit snap-in and connect to the domain

  1. Install the Support tools (SupTools.msi) from the Windows 2000 Server family installation CD-ROM.

    The Support Tools are located in the Support\Tools folder on the installation CD-ROM.

  2. Start MMC by typing MMC at the command prompt or from the Run menu.

  3. From the File menu, click Add/Remove Snap-in, and then click Add.

  4. From the Add Standalone Snap-in dialog box, click ADSI Edit, and then click Add.

  5. In the command window tree, right-click the ADSI Edit item, and then click Connect to.

  6. Accept the defaults appearing in the Connection Settings dialog box to connect to the domain, and then click OK.