141 4.19.2 Solution 4.19.2.1 Using a graphical user interface 1. Open ADSI Edit 2. If an entry for the naming context you want to browse is not already displayed, do the following: 3. Right-click on ADSI Edit in the right pane and click Connect to . . . 4. Fill in the information for the naming context, container, or OU that contains the object you want to rename. Click on the Advanced button if you need to enter alternate credentials. 5. In the left pane, browse to the container or OU that contains the object you want to modify. Once you've found the object, right-click on it and select Rename. 6. Enter the new name and click OK. 4.19.2.2 Using a command-line interface > dsmove "<ObjectDN>" -newname "<NewName>" 4.19.2.3 Using VBScript ' This code renames an object and leaves it in the same location. ' SCRIPT CONFIGURATION strCurrentParentDN = "<CurrentParentDN>" strObjectOldName = "cn=<OldName>" strObjectNewName = "cn=<NewName>" ' END CONFIGURATION set objCont = GetObject("LDAP://" & strCurrentParentDN) objCont.MoveHere "LDAP://" & strObjectOldName & "," & _ strCurrentParentDN, strObjectNewName 4.19.3 Discussion Before you rename an object, ensure no applications reference it by name. You can make objects rename-safe by requiring all applications that must store a reference to objects to use the GUID of the object, not the name. The GUID (stored in the objectGUID attribute) is guaranteed to be unique and does not change when an object is renamed. 4.19.3.1 Using a graphical user interface If the parent container of the object you want to rename has a lot of objects in it, you may want to add a new connection entry for the DN of the object you want to rename. This may save you time searching through the list of objects in the container. You can do this by right-clicking ADSI Edit and selecting Connect to. Under Connection Point, select Distinguished Name and enter the DN of the object you want to rename. 4.19.3.2 Using a command-line interface 142 The two parameters that are needed to rename an object are the original DN of the object and the new RDN (-newname). The -s option can also be used to specify a server name to work against. 4.19.3.3 Using VBScript The MoveHere method can be tricky to use, so an explanation of how to use it to rename objects is in order. First, you need to call GetObject on the parent container of the object you want to rename. Then call MoveHere on the parent container object and specify the ADsPath of the object to rename as the first parameter. The new RDN including prefix (e.g., cn=) of the object should be the second parameter. 4.19.4 See Also MSDN: IADsContainer::MoveHere Recipe 4.20 Deleting an Object 4.20.1 Problem You want to delete an object. 4.20.2 Solution 4.20.2.1 Using a graphical user interface 1. Open ADSI Edit. 2. If an entry for the naming context you want to browse is not already displayed, do the following: a. Right-click on ADSI Edit in the right pane and click Connect to . . . b. Fill in the information for the naming context, container, or OU that contains the object you want to delete. Click on the Advanced button if you need to enter alternate credentials. 3. In the left pane, browse to the object you want to delete. 4. Right-click on the object and select Delete. 5. Click Yes to confirm. 4.20.2.2 Using a command-line interface > dsrm "<ObjectDN>" 4.20.2.3 Using VBScript strObjectDN = "<ObjectDN>" set objUser = GetObject("LDAP://" & strObjectDN) objUser.DeleteObject(0) 143 4.20.3 Discussion This recipe covers deleting individual objects. If you want to delete a container or OU and all the objects in it, take a look at Recipe 4.21 . 4.20.3.1 Using a graphical user interface If the parent container of the object you want to delete has a lot of objects in it, you may want to add a new connection entry for the DN of the object you want to delete. This may save you time searching through the list of objects in the container and could help avoid accidental deletions. You can do this by right-clicking ADSI Edit and selecting Connect to. Under Connection Point, select Distinguished Name and enter the DN of the object you want to delete. 4.20.3.2 Using a command-line interface The dsrm utility can be used to delete any type of object (no limitations based on object type as with dsadd and dsmod). The only required parameter is the DN of the object to delete. You can also specify -noprompt to keep it from asking for confirmation before deleting. The -s parameter can be used as well to specify a specific server to target. 4.20.3.3 Using VBScript Using the DeleteObject method is straightforward. Passing 0 as a parameter is required, but does not have any significance at present. An alternate and perhaps safer way to delete objects is to use the IADsContainer::Delete method. To use this method, you must first bind to the parent container of the object. You can then call Delete by passing the object class and RDN of the object you want to delete. Here is an example for deleting a user object: set objCont = GetObject("LDAP://ou=Sales,dc=rallencorp,dc=com") objCont.Delete "user", "cn=rallen" Delete is safer than DeleteObject because you have to be more explicit about what you are deleting. With DeleteObject you only need to specify a distinguished name and it will delete it. If you happen to mis-type the DN or the user input to a web page that uses this method is mis- typed, the result could be disastrous. 4.20.4 See Also Recipe 4.21 for deleting a container, MS KB 258310 (Viewing Deleted Objects in Active Directory), MSDN: IADsContainer::Delete, and MSDN: IADsDeleteOps::DeleteObject 144 Recipe 4.21 Deleting a Container That Has Child Objects 4.21.1 Problem You want to delete a container or organizational unit and all child objects contained within. 4.21.2 Solution 4.21.2.1 Using a graphical user interface Open ADSI Edit and follow the same steps as in Recipe 4.20. The only difference is that you'll be prompted to confirm twice instead of once before the deletion occurs. 4.21.2.2 Using a command-line interface > dsrm "<ObjectDN>" -subtree 4.21.2.3 Using VBScript The same code from Recipe 4.20 will also delete containers and objects contained within them. 4.21.3 Discussion As you can see from the solutions, there is not much difference between deleting a leaf node versus deleting a container that has child objects. However, there is a distinction in what is happening in the background. Deleting an object that has no children can be done with a simple LDAP delete operation. On the other hand, to delete a container and its children, the tree-delete LDAP control has to be used. If you were to do the deletion from an LDAP-based tool like LDP, you would first need to enable the "Subtree Delete" control, which has an OID of 1.2.840.113556.1.4.805. LDP provides another option to do a "Recursive Delete" from the client side. That will essentially iterate through all the objects in the container, deleting them one by one. The Subtree Delete is much more efficient, especially when dealing with large containers. 4.21.4 See Also Recipe 4.20 for deleting objects and MSDN: IADsDeleteOps::DeleteObject 145 Recipe 4.22 Viewing the Created and Last Modified Timestamp of an Object 4.22.1 Problem You want to determine when an object was either created or last updated. 4.22.2 Solution 4.22.2.1 Using a graphical user interface 1. Follow the steps in Recipe 4.2. 2. Ensure that createTimestamp and modifyTimestamp are included in the list of attributes to be returned by looking at Attributes under Options Search. 4.22.2.2 Using a command-line interface > dsquery * "<ObjectDN>" -attr name createTimestamp modifyTimestamp 4.22.2.3 Using VBScript ' This code prints the created and last modified timestamp ' for the specified object. ' SCRIPT CONFIGURATION strObjectDN = "<ObjectDN>" ' END CONFIGURATION set objEntry = GetObject("LDAP://" & strObjectDN) Wscript.Echo "Object Name: " & objEntry.Get("name") Wscript.Echo " Created: " & objEntry.Get("createTimestamp") Wscript.Echo " Changed: " & objEntry.Get("modifyTimestamp") 4.22.3 Discussion When an object is created or modified in Active Directory, the createTimestamp and modifyTimestamp attributes get set with the current time. Those two attributes are replicated, so assuming the latest modification of the object in question has replicated to all domain controllers, they will contain the absolute create and last modified timestamps. You may have also run across the whenCreated and whenChanged attributes. They also contain create and modify timestamps, but these values are local to the domain controller and are not replicated. 4.22.4 See Also Recipe 4.2 for viewing the attributes of an object 146 Recipe 4.23 Modifying the Default LDAP Query Policy 4.23.1 Problem You want to view or modify the default LDAP query policy of a forest. The query policy contains settings that restrict search behavior, such as the maximum number of entries that can be returned from a search. 4.23.2 Solution 4.23.2.1 Using a graphical user interface 1. Open ADSI Edit. 2. In the Configuration partition, browse to Services Windows NT Directory Service Query Policies. 3. In the left pane, click on the Query Policies container, then right-click on the Default Query Policy object in the right pane, and select Properties. 4. Double-click on the lDAPAdminLimits attribute. 5. Click on the attribute you want to modify and click Remove. 6. Modify the value in the Value to add box and click Add. 7. Click OK twice. 4.23.2.2 Using a command-line interface To view the current settings, use the following command: > ntdsutil "ldap pol" conn "con to server <DomainControllerName>" q "show values" To change the MaxPageSize value to 2000, you can do the following: > ntdsutil "ldap pol" conn "con to server <DomainControllerName>" q ldap policy: set MaxPageSize to 2000 ldap policy: Commit Changes 4.23.2.3 Using VBScript ' This code modifies a setting of the default query policy for a forest ' SCRIPT CONFIGURATION pol_attr = "MaxPageSize" ' Set to the name of the setting you want to modify new_value = 1000 ' Set to the value of the setting you want modify ' END CONFIGURATION Const ADS_PROPERTY_APPEND = 3 Const ADS_PROPERTY_DELETE = 4 set rootDSE = GetObject("LDAP://RootDSE") set ldapPol = GetObject("LDAP://cn=Default Query Policy,cn=Query-Policies," & _ "cn=Directory Service,cn=Windows NT,cn=Services," & _ 147 rootDSE.Get("configurationNamingContext") ) set regex = new regexp regex.IgnoreCase = true regex.Pattern = pol_attr & "=" for Each prop In ldapPol.GetEx("ldapAdminLimits") if regex.Test(prop) then if prop = pol_attr & "=" & new_value then WScript.Echo pol_attr & " already equal to " & new_value else ldapPol.PutEx ADS_PROPERTY_APPEND, "lDAPAdminLimits", _ Array( pol_attr & "=" & new_value ) ldapPol.SetInfo ldapPol.PutEx ADS_PROPERTY_DELETE, "lDAPAdminLimits", Array(prop) ldapPol.SetInfo WScript.Echo "Set " & pol_attr & " to " & new_value end if Exit For end if next 4.23.3 Discussion The LDAP query policy contains several settings that control how domain controllers handle searches. By default, one query policy is defined for all domain controllers in a forest, but you can create additional ones and apply them to a specific domain controller or even at the site level (so that all domain controllers in the site use that policy). Query policies are stored in the Configuration NC as queryPolicy objects. The default query policy is located at: cn=Default Query Policy, cn=Query-Policies, cn=Directory Service, cn=Windows NT, cn=Services, <ConfigurationPartitionDN>. The lDAPAdminLimits attribute of a queryPolicy object is multivalued and contains each setting for the policy in name-value pairs. Table 4-4 contains the available settings. Table 4-4. LDAP query policy settings Name Default value Description MaxPoolThreads 4 per proc Maximum number of threads that are created by the DC for query execution. MaxDatagramRecv 4096 Maximum number of datagrams that can be simultaneously processed by the DC. MaxReceiveBuffer 10485760 Maximum size in bytes for an LDAP request that the server will attempt to process. If the server receives a request that is larger then this value, it will close the connection. InitRecvTimeout 120 secs Initial receive time-out. 148 Table 4-4. LDAP query policy settings Name Default value Description MaxConnections 5000 Maximum number of open connections. MaxConnIdleTime 900 secs Maximum amount of time a connection can be idle. MaxActiveQueries 20 Maximum number of queries that can be active at one time. MaxPageSize 1000 Maximum page size that is supported for LDAP responses. MaxQueryDuration 120 secs Maximum length of time the domain controller can execute a query. MaxTempTableSize 10000 Maximum size of temporary storage that is allocated to execute queries. MaxResultSetSize 262144 Maximum size of the LDAP Result Set. MaxNotificationPerConn 5 Maximum number of notifications that a client can request for a given connection. Since the settings are stored as name/value pairs inside a single attribute, also referred to as AVAs, the VBScript solution has to iterate over each value and use a regular expression to determine when the target setting has been found. It does this by matching <SettingName>= at the beginning of the string. See Recipe 4.16 for more on AVAs. You should not change the default query policy in production unless you've done plenty of testing. Changing some of the settings may result in unexpected application or domain controller behavior. Instead of modifying the default LDAP query policy, you can create a new one. In the Query Policies container (where the default query policy object is located), create a new queryPolicy object and set the lDAPAdminLimits attribute as just described based on the settings you want configured. Then modify the queryPolicyObject attribute on the nTDSDSA object of a domain controller you want to apply the new policy to. This can be done via the Active Directory Sites and Services snap-in by browsing to the nTDSDSA object of a domain controller (cn=NTDS Settings), right-clicking on it, and selecting Properties. You can then select the new policy from a drop-down menu beside Query Policy. Click OK to apply the new policy. 4.23.4 See Also MS KB 315071 (HOW TO: View and Set Lightweight Directory Access Protocol Policies by Using Ntdsutil.exe in Windows 2000) 149 Recipe 4.24 Exporting Objects to an LDIF File 4.24.1 Problem You want to export objects to an LDAP Data Interchange Format (LDIF) file. 4.24.2 Solution 4.24.2.1 Using a graphical user interface None of the standard Microsoft tools support exporting LDIF from a GUI. 4.24.2.2 Using a command-line interface > ldifde -f output.ldf -l <AttrList> -p <Scope> -r "<Filter>" -d "<BaseDN>" 4.24.2.3 Using VBScript There are no COM or VBScript-based interfaces to LDIF. With Perl you can use the Net::LDAP::LDIF module, which supports reading and writing LDIF files. 4.24.3 Discussion The LDIF specification defined in RFC 2849 describes a well-defined file-based format for representing directory entries. The format is intended to be both human and machine parseable, which adds to its usefulness. LDIF is the de facto standard for importing and exporting a large number of objects in a directory and is supported by virtually every directory vendor including Microsoft. 4.24.3.1 Using a command-line interface The - f switch specifies the name of the file to use to save the entries to, -s is the DC to query, -l is the comma-separated list of attributes to include, -p is the search scope, -r is the search filter, and - d is the base DN. If you encounter any problems using ldifde, the -v switch enables verbose mode and can help identify problems. 4.24.4 See Also Recipe 4.25 for importing objects using LDIF, RFC 2849 (The LDAP Data Interchange Format (LDIF)—Technical Specification), and MS KB 237677 (Using LDIFDE to Import and Export Directory Objects to Active Directory) 150 Recipe 4.25 Importing Objects Using an LDIF File 4.25.1 Problem You want to import objects into Active Directory using an LDIF file. The file could contain object additions, modifications, and/or deletions. 4.25.2 Solution 4.25.2.1 Using a command-line interface To import objects using the ldifde utility, you must first create an LDIF file with the objects to add, modify, or delete. Here is an example LDIF file that adds a user, modifies the user twice, and then deletes the user: dn: cn=jsmith,cn=users,dc=rallencorp,dc=com changetype: add objectClass: user samaccountname: jsmith sn: JSmith useraccountcontrol: 512 dn: cn=jsmith,cn=users,dc=rallencorp,dc=com changetype: modify add: givenName givenName: Jim - replace: sn sn: Smith - dn: cn=jsmith,cn=users,dc=rallencorp,dc=com changetype: delete Once you've created the LDIF file, you just need to run the ldifde command to import the new objects. > ldifde -i -f input.ldf 4.25.3 Discussion For more information on the LDIF format, check RFC 2849. 4.25.3.1 Using a command-line interface To import with ldifde, simply specify the -i switch to turn on import mode and -f <filename> for the file. It can also be beneficial to use the -v switch to turn on verbose mode to get more information in case of errors. . file-based format for representing directory entries. The format is intended to be both human and machine parseable, which adds to its usefulness. LDIF is the de facto standard for importing. asking for confirmation before deleting. The -s parameter can be used as well to specify a specific server to target. 4.20.3.3 Using VBScript Using the DeleteObject method is straightforward 4.25 for importing objects using LDIF, RFC 2849 (The LDAP Data Interchange Format (LDIF)—Technical Specification), and MS KB 237677 (Using LDIFDE to Import and Export Directory Objects to Active