Many systems administrators in large organizations spend most of their time with user and group management tasks. Although managing groups is generally a simple task to accomplish with Microsoft-provided tools, creating user accounts isn't. Typically, issuing an account to an end user involves many steps. In addition to creating an object in the namespace, you often need to create a home directory and corresponding share, then apply permissions in the file system. In addition, you often must grant group membership to a new user account so that the user can immediately gain access to shared resources.
Such multistep operations provide you with the perfect opportunity to use Active Directory Service Interfaces (ADSI) to create a complete solution that not only reduces the tedium of such administrative tasks but also reduces the risk of errors in these operations.
To create a user account, you typically need to perform these operations:
- Create a User object in the namespace
- Assign a password to the user
- Create a home directory for the user in the file system
- Set file-system permissions on the user's home directory
- Share the user's home directory
- Assign the user to additional security groups
Creating the User Object
To begin, you must create a User object in the namespace. For Windows NT domains and standalone machines, you always use the WinNT: ADSI service provider to create User objects in the SAM. For Windows 2000 domains, the general rule is to use the LDAP: ADSI service provider to create User objects in Active Directory (AD). This rule has two caveats:
- You can use the WinNT: provider instead of the LDAP: provider if you don't use any organizational units (OUs) in your directory and you define all the users in the default CN=Users container in the directory's root. Using the WinNT: provider in this situation enables backward compatibility with existing NT scripts. However, if you're not concerned with backward compatibility, the LDAP: provider is the best choice because it lets you take advantage of Lightweight Directory Access Protocol (LDAP)-specific properties, such as Manager, TelephoneNumber, or OfficeLocations.
- You can use the WinNT: provider instead of the LDAP: provider if the User object you want to create will exist in the SAM of an NT domain or local computer (i.e., a Win2K/NT standalone server or workstation) instead of in AD.
Let's look at how you can create a User object both in the SAM and AD.
Creating a User object in the SAM. The code in Listing 1 uses the WinNT: provider to create an object in NT's SAM (i.e., an NT domain or a Win2K/NT member server or workstation). Listing 1 begins by declaring and setting the variables. You need to customize the code at callout A in Listing
- You must replace
- Target_Container_Name with the name of the domain (for domain accounts) in which to create the account or the NetBIOS name of a local machine (for accounts on standalone machines) on which you want to create the account. An example of a NetBIOS name is Server_01. Optionally, you can add the resource domain for the server (e.g., Resource_Domain/Server_01) for faster binding.
- Name_For_New_Object with the name of your new object (e.g., teck).
- New_Password with the user's password (e.g., 1UsW2bT82!).
Next, you use the IADsContainer::GetObject method to bind to the target container. You establish the binding by concatenating the WinNT: provider (WinNT://) and the TargetContainer variable, which contains the domain or NetBIOS name.
After you bind to the container, you use the IADsContainer::Create method to create the User object in the cache. To write the object from the cache to the directory, you use the IADs::SetInfo method. Before you call this method, though, you can use the IADsUser interface to set any properties (e.g., FullName, Description) you want the object to have. Setting the optional properties before calling IADs::SetInfo conserves network bandwidth because the system writes to the directory only once. (For detailed information about IADsContainer, IADs, and IADsUser, see Alistair G. Lowe-Norris, "An ADSI Primer, Part 2: Manipulating Active Directory Objects," February 1999, and "An ADSI Primer, Part 6: Using ADSI to Create and Manipulate User Accounts," June 1999.)
You can test the success of the object-creation operation by examining the value of Err.Number. A value of 0 means that IADs::SetInfo successfully created the User object, in which case you use the IADsUser::SetPassword method to assign the password to that object.
Creating a User object in AD. The code in Listing 2, page 13, uses the LDAP: provider to create an object in Win2K's AD. Like Listing 1, Listing 2 begins by declaring and setting the variables. Similarly, you need to customize the code at callout A in Listing 2. You must replace
- Path_To_Container_in_AD with the path to the container in which you want to create the User object (e.g., ou=corporate_users,dc=nwtraders,dc=com)
- Name_For_New_Object with the user's common name (CN—e.g., Thomas Eck)
- Legacy_Name_For_Object with the name that represents the user to NT clients and servers (e.g., TEck)
- New_Password with the user's password (e.g., 1UsW2bT82!)
Next, you bind to the container in which you want to create the new User object by concatenating the LDAP: provider (LDAP://) and the TargetContainer variable, which contains the path to that container. After binding to the container, you create the User object. However, unlike the WinNT: provider, the LDAP: provider requires that you specify the property type (in this case, CN=) and the user's CN (which the ObjectCommonName variable holds) when calling the IADsContainer::Create method. In addition to setting the mandatory CN property, you need to set the mandatory sAMAccountName property before calling IADs::SetInfo. If you omit either property, the system gives you an error message rather than creating the object. In addition to specifying the two mandatory properties, you can specify any optional properties you might want before you call IADs::SetInfo.
Like Listing 1, Listing 2 ends by examining Err.Number to determine the success of the object-creation operation. If the operation was successful, the code sets the password.
Assigning a Password
In Listing 1 and Listing 2, you might have noticed that the code didn't call IADs::SetInfo after setting the user's password. This call isn't necessary because the IADsUser::SetPassword and IADsUser::ChangePassword methods are immediate operations that don't require the use of IADs::SetInfo to write the information to the directory.
For that reason, the LDAP: provider doesn't let you set or change a User object's password until it exists in the directory. However, oddly enough, the WinNT: provider lets you call IADsUser::SetPassword and IADsUser::ChangePassword before you use IADs::SetInfo to create the object in the directory. To keep your code consistent, I recommend that you always call IADs::SetInfo before you attempt to set or change a password for a newly created User object.
If you want, you can incorporate the RandomPwd function I discussed last month to assign a random password to the InitialPassword variable in Listing 1 and Listing 2. Callout B in Listing 1 and Listing 2 includes instructions about how to adapt the code.
With service accounts, you don't need to know the password that the RandomPwd function assigns to the account. With user accounts, however, you need to capture the generated random password so that you can let the user know it for the first logon. If you want to automate this process, you can use Collaboration Data Objects (CDO) to mail the account information to the user's functional manager.
Creating the Directory
ADSI doesn't natively support the creation of directories in the file system. However, you can use the Microsoft Scripting Run-Time Library's FileSystemObject object to create the user's home directory. In the Code Library on the Windows Scripting Solutions Web site (http://www.winscriptingsolutions.com), you can find code that uses the FileSystemObject object to create a directory in the file system.
The code uses the FileSystemObject::CreateFolder method to create the directory. In this code, you need to specify the path to the directory you want to create. However, be aware that the CreateFolder method can create only one directory level at a time. Thus, a call to create a new subdirectory below an existing directory will work, but you can't create two or more levels in one call. Instead, you must create the top-level directory, then create any additional subdirectories in separate calls.
Setting the Permissions
In its original distribution, ADSI doesn't provide an interface for managing NTFS or registry permissions. Recognizing the tremendous value that permission management provides, Microsoft made the ADsSecurity.dll available in the ADSI software development kit (SDK). You can download the ADSI SDK from http://www.microsoft.com/ntserver/nts/downloads/other/adsi25/sdk.asp. After you've downloaded and unzipped the file locally, open a command prompt and use the Regsvr32 utility to register ADsSecurity.dll.
With these steps completed, you can set the file-system permissions on the user's home directory. In the Code Library on the Windows Scripting Solutions Web site, you can find code that sets these permissions. The code grants Change rights to the user. Only NTFS supports permissions in the file system, so this code won't work on FAT or FAT32 drives. (If you're unfamiliar with using ADSI to set permissions, see Alistair G. Lowe-Norris, "An ADSI Primer, Part 10: Permissions and Auditing Basics," October 1999.)
Listing 3 contains the lines you need to customize in the permissions code. You need to replace
- Path_To_Share with the local path to the user's home directory (e.g., D:\HomeDirectories\TEck)
- Domain\Account with the name of the user to which you're granting permission, following the format Domain\Username (e.g., Domain_01\TEck)
Unfortunately, ADSI doesn't support modifying permissions on existing inherited folders and files, so it's important to ensure that the directory is empty before applying the permissions. If the directory isn't empty, you can contact me for information about how to enumerate and apply permissions to existing files in a directory.
Sharing the Directory
With the user's home directory created and the proper permissions assigned, you can use the IADsContainer::Create method to create a share, as Listing 4 shows. In the code at callout A in Listing 4, you need to replace
- Target_Computer_NetBIOS_Name with the NetBIOS name of the computer on which you created the home directory (e.g., Server_01)
- Share_Name with the name of the share (e.g., TEck$)
- Path_To_Share with the path to the share (e.g., D:\homedirs\teck)
The code at callout B in Listing 4 permits an unlimited number of connections to the home directory share. If you want to limit the number of connections, you can change -1 to the number of connections you want.
The default ADSI distribution doesn't let you manage permissions on shares. However, this inability typically isn't a problem because best practice dictates that you should leave the share permissions as Everyone Full Control and instead use the underlying NTFS permissions to control and manage access.
Assigning More Groups
With the User object and home directory created, you can modify security group membership to grant the new user permission to access various enterprise applications. In the Code Library on the Windows Scripting Solutions Web site, you can find code that uses the IADsGroup::Add method to assign the user to security groups in both the SAM and AD.
The Excel Tool
I've created an application that performs the user-administration tasks I've discussed here. You can download this application from the Code Library on the Windows Scripting Solutions Web site and customize it to match your organization's needs. To view the application's code, open the application in Microsoft Excel 2000, then press Alt+F11 to access the Visual Basic Editor. To use the Excel application, follow the directions in the workbook's Instructions sheet.
Automate Multiple Tasks
Using ADSI and any scripting language, you can easily create a workflow application that shadows the manual administrative processes currently in place for creating a new user account. After you automate your user-account creation process, you can integrate your code with a personnel database (such as PeopleSoft) to automatically create a basic user account when you add a new entry to the database.
Next month, I'll take a short diversion from ADSI to answer one of the most commonly asked questions I receive: How can I create a function that lets me programmatically administer the profile path attributes in a Win2K Server Terminal Services; NT Server 4.0, Terminal Server Edition (WTS); or Citrix WinFrame or MetaFrame environment?