[ Team LiB ] Previous Section Next Section

21.2 Creating a Full-Featured User Account

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.

Table 21-1. IADsUser methods

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.

Table 21-2. IADsUser read-write property methods

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)

Table 21-3. IADsUser read-only property methods

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.

21.2.1 WinNT Provider

Example 21-1 uses several IADsUser property methods and several constant values to create a fully featured user in NT.

Example 21-1. Creating a full-featured user account in Windows 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 Values

Assume 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:

27 + 22 = 128 + 4 = 132

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:

&B10000100 AND &B00001000 = &B00000000

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:

&B10000100 AND &B0000100 = &B00000100

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:

&B10000100 AND &B0000100 = &B00000100
132 AND 4 = 4
&H84 AND &H4 = &H4

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.

132 OR 8 = 140
&H84 OR &H8 = &H8C

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:

&B10000100 OR &B0000100 = &B10000100
132 OR 4 = 132
&H84 OR &H4 = &H84

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:

&B10000100 OR &B00001000 = &B10001100

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:

&B10000100 OR &B00011000 = &B10011100
132 OR 24 = 156
&H84 OR &H18 = &H9C

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

21.2.2 LDAP Provider

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.

Example 21-2. Creating a full-featured user account in Active Directory
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", "[email protected]"

'**********************************************************************
'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.

As an alternative to using CACLS to set permissions, you could write a script that makes use of the interfaces described in Chapter 23, or you could use the ADsSecurity.dll provided available in the Platform SDK.

    [ Team LiB ] Previous Section Next Section