Creating user accounts as we've done previously is fine for an introduction, but typically you'll need to set many more attributes to make them usable in your environment. The approaches you use to create fully featured users in the NT and Active Directory environments differ slightly; Active Directory offers considerably more properties than NT, such as the office and home addresses of users, as well as lists of email addresses and pager, fax, and phone numbers.
You can manipulate User objects with a special interface called IADsUser. IADsUser's methods and property methods let you directly set many of the User object's property values. Table 21-1 through Table 21-3 contain the methods, read-write property methods, and read-only property methods, respectively, for the IADsUser interface. The corresponding Active Directory attribute is included in parentheses for the property methods that can be set with the LDAP provider.
Method |
Description |
---|---|
IADsUser::ChangePassword |
Changes the existing password. |
IADsUser::SetPassword |
Sets a new password without needing the old one. |
IADsUser::Groups |
Gets a list of groups of which the user is a member. You can use the IADsMembers interface to iterate through the list. |
Property method |
Available with WinNT or LDAP? |
---|---|
IADsUser::AccountDisabled |
WinNT, LDAP (userAccountControl mask) |
IADsUser::AccountExpirationDate |
WinNT, LDAP (accountExpires) |
IADsUser::Department |
LDAP (department) |
IADsUser::Description |
WinNT, LDAP (description) |
IADsUser::Division |
LDAP (division) |
IADsUser::EmailAddress |
LDAP (mail) |
IADsUser::EmployeeID |
LDAP (employeeID) |
IADsUser::FaxNumber |
LDAP (facsimileTelephoneNumber) |
IADsUser::FirstName |
LDAP (givenName) |
IADsUser::FullName |
WinNT, LDAP (displayName) |
IADsUser::GraceLoginsAllowed |
Neither |
IADsUser::GraceLoginsRemaining |
Neither |
IADsUser::HomeDirectory |
WinNT, LDAP (homeDirectory) |
IADsUser::HomePage |
LDAP (wWWHomePage) |
IADsUser::IsAccountLocked |
WinNT, LDAP (userAccountControl) |
IADsUser::Languages |
LDAP (languages) |
IADsUser::LastName |
LDAP (sn) |
IADsUser::LoginHours |
WinNT, LDAP (logonHours) |
IADsUser::LoginScript |
WinNT, LDAP (scriptPath) |
IADsUser::LoginWorkstations |
WinNT, LDAP (userWorkstations) |
IADsUser::Manager |
LDAP (manager) |
IADsUser::MaxLogins |
WinNT |
IADsUser::MaxStorage |
WinNT, LDAP (maxStorage) |
IADsUser::NamePrefix |
LDAP (personalTitle) |
IADsUser::NameSuffix |
LDAP (generationQualifier) |
IADsUser::OfficeLocations |
LDAP (physicalDeliveryOfficeName) |
IADsUser::OtherName |
LDAP (middleName) |
IADsUser::PasswordExpirationDate |
WinNT |
IADsUser::PasswordMinimumLength |
WinNT |
IADsUser::PasswordRequired |
WinNT, LDAP (userAccountControl mask) |
IADsUser::Picture |
LDAP (thumbNailPhoto) |
IADsUser::PostalAddresses |
LDAP (postalAddress) |
IADsUser::PostalCodes |
LDAP (postalCode) |
IADsUser::Profile |
WinNT, LDAP (profilePath) |
IADsUser::RequireUniquePassword |
WinNT |
IADsUser::SeeAlso |
LDAP (seeAlso) |
IADsUser::TelephoneHome |
LDAP (homePhone) |
IADsUser::TelephoneMobile |
LDAP (mobile) |
IADsUser::TelephoneNumber |
LDAP (telephoneNumber) |
IADsUser::TelephonePager |
LDAP (pager) |
IADsUser::Title |
LDAP (title) |
Property method |
Available with WinNT or LDAP? |
---|---|
IADsUser::BadLoginAddress |
Neither |
IADsUser::BadLoginCount |
WinNT, LDAP (badPwdCount) |
IADsUser::LastFailedLogin |
LDAP (badPasswordTime) |
IADsUser::LastLogin |
WinNT, LDAP (lastLogin) |
IADsUser::LastLogoff |
WinNT, LDAP (lastLogoff) |
IADsUser::PasswordLastChanged |
LDAP (pwdLastSet) |
For more information on IADsUser, check out the following location in the MSDN Library (http://msdn.microsoft.com/library/): Networking and Directory Services Active Directory, ADSI and Directory Services SDK Documentation Directory Services Active Directory Service Interfaces Active Directory Service Interfaces Reference ADSI Interfaces Persistent Object Interfaces IADsUser.
Now let's apply some of this knowledge to two examples. The first shows how to create a fully featured user in Windows NT, and the second shows how to create a fully featured user in Active Directory.
Example 21-1 uses several IADsUser property methods and several constant values to create a fully featured user in NT.
Option Explicit '********************************************************************** 'Flag constants. See the later sidebar on "Boolean Arithmetic with 'Hexadecimal Values." '********************************************************************** Const UF_SCRIPT = &H1 Const UF_ACCOUNTDISABLE = &H2 Const UF_LOCKOUT = &H10 Const UF_PASSWD_NOTREQD = &H20 Const UF_PASSWORD_CANT_CHANGE = &H40 Const UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = &H80 Const UF_DONT_EXPIRE_PASSWD = &H10000 Dim objDomain, objUser, fso, intUserFlags, intNewUserFlags Dim fldUserHomedir, wshShell Set objDomain = GetObject("WinNT://MYDOMAIN") Set objUser = objDomain.Create("user","vlaunders") '********************************************************************** 'Write the newly created object out from the property cache and read 'all the properties for the object, including the ones set by the 'system on creation '********************************************************************** objUser.SetInfo objUser.GetInfo '********************************************************************** 'Set the properties '********************************************************************** objUser.AccountDisabled = False objUser.AccountExpirationDate = "02/05/04" objUser.Description = "My description goes here!" objUser.FullName = "Victoria Launders" objUser.IsAccountLocked = False objUser.LoginScript = "login.vbs" objUser.PasswordRequired = True '********************************************************************** 'Set all the properties for the user and read back the data, including 'any default so that you can set the flags '********************************************************************** objUser.SetInfo objUser.GetInfo '********************************************************************** 'Make sure the password never expires and the user can't change it '********************************************************************** intUserFlags = objUser.Get("userFlags") intNewUserFlags = intUserFlags Or UF_DONT_EXPIRE_PASSWD intNewUserFlags = intNewUserFlags Or UF_PASSWORD_CANT_CHANGE objUser.Put "userFlags", intNewUserFlags objUser.SetInfo '********************************************************************** 'Set the password '********************************************************************** objUser.SetPassword "thepassword"
Most of the code in the script is self-explanatory, except for making sure the password never expires. We used two hexadecimal constants to explicitly force the new user account to have a password that never expires and that the user can't change. The code to set these password requirements might seem complicated, but it involves simple arithmetic; the sidebar "Boolean Arithmetic with Hexadecimal Values" explains this arithmetic. If you prefer not to use hex constants, you might be able to use a User object property method. For example, you can use the IADsUser::AccountDisabled property method instead of the UF_ACCOUNTDISABLE constant to disable an account. Similarly, you can use the IADsUser::IsAccountLocked property method instead of the UF_LOCKOUT constant to lock an account. These IADs property methods hide the arithmetic within a simple Boolean value.
Boolean Arithmetic with Hexadecimal ValuesAssume that you want an attribute of an object (e.g., userFlags of the User object) to set 8 values. You use an 8-bit binary number to represent those 8 values. If you want the attribute to hold 11 values, you use an 11-bit binary number. The binary system is a base-2 system in which 0 typically represents a false condition and 1 typically represents a true condition. In this example, 0 means the value isn't set, and 1 means the value is set. If you want to set only the third and eighth values of an 8-value attribute, you set the third and eighth bits of an 8-bit binary number to 1, or &B10000100. (You read binary numbers from right to left.) The prefix &B specifies that the number is binary. However, attributes store data as decimal values. Thus, you need to convert the binary number into a decimal value, which is base-10. For example, the binary number &B10000100 translates into:
You use the Boolean AND operator to check whether a bit is set and the OR operator to set a bit. For example, suppose you want to see whether the fourth bit is set in an 8-bit binary number that has a decimal value of 132. You can check for the existence of this bit using the AND operator to compare the number to a binary mask indicating that the fourth bit is set. The equation to do this is:
You solve this equation by resolving the AND operation for each bit individually. For example, the first bit in &B10000100 is 0, and the first bit in &B00001000 is 0: 0 AND 0 is 0. The second bit in &B10000100 is 0, and the second bit in &B00001000 is 0: 0 AND is 0. The third bit in &B10000100 is 1, and the third bit in &B00001000 is 0; 1 AND 0 is 0. When you calculate all eight bits, the result is &B00000000. In other words, the fourth bit isn't set. Suppose you want to test whether the third bit is set:
Because the third bit in &B10000100 is 1, and the third bit in &B0000100 is 1, the resulting bit is 1 (1 AND 1 is 1), which specifies that the value for the third bit is set. Let's translate this binary equation into decimal and hex equations:
If the return value is 0 or &H0, the bit isn't set. If the return value is the bit's actual value (in this case, 4 or &H4), the bit is set.
Just like the AND operator, the OR operator works with binary, decimal, and hex systems. Taking the example just given, let's try to set the third bit, which happens to be already set:
In other words, the result is the new value with that bit set. Because that bit was already set, nothing changes. Let's try setting the fourth bit, which isn't already set:
The result includes a newly set fourth bit. You can even set two bits at once. For example, here's how you set the fourth and fifth bits:
Although the Boolean mathematics is straightforward, luckily you don't have to include this code in a script. Instead, you typically use constants. For example, if you declare the constant: Const UF_DONT_EXPIRE_PASSWD = &H10000 you just need to specify that constant in the script. To determine this bit's existence, use the code: If intUserFlags And UF_DONT_EXPIRE_PASSWD = 0 Then 'UF_DONT_EXPIRE_PASSWD is not set Else 'UF_DONT_EXPIRE_PASSWD is set End If You set bits in a similar fashion. For example, to set the &H10000 bit, use the code: intUserFlags = intUserFlags Or UF_DONT_EXPIRE_PASSWD |
Example 21-2 shows how to create a fully featured user in Active Directory. This script is similar to the last one, with a couple of major differences. The property name userFlags changes to userAccountControl for the extended settings. Home directory attributes are set along with creation of the home directory folder if it doesn't exist. Other minor differences exist, such as the use of more constants and property methods. Active Directory lets you set many property values for users, including multivalue properties that you set via an array. For example, you can list several telephone numbers for the TelephoneNumber, TelephoneMobile, and TelephoneHome properties. Through the use of constants, you can even set up Active Directory to let users log on with smart cards.
Option Explicit '********************************************************************** 'WshShell::Run constants '********************************************************************** Const vbMinimizedNoFocus = 6 '********************************************************************** 'Flag constants. See the later sidebar on "Boolean Arithmetic with 'Hexadecimal Values." '********************************************************************** Const UF_SCRIPT = &H1 Const UF_ACCOUNTDISABLE = &H2 Const UF_HOMEDIR_REQUIRED = &H8 Const UF_LOCKOUT = &H10 Const UF_PASSWD_NOTREQD = &H20 Const UF_PASSWORD_CANT_CHANGE = &H40 Const UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = &H80 Const UF_DONT_EXPIRE_PASSWD = &H10000 Const UF_MNS_LOGON_ACCOUNT = &H20000 Const UF_SMARTCARD_REQUIRED = &H40000 Const UF_TRUSTED_FOR_DELEGATION = &H80000 Const UF_NOT_DELEGATED = &H100000 Const ADS_PROPERTY_UPDATE = 2 Dim objDomain, objUser, fso, intUserFlags, intNewUserFlags Dim fldUserHomedir, wshShell Set objDomain = GetObject("LDAP://cn=Users,dc=mycorp,dc=com") Set objUser = objDomain.Create("user","cn=vlaunders") objUser.Put "sAMAccountName", "vlaunders" objUser.Put "userPrincipalName", "vlaunders@mycorp.com" '********************************************************************** 'Write the newly created object out from the property cache and read 'all the properties for the object, including the ones set by the 'system on creation '********************************************************************** objUser.SetInfo objUser.GetInfo '********************************************************************** 'Set the properties '********************************************************************** objUser.AccountDisabled = False objUser.AccountExpirationDate = "02/05/01" objUser.Description = "My description goes here!" objUser.IsAccountLocked = False objUser.LoginScript = "login.vbs" objUser.Profile = "\\MYDOMAIN\DFS\Users\vlaunders\profile" objUser.PasswordRequired = True objUser.TelephoneHome = Array("0123-555-7890") objUser.PutEx ADS_PROPERTY_UPDATE, "otherHomePhone", _ Array("0123 555 7891", "0123 555 7892") objUser.TelephoneNumber = Array("0123 555 7890") objUser.PutEx ADS_PROPERTY_UPDATE, "otherTelephone", _ Array("0123 555 7891", "0123 555 7892") objUser.TelephoneMobile = Array("0123 555 7890") objUser.PutEx ADS_PROPERTY_UPDATE, "otherMobile", _ Array("0123 555 7891", "0123 555 7892") objUser.NamePrefix = "Ms." objUser.FirstName = "Victoria" objUser.LastName = "Launders" objUser.DisplayName = "Victoria Launders" '********************************************************************** 'Set the drive that you'll map to '********************************************************************** objUser.HomeDirectory = "\\MYDOMAIN\DFS\Users\vlaunders" objUser.Put "homeDrive", "Z:" '********************************************************************** 'Set all the properties for the user and read back the data, including 'any defaults, so that you can set the flags '********************************************************************** objUser.SetInfo objUser.GetInfo '********************************************************************** 'Make sure the password never expires and the user can't change it '********************************************************************** intUserFlags = objUser.Get("userAccountControl") intNewUserFlags = intUserFlags Or UF_DONT_EXPIRE_PASSWD intNewUserFlags = intNewUserFlags Or UF_PASSWORD_CANT_CHANGE objUser.Put "userAccountControl", intNewUserFlags objUser.SetInfo '********************************************************************** 'Create the home directory '********************************************************************** Set fso = CreateObject("Scripting.FileSystemObject") If Not fso.FolderExists("\\MYDOMAIN\DFS\Users\vlaunders") Then Set fldUserHomedir = fso.CreateFolder("\\MYDOMAIN\DFS\Users\vlaunders") End If '********************************************************************** 'Set full rights for the user to the home directory '********************************************************************** Set wshShell = WScript.CreateObject("Wscript.Shell") wshShell.Run "cacls.exe \\MYDOMAIN\DFS\Users\vlaunders /e /g vlaunders:F", vbMinimizedNoFocus, True '********************************************************************** 'Set the password '********************************************************************** objUser.SetPassword "thepassword"
We created the home directory by obtaining a reference to a FileSystemObject object and calling the FileSystemObject::CreateFolder method if the directory doesn't already exist. The permissions were set by running the cacls.exe command available from the Resource Kit using the WshShell::Run method. When calling WshShell::Run, you need to include three parameters. The first parameter is the command you want to execute; the second parameter can be any of the following constant values that describe how you want to treat the new window produced by executing the command:
Const vbHide = 0 ` hides the window Const vbNormalFocus = 1 ` displays the window Const vbMinimizedFocus = 2 ` minimizes the window with focus Const vbMaximizedFocus = 3 ` maximizes the window with focus Const vbNormalNoFocus = 4 ` displays the window w/o focus Const vbMinimizedNoFocus = 6 ` minimizes the window w/o focus
The last parameter to the WshShell::Run method should be to set to true if you want the script to wait until CACLS finishes before continuing to the next line.
|