331 objAttr.SetInfo WScript.Echo "New ANR attribute: " & strAttrName The CLI and VBScript solutions assume that searchFlags wasn't previously set and just blindly overwrites whatever value is present if one was. Check out Recipe 4.12 for a better solution that will enable the bit you want without overwriting any previous settings. 10.13.3 Discussion ANR is an efficient search algorithm that allows for a complex search filter to be written using a single comparison. For example, a search for (anr=Jim Smith) would translate into the following query: • An OR filter with every attribute in the ANR set against Jim Smith* • A filter for givenName = Jim* and sn = Smith* • A filter for givenName = Smith* and sn = Jim* These filters are ORed together and then processed by Active Directory. Since all default ANR attributes are also indexed, the query return should come back quickly. Here is a list of the default attributes that are included as part of ANR searches. The LDAP display name of the attribute is shown first with the common name in parenthesis. • displayName (Display-Name) • givenName (Given-Name) • legacyExchangeDN (Legacy-Exchange-DN) • msDS-AdditionalSamAccountName (ms-DS-Additional-Sam-Account-Name) • physicalDeliveryOfficeName (Physical-Delivery-Office-Name) • name (RDN) • sAMAccountName (SAM-Account-Name) • sn (Surname) msDS-AdditionalSamAccountName was added as an ANR attribute in Windows Server 2003. It is important to make sure that any new ANR attributes are also indexed. ANR searches are intended to be very fast, and if a non-indexed attribute was added to the set, it could dramatically impact the performance of the searches. You can find which attributes are included in the ANR set by using the following search criteria: Base cn=Schema,cn=Configuration,<ForestRootDN> Filter 332 (&(objectcategory=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:= 4)) Scope onelevel Alternatively, to find attributes that aren't included in ANR, change the previous search filter to the following: (&(objectcategory=attributeSchema)(!(searchFlags:1.2.840.113556.1.4.803:=4))) 10.13.4 See Also Recipe 4.12 for modifying a bit-flag attribute, Recipe 10.7 for adding a new attribute, MS KB 243299 (Ambiguous Name Resolution for LDAP in Windows 2000), and MS KB 243311 (Setting an Attribute's searchFlags Property to Be Indexed for ANR) Recipe 10.14 Adding or Removing an Attribute in the Global Catalog 10.14.1 Problem You want to add or remove an attribute in the global catalog. 10.14.2 Solution For Windows 2000 Active Directory, you need to enable schema modifications before proceeding. See Recipe 10.2 for more information. 10.14.2.1 Using a graphical user interface 1. Open the Active Directory Schema snap-in. 2. In the left pane, click on the Attributes folder. 3. In the right pane, double-click the attribute you want to edit. 4. Check the box beside Replicate this attribute to the Global Catalog to add to the global catalog, or uncheck to remove the global catalog. 5. Click OK. 10.14.2.2 Using a command-line interface You can add an attribute to the global catalog by using the ldifde utility and an LDIF file that contains the following: dn: cn=<AttrCommonName>,cn=schema,cn=configuration,<ForestRootDN> changetype: modify replace: isMemberOfPartialAttributeSet isMemberOfPartialAttributeSet: TRUE 333 - If the LDIF file were named add_gc_attr.ldf, you would run the following command: > ldifde -v -i -f add_gc_attr.ldf 10.14.2.3 Using VBScript ' This code adds an attribute to the global catalog ' SCRIPT CONFIGURATION ' Set to the common name (not LDAP display dame) of the attribute. strAttrName = "<AttrCommonName>" ' e.g. surname ' Set to TRUE to add to GC, set to FALSE to remove from GC boolAddtoGC = TRUE ' END CONFIGURATION set objRootDSE = GetObject("LDAP://RootDSE") set objAttr = GetObject("LDAP://cn=" & strAttrName & "," & _ objRootDSE.Get("schemaNamingContext")) objAttr.Put "isMemberOfPartialAttributeSet", boolAddtoGC objAttr.SetInfo WScript.Echo "Added attribute to GC: " & strAttrName 10.14.3 Discussion Each domain controller in a forest replicates a copy of the Domain naming context for its own domain as well as copies of the forest-wide Configuration and Schema partitions. However, domain controllers do not replicate Domain naming contexts for other domains in the forest. When enabled as a global catalog server, a domain controller will replicate partial, read-only replicas of all the objects in other domains in the forest. Searching against the global catalog is useful when you need to perform a single search across several naming contexts at once. The global catalog stores only a subset of each object's attributes, which is why it is considered a partial replica. Attributes stored in the global catalog are considered part of the partial attribute list (PAS). The attributes that are part of the PAS should be either ones you'd want to use as part of searches against the global catalog, or ones you would want returned after searching the global catalog. You can add attributes that are stored in the global catalog by setting the isMemberOfPartitalAttributeSet attribute of an attributeSchema object to TRUE. Likewise, to remove an attribute from the partial attribute set, you need to set isMemberOfPartitalAttributeSet to FALSE for the target attribute. With Windows 2000, anytime you added an attribute to the partial attribute set, a full sync of all of the global catalog contents was done for every global catalog server. This could have a major impact on replication in some multidomain environments, as the amount of data that needs to replicate across your forest could be significant. Fortunately, this limitation was removed in Windows Server 2003 so that a full sync is no longer 334 performed. Removing an attribute from the partial attribute list does not force a global catalog sync, even under Windows 2000. You can find which attributes are included in the global catalog by using a query with the following criteria: Base cn=Schema,cn=Configuration,<ForestRootDN> Filter (&(objectcategory=attributeSchema)(isMemberOfPartitalAttributeSet=TRUE)) Scope onelevel Alternatively, to find attributes that aren't in the global catalog, you only need to change part of the previous filter to the following: (isMemberOfPartialAttributeSet=FALSE) 10.14.4 See Also MS KB 229662 (How to Control What Data Is Stored in the Global Catalog), MS KB 230663 (HOW TO: Enumerate Attributes Replicated to the Global Catalog), MS KB 232517 (Global Catalog Attributes and Replication Properties), MS KB 248717 (How to Modify Attributes That Replicate to the Global Catalog), MS KB 257203 (Common Default Attributes Set for Active Directory and Global Catalog), and MS KB 313992 (HOW TO: Add an Attribute to the Global Catalog in Windows 2000) Recipe 10.15 Finding the Nonreplicated and Constructed Attributes 10.15.1 Problem You want to find the attributes are not replicated or are constructed by Active Directory. 10.15.2 Solution 10.15.2.1 Using a graphical user interface 1. Open LDP. 2. From the menu, select Connection Connect. 3. For Server, enter the name of a domain controller (or leave blank to do a serverless bind). 4. For Port, enter 389. 5. Click OK. 6. From the menu, select Connection Bind. 7. Enter credentials of a domain user. 335 8. Click OK. 9. From the menu, select Browse Search. 10. For BaseDN, type the Schema Container DN (e.g., cn=schema,cn=configuration,dc=rallencorp,dc=com). 11. For Scope, select One Level. 12. To find nonreplicated attributes, use the following for Filter: (&(objectcategory=attributeSchema)(systemFlags:1.2.840.113556.1.4.803:= 1)) 13. To find constructed attributes, use the following for Filter: (&(objectcategory=attributeSchema)(systemFlags:1.2.840.113556.1.4.803:= 4)) 14. Click Run. 10.15.2.2 Using a command-line interface To find the nonreplicated attributes, use the following command: > dsquery * cn=schema,cn=configuration,<ForestRootDN> -scope onelevel -attr "cn"[RETURN] -filter "(&(objectcategory=attributeSchema)(systemFlags:1.2.840.113556.1.4.803:=1))" To find the constructed attributes, use the following command: > dsquery * cn=schema,cn=configuration,<ForestRootDN> -scope onelevel -attr "cn"[RETURN] -filter "(&(objectcategory=attributeSchema)(systemFlags:1.2.840.113556.1.4.803:=4))" 10.15.2.3 Using VBScript ' This script will print out the nonreplicated and constructed attributes set objRootDSE = GetObject("LDAP://RootDSE") strBase = "<LDAP://" & objRootDSE.Get("SchemaNamingContext") & ">;" strFilter = "(&(objectcategory=attributeSchema)" _ & "(systemFlags:1.2.840.113556.1.4.803:=1));" strAttrs = "cn;" strScope = "onelevel" set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADsDSOObject" objConn.Open "Active Directory Provider" set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope) objRS.MoveFirst WScript.Echo "Nonreplicated attributes: " while Not objRS.EOF Wscript.Echo " " & objRS.Fields(0).Value objRS.MoveNext 336 wend strFilter = "(&(objectcategory=attributeSchema) " _ & "(systemFlags:1.2.840.113556.1.4.803:=4));" set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope) objRS.MoveFirst WScript.Echo "" WScript.Echo "Constructed attributes: " while Not objRS.EOF Wscript.Echo " " & objRS.Fields(0).Value objRS.MoveNext wend 10.15.3 Discussion The systemFlags attribute of attributeSchema objects defines a few special attribute properties, including whether an attribute is not replicated between domain controllers and whether it is dynamically constructed by Active Directory. Most attributes are replicated after they are updated on an object, but some never replicate between domain controllers. These attributes are considered nonreplicated. An example of a nonreplicated attribute you may be familiar with is the lastLogon attribute that stores the last logon timestamp for user and computer objects. Whenever a user or computer logs in to Active Directory, the authenticating domain controller updates the user or computer's lastLogin attribute, but the update does not get replicated out to other domain controllers. Constructed attributes are automatically maintained by Active Directory and cannot be set manually. A good example of a constructed attribute is the new msDS-Approx-Immed- Subordinates that is available in Windows Server 2003. That attribute contains the approximate number of child objects within a container. Obviously this attribute wouldn't be of much value if you had to maintain it, so Active Directory does it automatically. One of the downsides to constructed attributes is that you cannot search against them. For example, I cannot perform a search to find all containers that have more than 10 objects in them (i.e., msDS-Approx-Immed-Subordinates>10). This would return an operations error. Constructed attributes can only be returned as part of the attribute set for a query and not used as part of the query itself. To find the nonreplicated or constructed attributes you have to use a bitwise LDAP filter against attributeSchema objects. A bit value of 1 indicates the attribute is non-replicated and a value of 4 indicates the attribute is constructed. 10.15.4 See Also Recipe 4.9 for searching with a bitwise filter 337 Recipe 10.16 Finding the Linked Attributes 10.16.1 Problem You want to find attributes that are linked. 10.16.2 Solution 10.16.2.1 Using a graphical user interface 1. Open LDP. 2. From the menu, select Connection Connect. 3. For Server, enter the name of a domain controller (or leave blank to do a serverless bind). 4. For Port, enter 389. 5. Click OK. 6. From the menu, select Connection Bind. 7. Enter credentials of a domain user. 8. Click OK. 9. From the menu, select Browse Search. 10. For BaseDN, type the Schema container DN (e.g., cn=schema,cn=configuration,dc=rallencorp,dc=com). 11. For Scope, select One Level. 12. To find linked attributes, use the following for Filter: (&(objectcategory=attributeSchema)(linkid=*)) 13. Click Run. 10.16.2.2 Using a command-line interface > dsquery * cn=schema,cn=configuration,<ForestRootDN> -scope onelevel - filter[RETURN] "(&(objectcategory=attributeSchema)(linkid=*))" -attr cn linkID 10.16.2.3 Using VBScript ' This code prints out all of the attributes that are linked ' and their corresponding linkID values set objRootDSE = GetObject("LDAP://RootDSE") strBase = "<LDAP://" & objRootDSE.Get("SchemaNamingContext") & ">;" strFilter = "(&(objectcategory=attributeSchema)(linkid=*));" strAttrs = "cn,linkid;" strScope = "onelevel" set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADsDSOObject" objConn.Open "Active Directory Provider" set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope) objRS.MoveFirst while Not objRS.EOF Wscript.Echo objRS.Fields(1).Value & " : " & objRS.Fields(0).Value 338 objRS.MoveNext wend 10.16.3 Discussion The values of some attributes in Active Directory are linked. For example, if you set the manager attribute on one user object to be the DN of a second user object, the reports attribute on the second user object will automatically contain the first user object's DN. In this example, the manager attribute, or the attribute that gets set, is considered the forward link and the reports attribute, or the attribute that automatically gets calculated, is called the back link. Another common example is group membership. The member attribute of the group object represents the forward link, while the memberOf attribute of the corresponding object (e.g., user) represents the back link. You can identify which attributes are linked in the schema by searching for attributeSchema objects that have a linkID attribute that contains some value. The linkID value for a forward- link attribute will be an even, positive number. The corresponding back-link attribute will be the forward-linkID plus 1. For example, the manager attribute linkID is 42 and the back-link reports attribute has a linkID of 43. Recipe 10.17 Finding the Structural, Auxiliary, Abstract, and 88 Classes 10.17.1 Problem You want to list the structural, auxiliary, abstract, and 88 classes. 10.17.2 Solution 10.17.2.1 Using a graphical user interface 1. Open the Active Directory Schema snap-in. 2. In the left pane, click on the Classes folder. 3. In the right pane, the list of all the classes will be displayed. The Type column contains the type of class. Even though you can click on the column header, it currently does not sort the classes by type. 10.17.2.2 Using a command-line interface > dsquery * cn=schema,cn=configuration,<ForestRootDN> -limit 0 -scope onelevel[RETURN] -filter "(objectcategory=classSchema)" -attr lDAPDisplayName objectclasscategory 10.17.2.3 Using VBScript ' This code prints out classes of a particular type ' SCRIPT CONFIGURATION 339 ' Set the following to TRUE or FALSE depending if you want to ' view or not view classes of the type defined by the variable boolShowStructural = TRUE boolShowAuxiliary = TRUE boolShowAbstract = TRUE boolShow88 = TRUE ' END CONFIGURATION set objRootDSE = GetObject("LDAP://RootDSE") set objSchemaCont = GetObject("LDAP://cn=schema," & _ objRootDSE.Get("configurationNamingContext")) objSchemaCont.Filter = Array("classSchema") WScript.Echo "Loading classes, this will take a few seconds." for each objClass in objSchemaCont WScript.StdOut.Write(".") if objClass.Get("objectClassCategory") = 0 then str88 = str88 & vbTab & objClass.Get("lDAPDisplayName") & vbCrlf elseif objClass.Get("objectClassCategory") = 1 then strStruct = strStruct & vbTab & _ objClass.Get("lDAPDisplayName") & vbCrlf elseif objClass.Get("objectClassCategory") = 2 then strAbst = strAbst & vbTab & objClass.Get("lDAPDisplayName") & vbCrlf elseif objClass.Get("objectClassCategory") = 3 then strAux = strAux & vbTab & objClass.Get("lDAPDisplayName") & vbCrlf else WScript.Echo "Unknown class type: " & _ objClass.Get("lDAPDisplayName") & vbCrlf end if next WScript.Echo vbCrlf if boolShowStructural = TRUE then WScript.Echo "Structural Classes: " WScript.Echo strStruct WScript.Echo end if if boolShowAbstract = TRUE then WScript.Echo "Abstract Classes: " WScript.Echo strAbst WScript.Echo end if if boolShowAuxiliary = TRUE then WScript.Echo "Auxiliary Classes: " WScript.Echo strAux WScript.Echo end if if boolShow88 = TRUE then WScript.Echo "88 Classes: " WScript.Echo str88 WScript.Echo end if 340 10.17.3 Discussion There are four supported class types in the Active Directory schema. The class type is defined by the objectClassCategory attribute on classSchema objects. Each class type is used for a different purpose relating to organizing and inheriting classes. Table 10-6 describes each type. Table 10-6. Object class category values Name Value Description 88 0 Legacy class type defined by the original X.500 standards. It should not be used for new classes. Structural 1 Used for instantiating objects. Can be comprised of abstract, auxiliary, and other structural classes. Abstract 2 Used to define a high-level grouping of attributes that can be used as part of other abstract or structural class definitions. Objects cannot be instantiated using an abstract class. Auxiliary 3 Used as a collection of attributes that can be applied to other abstract, auxiliary, or structural classes. Recipe 10.18 Finding the Mandatory and Optional Attributes of a Class 10.18.1 Problem You want to view the mandatory and optional attributes of a class. 10.18.2 Solution 10.18.2.1 Using a graphical user interface 1. Open the Active Directory Schema snap-in. 2. In the left pane, click on the Classes folder. 3. In the right pane, double-click the class you want to view. 4. Click on the Attributes tab. 10.18.2.2 Using a command-line interface > dsquery * cn=<ClassCommonName>,cn=schema,cn=configuration,<ForestRootDN> - l[RETURN] -scope base -attr mayContain mustContain systemMayContain systemMustContain 10.18.2.3 Using VBScript ' This code displays the mandatory and optional attributes for a class. ' SCRIPT CONFIGURATION . 2000 Active Directory, you need to enable schema modifications before proceeding. See Recipe 10.2 for more information. 10.14.2.1 Using a graphical user interface 1. Open the Active Directory. removed in Windows Server 2003 so that a full sync is no longer 334 performed. Removing an attribute from the partial attribute list does not force a global catalog sync, even under Windows. contexts for other domains in the forest. When enabled as a global catalog server, a domain controller will replicate partial, read-only replicas of all the objects in other domains in the forest.