System Administration

Overview

Traditionally, administrators have had to resort to system-specific application programming interfaces (APIs) to perform system maintenance—for example, manipulating network resources, user accounts, or mailboxes. Windows provides Win32 APIs to manipulate such resources. These interfaces are generally difficult to use and not directly accessible to scripting environments such as Windows Script Host (WSH).

The introduction of Active Directory Services Interface (ADSI) makes performing administrative tasks in the Windows environment much more accessible as well as easier.

ADSI on its own doesn't perform any system administrative tasks, such as creating a user account, but it provides an interface to directory services. A directory service exposes access to system administrative tasks through directory providers. A few directory providers are listed in Table 14-1.

Table 14-1: Directory Providers

PROVIDER

DESCRIPTION AND AVAILABILITY

WinNT

Windows NT 4.0 and later. Performs user account, group, domain, service, file share, and print queue operations.

LDAP

Exchange server and Windows 2000. Performs user account and Active Directory administration under Windows 2000 and Exchange server administration, such as creating mailboxes and distribution lists.

IIS

Internet Information Server. Performs site creation, manipulation, and maintenance.

NWCOMPAT

Novell services.

Each provider, such as WinNT or LDAP, exposes a namespace. A namespace exposes the objects in the provider. A reference to the object must always start with the namespace it resides in, such as WinNT:// for the Windows NT provider, LDAP:// for Active Directory and Exchange administration, or IIS:// for IIS servers.

Though ADSI is used to perform Active Directory operations in a Windows 2000/XP environment, having it installed does not mean you are using Active Directory. Installing ADSI on Windows NT 4.0 or Windows 9x/ME does not provide them with Active Directory capabilities.

The WinNT provider is installed on Windows NT 4.0 and Windows 2000 computers. There are operations and information that cannot be performed through LDAP and Active Directory on Windows 2000 that can be accessed through the WinNT provider.

  Note 

For more information, refer to the following resources: "ADSI Overview" (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmind99/html/cutting0599.asp), "An Introduction to ADSI" (http://www.asptoday.com/articles/19990310.htm),"Manage Directory Resources with Active Directory Services Interface" (http://ntmag.com/Articles/Index.cfm?ArticleID=258), Download page for ADSI (http://www.microsoft.com/ntserver/nts/downloads/other/Adsi25/default.asp),"Exploring the NT Directory Services" (http://www.asptoday.com/articles/19990806.htm), LabMice.net's Active Directory Resources (http://www.labmice.net/ActiveDirectory/default.htm), and the Active Directory newsgroup at news://microsoft.public.activedirectory.interfaces.

Setting Domain Properties

Problem

You want to set the password length for a Windows NT domain.

Solution

You can reference the domain using the WinNT ADSI provider and then set the required properties:

Dim objDomain 'get a reference to the Acme domain Set objDomain = GetObject("WinNT://ACME") objDomain. MinPasswordLength = 6 objDomain.SetInfo

Discussion

The WinNT provider domain object exposes account policy information, such as password policies.

Use the GetObject function to bind to an NT domain object:

Set objDomain = GetObject("WinNT://DomainName")

DomainName represents the name of the Windows NT domain you are binding to. The WinNT provider name is case-sensitive, but object paths are not.

Once you have a bound to a domain object, you can set any of the properties listed in Table 14-2. These properties correspond to the Account Policies option under Windows NT User Manager for NT 4.0.

Table 14-2: WinNT Provider Domain Object Properties

PROPERTY

DESCRIPTION

MinPasswordLength

Minimum password length required for user. If 0, then no minimum password length enforced.

MinPasswordAge

Minimum time a password must be used before it can be changed. Stored in seconds. If 0, a user can change his or her password immediately.

MaxPasswordAge

The maximum time a password can be used before it expires and must be changed. Stored in seconds. If 0, the password never expires.

MaxBadPasswordsAllowed

Number of bad passwords that can be entered before the account is locked out.

PasswordHistoryLength

Number of unique passwords a user must change before reusing a password. The maximum value is 24 passwords. If 0, no password history is kept.

AutoUnlockInterval

If the number of bad passwords a user enters exceeds the value set by MaxBadPasswordsAllowed, the user is "locked out" for the duration defined by AutoUnlockInterval. Stored in seconds. If 0, the user account must be unlocked by an administrator.

LockoutObservationInterval

Time that will elapse before the bad password counter is reset. Stored in seconds.

When any properties have been changed, you must invoke the domain object's SetInfo method for any of the changes to take effect.

To get a reference to a Windows 2000 domain object using ADSI, you must reference the domain's distinguished name (DN) using the Lightweight Directory Access Protocol (LDAP) provider:

Set objDomain = GetObject("LDAP://distinguishedname")

A DN is a path to an object, and it is composed of the relative distinguished name (RDN) separated by commas. The RDN is used to identify objects and is composed of the attribute ID (object type) followed by the object's name property. In the case of dc=Acme, the attribute is dc and the value is Acme.

One way of thinking of a DN is as an Internet domain name. For example, microsoft.com is a valid Internet DNS address. The DN for a Microsoft domain controller would be dc=microsoft,dc=com. The name of the highest level object is at the end of the string.

ADSI requires the LDAP:// prefix for the DN so it knows what providers to use. The LDAP must be uppercase, while the DN is not case-sensitive.

To get a reference to the ACME domain, use this code:

Set objDomain = GetObject("LDAP://dc=Acme,dc=com")

LDAP exposes a RootDSE directory service object. This object provides information about the directory services. To get the RootDSE, use the following line:

Set objRootDSE = GetObject("LDAP://RootDSE")

Using the RootDSE, you can automatically find the most available directory server to use. The RootDSE returns a defaultNameContext property, which identifies the name of the current domain:

Dim objRootDSE, objDomain, strDomain 'get a reference to the rootDSE Set objRootDSE = GetObject("LDAP://RootDSE") 'get the domain strDomain = objRootDSE.Get("defaultNamingContext") 'get a reference to the domain object Set objDomain = GetObject("LDAP://" & strDomain)

Windows 2000 implements the WinNT and LDAP providers, but only the WinNT provider is available to Windows NT 4.0. Windows NT 4.0 requires ADSI to be installed separately.

The properties exposed through the WinNT domain object, such as MinPasswordAge, are not available through the LDAP domain object, because Active Directory implements such settings differently using Group Policies.

Corresponding objects exposed by the different providers are not guaranteed to expose the same properties, so in the case of domain information for the LDAP and WinNT providers, the properties implemented are different.

See Also

Search the ADSI SDK for the topic "binding." For more information, read the MSDN Library articles "Binding String" (http://msdn.microsoft.com/library/en-us/netdir/adsi/winnt_binding_string.asp), "ADSI Scripting Tutorial, Binding" (http://msdn.microsoft.com/library/en-us/netdir/adsi/binding.asp), "Domains" (http://msdn.microsoft.com/library/en-us/netdir/adsi/domains.asp), and "Provider Support of ADSI Interfaces" (http://msdn.microsoft.com/library/en-us/netdir/adsi/provider_support_of_adsi_interfaces.asp).

Determining a Computer s OS

Problem

You want to determine a computer's OS.

Solution

You can reference an ADSI computer object using the WinNT provider and then reference the OperatingSystem property:

Set objComputer = GetObject("WinNT://odin,computer") strOS = objComputer.OperatingSystem

Discussion

The Computer object represents an individual computer on your network. To get a reference to a computer object:

Set objComputer = GetObject("WinNT://computername,computer")

computername represents the name assigned to the computer. The ,computer that follows the computer name is optional, but it speeds the resolving of the object.

Table 14-3 lists the properties associated with the Computer object. These properties are read-only.

Table 14-3: Computer Object Properties

PROPERTY

DESCRIPTION

Owner

Name of individual/organization the OS was registered to.

Division

Name of individual/company the OS was registered to.

OperatingSystem

Operating system name (e.g., Windows NT).

OperatingSystemVersion

OS version (e.g., 4.0, 5.0).

Processor

Processor description (e.g., x86 Family 6 Model 5 Stepping 2). Doesn't directly correspond to a chip model such as Pentium II.

ProcessorCount

Number of processors (if supported).

To get a reference to a computer using the LDAP provider and Active Directory, use the following line:

Set objComputer = GetObject("LDAP://ComputerName")

The computer object returned by the LDAP provider does not expose any of the properties the WinNT provider does; however, because Windows 2000 implements the WinNT provider as well as the LDAP provider, this doesn't matter.

See Also

For more information, read the MSDN Library articles "ADSI Scripting Tutorial, Binding" (http://msdn.microsoft.com/library/en-us/netdir/adsi/binding.asp) and "Provider Support of ADSI Interfaces" (http://msdn.microsoft.com/library/en-us/netdir/adsi/provider_support_of_adsi_interfaces.asp).

Listing Users

Problem

You want to list all users from a computer or domain.

Solution

You can bind to a WinNT domain or computer object and then set the Filter property, filtering on the User class:

Dim objDomain, objUser 'get a reference to a domain ojbect Set objDomain = GetObject("WinNT://Acme") 'filter on the user objects objDomain.Filter = Array("user") For Each objUser In objDomain Wscript.Echo objUser.Name Next

Discussion

You can use the Filter property to filter ADSI objects. The property takes an array of class names, which allows for multiple class values to be specified. In the following example, all users and groups for the Acme domain are counted:

Set objDomain = GetObject("WinNT://Acme") 'filter on the user and group objects objDomain.Filter = Array("user", "group") For Each obj In objDomain Wscript.Echo obj.Name Next

The class names specified in the array are not case-sensitive.

If the filter not is set, enumerating a computer or domain container object will return all objects in the container. This can be useful to determine all objects in that container.

'list all objects in the Acme domain and their type Set objDomain = GetObject("WinNT://Acme") 'loop through each object, listing the name and object class For Each obj In objDomain Wscript.Echo obj.Name, obj.Class Next

Filters can be set on Active Directory container objects in exactly the same way as WinNT:

'list all users in the Users container Set objCN = GetObject("LDAP://cn=Users,DC=acme,DC=com") objCN.Filter=Array("User") For Each obj In objCN Wscript.Echo obj.Name Next

When an Active Directory container is enumerated, no attempt is made to enumerate subcontainers. In the following example, a reference is made to the container for the domain object for Acme.com and the user class is enumerated:

'list all users in the Users container Set objCN = GetObject("LDAP://DC=acme,DC=com") objCN.Filter=Array("User") For Each obj In objCN Wscript.Echo obj.Name Next

In this example, you would assume that all users belonging to the Acme domain would be listed, but only user objects from the domain container (and usually there shouldn't be any) will be listed. None of the containers below the domain container, such as the Users container, will be searched. Solution 14.16 provides a method of querying Active Directory containers using ADO.

See Also

Solution 14.16. For more information, read the MSDN Library article "Listing Users" (http://msdn.microsoft.com/library/en-us/netdir/adsi/listing_users.asp).

Creating a New User

Problem

You want to create a new user.

Solution

Using the WinNT provider, get an instance of a domain or computer object where you want to create a new user and invoke the Create method:

'bind to a domain Set objDomain = GetObject("WinNT://ACME") 'create a new user - Fred Smith Set objUser = objDomain.Create("User", "FredS") objUser.SetPassword("iu12yt09") objUser.SetInfo

Discussion

To create a new user with the WinNT provider, you first need to reference the domain or computer object in which you want to create the user.

Then invoke the object's Create method, specifying the User class as the object you want to create. When creating a user, you must be logged on with the appropriate administrative security level to create and manipulate user objects. The syntax is as follows:

Set objUser = objContainer.Create("User", strUserName)

strUserName represents the user name that is used to log on. The objContainer object represents the domain or computer in which you want to create the user.

Once the user has been created, invoke the object's SetInfo method to save the changes. No additional properties need be set once a new user has been created using the WinNT provider.

The solution demonstrates the SetPassword method. The SetPassword method sets the password for the current user object:

objUser.SetPasswordstrPassword

The strPassword parameter is the password you want to set for the user.

Creating a user using the LDAP provider under Windows 2000 requires a similar procedure as using the WinNT provider:

'bind to the container to add user to. In this example the acme domain. Set objContainer = GetObject("LDAP://cn=Users,dc=acme,dc=com") Set objUser = objContainer.Create("User", "cn=Fred Smith ") objUser.Put "samAccountName", "freds" objUser.SetInfo objUser.pwdLastSet = -1 objUser.SetPassword "we12oi90" objUser.AccountDisabled = False objUser.SetInfo

First, a reference to a container is required. Under Windows NT all users were added to a single list, essentially a single large container. Active Directory allows additional containers to be created to organize users. The commonly used container to perform this task is the organizational unit.

Windows 2000/XP and Active Directory provides organizational units to organize objects into logical groupings. For example, users could be organized by departments, so you could create an organizational unit for accounting, finance, marketing, and so on.

To create an organizational unit, get a reference to the parent container, which is the container where the organizational unit will reside.

Then call the Create method, specifying organizationalUnit as the object class to be created followed by the organizational name in RDN format:

Set objCN = GetObject("LDAP://ou=Accounting,DC=Acme,DC=com") Set objOU = objCN.Create("organizationalUnit","ou=Accounting")

To get a reference to a given organizational unit, you must specify the organizational unit name together with full domain DN:

'get a reference to account organizational unit container Set objContainer = GetObject("LDAP://ou=accounting,dc=acme,dc=com")

Organizational units can be nested, allowing even greater organizational granularity:

Set objCN = GetObject("LDAP://ou=Accounting,DC=Acme,DC=com") Set objOU = objCN.Create("organizationalUnit","ou=Taxation")

Users and groups can be created in organizational units. Organizational units may seem like a different way of creating a user group, but they allow for control of administrative delegation. For example, a user in the accounting department could be granted access to administer users in the accounting organizational unit, but nowhere else.

The Users container is a built-in container Windows 2000/XP provides for Windows NT 4.0 user compatibility. If you did not specify a specific container in the user string, the user would have been created at the root of the Windows 2000/XP domain tree. This is a perfectly valid, but not very logical place, to put users.

The LDAP user name specified for the Create method is a bit more involved than the user name specified for the WinNT provider. In the previous code snippet, cn=Fred Smith indicates the user name is Fred Smith.

The following example gets a reference to the Accounting organizational unit (OU) container and adds a user to it:

Set objContainer = GetObject("LDAP://OU=Accounting,DC=acme,DC=com") Set objUser = objContainer.Create("User", "CN=Fred Smith") objUser.Put "samAccountName", "freds" objUser.SetInfo

The user name (Fred Smith) is not used to identify the person when logging on. The samAccountName property that is set after the creation of the user is used to identify the user when logging on, and it must be set when creating a new Active Directory user.

The samAccountName property represents the user ID that non-Windows 2000/XP clients (Windows 9x/ME, Windows NT) would use to log on. Windows 2000/XP uses this ID in NT domain or mixed-mode authentication, in a Windows 2000-only environment, Windows 2000 clients would use a user principal name (UPN).

A UPN user ID looks like an Internet e-mail address. If not set, the UPN is created using the samAccountName property followed by the at sign (@) and an Internet-style fully qualified domain name representing the organizational domain. So in the previous example, the UPN for Fred Smith would be freds@acme.com.

You can set an alternative UPN for a user by setting the userPrincipalName property:

Set objContainer = GetObject("LDAP://OU=Accounting,DC=acme,DC=com") Set objUser = objContainer.Create("User", "CN=Fred Smith") objUser.Put "samAccountName", "freds" objUser.Put "userPrincipalname", "fredsmith@acme.com" objUser.SetInfo

See Also

For more information, read the MSDN Library articles "Creating Local Users" (http://msdn.microsoft.com/library/en-us/netdir/adsi/creating_local_users.asp) and "ADSI Scripting Tutorial, Binding" (http://msdn.microsoft.com/library/en-us/netdir/adsi/binding.asp).

Listing Object Properties

Problem

You want to list properties that are associated with an object and their corresponding set values.

Solution

You can bind to any instance of an ADSI object and then reference the Schema property. Enumerate the MandatoryProperties and OptionalProperties collections.

The following command-line script takes the path to an ADSI object (for any ADSI provider) and enumerates the mandatory and optional properties for the object:

'listprop.vbs 'lists schema properties for specified object Dim objClass,varAttrib, aval, objObject, strLine On Error Resume Next If Wscript.Arguments.Count <> 1 Then ShowUsage Wscript.Quit End If 'get the object Set objObject = GetObject(Wscript.Arguments (0)) If Err Then Wscript.Echo "Unable to get object "& Wscript.Arguments (0) Wscript.Quit End If Set objClass = GetObject(objObject.Schema) Wscript.Echo "Mandatory Attributes: " For Each varAttrib In objClass.MandatoryProperties strLine = " "& varAttrib If IsArray(objObject.Get(varAttrib)) Then If Not Err Then For Each aval In objObject.Get(varAttrib) varAttrib = varAttrib & "," & aval Next End If Else strLine = strLine & " "& objObject.Get(varAttrib) End If If Err Then strLine = strLine & " No value" Err.Clear Wscript.Echo strLine Next Wscript.Echo "Optional Attributes: " For Each varAttrib In objClass.OptionalProperties strLine = " "& varAttrib objObject.GetInfoEx Array(varAttrib), 0 'check if object is an array If IsArray(objObject.Get(varAttrib)) Then If Not Err Then For Each aval In objObject.Get(varAttrib) strLine = strLine & "," & aval Next End If Else strLine = strLine & " "& objObject.Get(varAttrib) End If If Err Then strLine = strLine & " No value" Err.Clear Wscript.Echo strLine Next Sub ShowUsage WScript.Echo "listprop list properties for specified object" _ & vbCrLf & "Syntax:" & vbCrLf & _ "listprop.vbs objectpath" & vbCrLf & _ "objectpath Path to " & vbCrLf & _ "Example: List details about computer Odin" & vbCrLf & _ "listprop WinNT://Odin,computer" End Sub

Discussion

The tables throughout this chapter list a number of important properties related to the various ADSI objects. However, there are literally hundreds of properties that are not listed here. And as a result of the extensible nature of Active Directory, additional attribute properties may be added to an object.

The class schema determines what objects and properties are exposed through the object. For each object, there may be many more properties than the ones that can be set using management applications for the underlying directory store, such as User Manager for Windows NT 4.0.

To get a reference to the schema, reference the Schema property of any given ADSI object.

The Schema object contains two properties that list the properties associated with the object: MandatoryProperties and OptionalProperties.

The MandatoryProperties property is a collection that stores the properties required to make the object functional.

The OptionalProperties property is a collection that stores the optional properties. This might not be always totally accurate, because there may be optional properties that are required to be set before an object is made functional.

To list the associated properties, you must get a reference to an object's schema:

'get an ADSI object Set objClass = GetObject(strPath) 'get the schema for the object Set objClass = GetObject(objObject.Schema)

Once you have the schema, you can enumerate the MandatoryProperties and OptionalProperties collections. They contain the name of each property.

Enumerating the OptionalProperties and MandatoryProperties collections lists all properties associated with an object. If you just need to list properties that contain values, use the object's PropertyCount property, which returns the number of set properties for an object.

The PropertyCount property returns the count for all properties that have been loaded into the cache. To ensure it gives an accurate count of properties, call the GetInfo method before using PropertyCount. The following script lists all set properties for the user freds:

Set objUser = GetObject("WinNT://Acme/freds,user") objUser.GetInfo For nF = 0 To objUser.PropertyCount - 1 Wscript.Echo objUser.Item(nF).Name Next

The solution lists all properties for a given ADSI path:

listprop ADSIpath

ADSIpath is the path to any ADSI object, so use the following line to list all properties for the computer Odin using the WinNT provider:

listprop WinNT://Odin,computer

If the ADSI path contains spaces, it must be surrounded by double quotes:

listprop "LDAP://CN=Fred Smith,OU=Accounting,DC=Acme,DC=com"

With the listprop command-line utility, you can easily determine all properties associated with an object. One way of determining what property is associated with a field in a management interface (such as User Manager or the Active Directory snap-in under Windows 2000) is to fill in all of the fields with easily distinguishable values and then run listprop against the object.

See Also

For more information, read the MSDN Library article "Modifying User Properties" (http://msdn.microsoft.com/library/en-us/netdir/adsi/modifying_user_properties.asp).

Setting Object Properties

Problem

You want to set user properties.

Solution

You can use the dot or Put method to set values.

The following sample sets the name and description properties for an NT user:

'get a reference to user Set objUser = GetObject("WinNT://Acme/freds,user") 'set fullname and description properties objUser.FullName = "Fred Smith" objUser.Put "Description", "Accounting Manager" objUser.SetInfo

Discussion

To set a property, use the dot method:

objUser.Property = Value

Alternatively, you can use the Put method to assign properties:

objUser.Put strProperty, value

strProperty represents the property name and value is the value to assign to the property.

When an object is bound, it is cached locally. When a property is read or set, the property values for the object are loaded into the local cache. Changing a property value updates the local cached copy of the object but not the underlying directory object.

Use the SetInfo method to update the underlying directory store, such as Windows NT security database, Windows 2000 Active Directory, Exchange server, and so on. Any changes to properties do not take effect until SetInfo is invoked:

'get a reference to user Set objUser = GetObject("WinNT://Acme/freds,user") 'set fullname and description properties objUser.FullName = "Fred Smith" objUser.Put "Description", "Accounting Manager" objUser.SetInfo

If you are setting a large number of properties, you should call SetInfo once after all the properties have been set instead of calling it after every single property has been set to limit traffic to the underlying directory store.

To get an object's property, use either the Get or dot method:

'get a reference to user Set objUser = GetObject("WinNT://Acme/freds,user") 'read description using Get and dot method Wscript.Echo objUser.Description Wscript.Echo objUser.Get "Description"

Some properties do not automatically load into the ADSI cache, and when you reference a property it doesn't reflect any changes made by other uses since the object was created. The GetInfo method forces the reload of the cache:

objObject.GetInfo

Some properties fail to load even after using GetInfo and require the GetInfoEx method to read the information. The GetInfoEx method requires you to specify the properties you are attempting to reference:

objMailbox.GetInfoEx aProperties, nValue

The aProperties parameter is an array of property names you want to load. The nValue parameter is currently not used and must be set to 0.

Set objUser = GetObject("WinNT://Acme/freds,user") objUser.GetInfoEx Array("Description"), 0

Unfortunately, there are no rules as to what properties require the GetInfo or GetInfoEx methods to load information.

Table 14-4 lists properties available through the WinNT provider.

Table 14-4: Windows NT User Object Properties

PROPERTY

DESCRIPTION

AccountDisabled

Boolean. Determines if the user account is disabled.

AccountExpirationDate

Date the account will expire. To disable account expiration date, set the property to Empty.

BadPasswordAttempts

Number of unsuccessful logon attempts.

Description

Account description.

Fullname

Full name of the user.

HomeDirDrive

Directory letter to associate with the user's home directory (e.g., H:).

HomeDirectory

User's home directory. If set to network share using UNC format, the network home directory is assumed and the HomeDirDrive property is required to be assigned a directory. If set to a local directory (e.g., d:data), the HomeDirDrive setting is ignored.

IsAccountLocked

Boolean. If the account becomes locked out due to login failure, this flag is set to True. An account cannot be manually locked out by setting this value (the value cannot be set to True). To unlock an account, set the property to False.

LastLogin

Date and time of last login. Read-only.

LastLogoff

Date and time of last logoff. Read-only.

LoginHours

Array of bytes. Hours a user is allowed to log in. See Solution 14.9 for how to set hours.

LoginScript

Name of the logon script/batch file to execute upon logon.

LoginWorkstations

Array of string values. Determines what machine a user can log on to. See Solution 14.10 for how to set the LoginWorkstations property.

PasswordExpired

Indicates if the user's password has expired. If set to 1, the password is flagged as expired and the user must change his or her password at next logon. This is the same as the "User Must Change Password at Next logon" flag under in the Windows NT User Manager.

PasswordExpirationDate

Date and time of password expiration. Read-only, cannot be changed.

Profile

Path to the directory where profile information is stored.

UserFlags

Integer. Combination of bitwise OR-ed values that determine options such as account status and password limitations. See Solution 14.11.

If you examine the ADSI User object using an object browser as shown in Figure 14-1, you will notice that more properties are available than listed in Table 14-4.

Figure 14-1: Active Directory Object Browser

Some properties, such as EmployeeID, cannot be set under NT 4.0 using administrative tools such as User Manager. This is a result of the fact that the ADSI User object contains generic properties that might be applicable to users in other providers. So while it may seem as if there are more properties available than the provider exposes, they cannot all be used.

When you work with properties, make sure the program has error handling (On Error Resume Next) enabled to catch any errors. Some properties are not set until certain operations take place, and referencing the property may generate an error because it doesn't exist in the cache for the object. For example, the User object does not set the LastLogin property until a user has logged on for the first time. Properties that are not set usually do not contain values to identify them as being empty, such as a Null or Empty value, so it is not easy to test for these conditions.

One way to determine the properties associated with directory objects is to use a directory browser application. This kind of application allows you to view, and in some cases set, values associated with directory objects.

The Windows 2000 CD includes a utility called ADSIEdit. It is not installed with Windows 2000 but it is included as part of a support tools installation located under the support ools directory of the Windows installation CD.

The ADSI Resource Kit 2.5 contains two visual browsers: Active Directory Browser and DSBrowse. The Active Directory Browser can browse any object and lists associated properties.

The Active Directory Browser is located under the ADSI SDKAdsVWi386 directory, while DSBrowse is located under ADSI SDKSamplesGeneralDSbrowse. Figure 14-2 shows a screen shot of the Active Directory Browser.

Figure 14-2: Active Directory Browser

The Active Directory provider exposes more properties than the WinNT provider because there is much more information that can be stored in Windows 2000 Active Directory for each account:

'get a reference to a user object Set objUser = GetObject("LDAP://CN=Fred Smith,OU=Accounting,DC=acme,DC=com") 'set a property objUser.Put "samAccountName", "freds" objUser.SetInfo

Table 14-5 lists a number of Windows 2000 Active Directory properties.

Table 14-5: Active Directory Provider User Properties

PROPERTY

DESCRIPTION

BadLoginCount/badPwdCount

Number of bad passwords.

c

Country code (e.g., US).

Department

User's department.

Division

Company division.

EmailAddress/mail

E-mail address.

EmployeeID

Employee ID.

FaxNumber

Fax number.

FirstName

User's first name.

HomeDrive

Directory letter to associate with the user's home directory.

logonCount

Number of times the user has logged on.

HomePage

User's home page.

L

Locale/city.

LastFailedLogin

Date. Last failed logon.

LastName

Last name.

Manager

LDAP path to user's manager (e.g., CN=Administrator,CN=Users,DC=Acme,DC=com).

NamePrefix

Name prefix (e.g., Mr.).

Notes/Comment

User notes.

OfficeLocations

Office address.

PasswordLastChanged

Date and time password last changed.

PostalAddress

User's address.

PostOfficeBox

User's post office box.

PostalCode

Postal code.

Sn

Surname.

St

State.

StreetAddress

Address property.

TelephoneHome

Home phone number.

TelephoneMobile

Mobile phone number.

TelephoneNumber

Mobile phone number.

TelephonePager

Pager number.

Title

Employee title.

userSharedFolder

User shared document folder.

UserAccountControl

Integer. Combination of bitwise OR-ed values that determine options such as account status and password limitations. See Solution 14.11 later in chapter. Same as the WinNT provider's UserFlags property.

The usermnt.wsf script is a command-line utility that provides the ability to create or update users using the Windows NT ADSI provider:

For example, the following command sequence creates a new user, "freds," and sets the Fullname and Description properties:

usermnt.wsf Acme freds /p:fullname "Fred Smith" /p:description "Accountant"

If you want to update existing users, add the /u switch to the command line. The following command line updates the description for user freds:

usermnt.wsf Acme freds /u /p:description "Accountant"

The usermnt.wsf script includes an adsilib.vbs library of reusable support code. This code is used throughout the chapter:

'adsilib.vbs 'Description: Contains routines used by ADSI scripts 'Gets the value of a server object based on its server comment/name 'Parameters: 'objWebService WebService object 'strSiteName Site name you wish to get value 'Returns: Site number, blank string if not found Function FindSiteNumber(objWebService, strSiteName, strType) Dim nF, objSite nF = "" 'loop through each site, find available site # For Each objSite In objWebService 'check if the object is a web site If strcomp(objSite.Class,"IIs" & strType & "Server",1)=0 Then 'check if server comment is same as specified server name If Ucase(objSite.ServerComment) = Ucase(strSiteName) Then nF = objSite.Name Exit For End If End If Next FindSiteNumber = nF End Function 'returns the 'Parameters 'strType site type, web or FTP Function GetSiteType(strType) Select Case Ucase(strType) Case "FTP" GetSiteType = "MSFTPSVC" Case "SMTP" GetSiteType = "SmtpSvc" Case "NNTP" GetSiteType = "nntpSvc" Case Else GetSiteType = "W3SVC" End Select End Function 'Find next available site number ' Function FindNextSite(objService) Dim nF, objSite nF = 0 'loop through each site, find available site # For Each objSite In objService 'check if object is a IIS site If Left(objSite.Class,3) = "IIs" And _ Right(objSite.Class,6)= "Server" Then If nF < objSite.Name Then nF = objSite.Name End If Next FindNextSite = nF + 1 End Function 'check if script is being run interactively 'Returns:True if run from command line, otherwise False Function IsCscript() If strcomp(Right(Wscript.Fullname,11),"cscript.exe",1)=0 Then IsCscript = True Else IsCscript = False End If End Function 'display an error message and exits script 'Parameters: 'strMsg Message to display Sub ExitScript(strMsg) Wscript.Echo strMsg Wscript.Quit -1 End Sub

See Also

Search for the topic "Getting Properties for Active Directory Objects" in the ADSI 2.5 SDK Help. For more information, read the MSDN Library articles "Getting and Setting Properties" (http://msdn.microsoft.com/library/en-us/netdir/adsi/getting_and_setting_properties.asp), "Modifying User Properties" (http://msdn.microsoft.com/library/en-us/netdir/adsi/modifying_user_properties.asp), "WinNT Schema's Mandatory and Optional Properties" (http://msdn.microsoft.com/library/en-us/netdir/adsi/winnt_schemaampaposs_mandatory_and_optional_properties.asp), and "Provider Support of ADSI Interfaces" (http://msdn.microsoft.com/library/en-us/netdir/adsi/provider_support_of_adsi_interfaces.asp).

Setting Multivalued Properties

Problem

You want to read and update multivalued properties.

Solution

You can use the PutEx method to set, update, and clear multivalued properties:

Const ADS_PROPERTY_CLEAR = 1 Const ADS_PROPERTY_UPDATE = 2 Const ADS_PROPERTY_APPEND = 3 Const ADS_PROPERTY_DELETE = 4 Set objUser = GetObject("LDAP://CN=Freds,CN=Users,DC=Acme,DC=com") 'add new home phone numbers to the user objUser.PutEx ADS_PROPERTY_APPEND, "OtherhomePhone", _ Array("555-1234", "222-2222") objUser.SetInfo 'deletes a fax number objUser.PutEx ADS_PROPERTY_DELETE, "otherFacsimileTelephoneNumber", _ Array("555-3453") objUser.SetInfo 'updates (sets) the addtional mailbox addresses for the user objUser.PutEx ADS_PROPERTY_UPDATE, "otherMailbox", _ Array("freddy@acme.com", "fred@acme.com") objUser.SetInfo 'clear mobile phone objUser.PutEx ADS_PROPERTY_CLEAR, "OtherMobile", Null objUser.SetInfo

Discussion

Provider properties can be multivalued. Multivalued properties may contain one or more values. Use the GetEx method to reference values from multivalued properties:

aValues = objADSI.GetEx(strProperty)

The GetEx method returns an array of values for the property specified by the strProperty argument.

Set objUser = GetObject("LDAP://cn=Fred Smith,cn=Users,dc=acme,dc=com") aOtherPhone = objUser.GetEx("OtherTelephone") For Each strPhone In aOtherPhone Wscript.Echo strPhone Next

GetEx can return an error if the property hasn't been set in the underlying directory. For example, if no additional phone numbers have been set for an Active Directory user, an error will occur if you attempt to retrieve the OtherTelephone property.

The PutEx method adds, deletes, and sets multivalued properties:

objUser.PutEx intAction, strProperty, vValue

The intAction parameter determines how the value will be stored in the multivalued property array. Table 14-6 lists the valid intAction values.

Table 14-6: PutEx Method Action Values

ACTION VALUE

VALUE

DESCRIPTION

ADS_PROPERTY_CLEAR

1

Clears the property.

ADS_PROPERTY_UPDATE

2

Sets the property.

ADS_PROPERTY_APPEND

3

Appends a value to the property.

ADS_PROPERTY_DELETE

4

Deletes a value from the property. Error occurs if the value specified to delete does not exist.

The strProperty parameter is the property you want to maintain. The vValue parameter is the value(s) you are adding, setting, or removing from the property.

Table 14-7 contains a number of multivalued properties for the Active Directory provider.

Table 14-7: Active Directory Provider Multivalued Properties

PROPERTY

DESCRIPTION

OtherFacsimileTelephoneNumber

Additional fax numbers

OtherHomePhone

Additional home phone numbers

OtherIpPhone

Additional IP phone addresses

OtherMailbox

Additional mailboxes

OtherMobile

Additional mobile/cellular phone numbers

OtherPager

Additional pager numbers

OtherTelephone

Additional office phone numbers

See Also

Search for "Example Code for Using PutEx" in the ADSI 2.5 SDK Help file.

Deleting a User

Problem

You want to delete a user.

Solution

Using the WinNT ADSI provider, reference a computer or domain object in which the user resides and then invoke the Delete method:

Dim objDomain 'get a reference to the Acme domain Set objDomain = GetObject("WinNT://Acme") 'delete a user objDomain.Delete "user", "Freds"

For the Active Directory provider, you must get a reference to the container the object the user resides in and call the Delete method using the relative distinguished name (RDN) for the user you want to delete:

'delete a user using the Active Directory provider Dim objContainer 'get a reference to the LDAP container that contains the object to delete Set objContainer = GetObject("LDAP://OU=Accountants,DC=Acme,DC=com") 'delete Fred Smith by specifying his relative distinguished name objContainer.delete "user", "CN=Fred Smith"

Discussion

To delete a user, get a reference to the container that you want to delete the user from. Under the Windows NT provider, the container is the domain or computer the user resides in.

Once you have a reference to the container, invoke the Delete method:

objContainer.Delete strClassName, strObjectName

strClassName specifies the object class type to delete, for a user the class name is "user." strObjectName represents the user to delete. For the Windows NT provider, this is the user ID.

See Also

For more information, read the MSDN Library article "Removing Users" (http://msdn.microsoft.com/library/en-us/netdir/adsi/removing_users.asp).

Setting User Logon Time

Problem

You want to set user logon times.

Solution

The following command-line script sets the logon time for a specified user. It uses the WinNT provider to get a reference to a user and updates the LoginHours property with any changes specified from the command prompt:

Discussion

The logon hours allowed control of what hours a user can log on to the system. The user can be allowed/disallowed any 1-hour block within a 7-day period. This can be set from NT User Manager or the Windows 2000 Active Directory plug-in, as shown in Figure 14-3.

Figure 14-3: Logon Hours dialog box

The logon hours are represented by an array of byte (8-bit) values. Each bit represents 1 hour, so the entire week of 168 hours (7×24) is represented by an array of 21-byte values (218). If the bit is set to 1, access is allowed, if it is set to 0, access is disallowed during that period.

The LogonHours property returns the array of values, which has an offset of 0. The first element in the array stores the period of 8 hours from 9 AM Sunday to 5 PM Sunday. The second array element stores from 5 PM Sunday to 1 AM Monday, and so on.

Unfortunately, the LogonHours property is returned as an array of byte values. VBScript cannot handle this data type (it recognizes it, but can't deal with it). The following code binds to a user and returns the LoginHours property, but an error is generated when it tries to enumerate the array:

'get user Set objuser = GetObject("WinNT://acme/freds,user") 'get the login hours obj = objuser.LoginHours 'display data type Wscript.Echo Vartype(obj) 'try to enumerate array, an error will occur: For nF = Lbound (obj) to ubound (obj) Wscript.Echo obj(nF) Next

A simple wrapper object BAC.Convert (Binary Array Conversion) written in Visual Basic is provided to solve this problem. The object converts an array of byte values returned from functions to an array of variant values, and it also converts an array of variants to an array of byte values to pass to COM methods and functions that require this data type as a parameter.

The BAC object can be used for any COM object that returns or requires a parameter as an array of bytes.

The DLL is located in the supporting file for download at the Apress Web site (http://www.apress.com/). To use the BAC component, you must have VB runtime installed. Copy BAConv.DLL to your system and register it using Regsvr32.exe.

The BAC component exposes two functions: ByteToVariant and VariantToByte. Their syntax is as follows:

arrVar = ByteToVariant(arrByte) arrByte = VariantToByte(arrVar)

ByteToVariant converts an array of byte values supplied by the arrByte parameter to an array of variants.

VariantToByte converts an array of variant values supplied by the arrVar parameter to an array of byte values.

The following snippet uses the BAC object to convert the array of bytes the LoginHours property returns to an array of variant values that VBScript can manipulate:

'get a user object Set objUser = GetObject("WinNT://acme/freds,user") 'create a Byte array conversion object Set objBAC = CreateObject("BAC.Convert") 'get the login hours using the BAC object obj = objBAC.ByteToVariant(objUser.LoginHours) 'enumerate array For nF = Lbound (obj) to ubound (obj) Wscript.Echo obj(nF) Next

The sethours.wsf solution script sets the logon hours for a user:

sethours.wsf container user day hour duration onoff

The container parameter is a computer or domain in which the user resides. The user is the user ID for whom you set the logon hours. The day parameter is an integer value representing the day to set the logon access for: 1 is Sunday, 2 is Monday, and so on. Hour is the start hour to set the logon access for in 24-hour clock format: 0 is 12 AM, 13 is 1 PM, and so on. Duration is number of hours to set. Onoff is a Boolean value indicating whether to grant or deny access. If onoff is True, access is granted, and if False, access is denied.

To deny access to user freds in the Acme domain on Sunday from 1 PM to 4 PM, use the following line:

sethours.wsf Acme freds 1 13 3 False

Limiting Computer Access

Problem

You want to limit the machines on which a user can log on.

Solution

Using the ADSI WinNT provider, you can set the LoginWorkstations property to limit the computers on which a user can log on:

'get a reference to the user Set objUser = GetObject("WinNT://Acme/freds") 'list the machines the users have access to For Each station In objUser.LoginWorkstations Wscript.Echo station Next 'set the machines a user is permitted to logon to objUser.LoginWorkstations = Array("thor"," odin"," loki") objUser.SetInfo

Discussion

Windows NT and Windows 2000 provide the ability to limit the machines users can log on to.

The WinNT provider's LoginWorkstations property returns an array of machine names that the user can access.

The ADSI provider can use the LoginWorkstations property, but it includes a UserWorkstations property as well. The UserWorkstations property is a string that contains workstation names separated by commas:

'set the workstations a user can log on to using the Active Directory provider Dim objUser 'get a reference to a user object Set objUser=GetObject("LDAP://CN=Fred Smith,OU=Accounting,DC=acme,DC=com") objUser.UserWorkstations =" odin,thor,loki" objUser.SetInfo 'update user settings

If workstation limitations have not been set under User Manager, an error will occur if you try to read the LoginWorkstations property.

Setting User Flags

Problem

You want to prevent a user's password from expiring.

Solution

You can set the UF_DONTEXPIRE_PASSWD bit of the UserFlags property for the WinNT provider:

Const UF_DONTEXPIRE_PASSWD = 65536 Set objUser = GetObject("WinNT://ACME/fsmith,user") objUser.Put "userFlags", usr.Get("UserFlags") Or ADS_UF_DONTEXPIRE_PASSWD objUser.SetInfo

Discussion

The Windows NT provider's User object's UserFlags property is a combination of values that control various user settings. Table 14-8 lists the WinNT provider's UserFlags values.

Table 14-8: WinNT Provider's UserFlags Values

FLAG

VALUE

DESCRIPTION

UF_SCRIPT

1

The logon script will be executed.

UF_ACCOUNTDISABLE

2

The user's account is disabled.

UF_LOCKOUT

16

The account is currently locked out.

UF_PASSWD_NOTREQD

32

No password is required.

UF_PASSWD_CANT_CHANGE

64

The user cannot change the password.

UF_TEMP_DUPLICATE_ACCOUNT

256

This is an account for users whose primary account is in another domain. This account provides user access to this domain, but not to any domain that trusts this domain. Sometimes it is referred to as a local user account.

UF_NORMAL_ACCOUNT

512

This is a default account type that represents a typical user.

UF_DONTEXPIRE_PASSWD

65536

Represents the password, which should never expire on the account.

Use the logical operators OR and XOR to perform bitwise operations to set flags. To set a flag, OR the value against the UserFlags property:

'prevent user from changing their own password Const UF_PASSWD_CANT_CHANGE = 65536 Set objUser = GetObject("WinNT://ACME/fsmith,user") objUser.Put "userFlags", usr.Get("UserFlags") Or UF_PASSWD_CANT_CHANGE objUser.SetInfo

To turn a flag off, use the AND NOT Boolean operation. If you want to "toggle" a flag, use the XOR operator. The XOR operator works like a toggle, so it can turn a bit on or off:

Const UF_PASSWD_CANT_CHANGE = 64 Const UF_DONTEXPIRE_PASSWD = 65536 Dim objUser Set objUser = GetObject("WinNT://ACME/fsmith,user") 'turn of the UF_PASSWD_CANT_CHANGE flag objUser.userFlags = objUser.userFlags And Not UF_PASSWD_CANT_CHANGE 'toggle the UF_DONTEXPIRE_PASSWD flag objUser.userFlags = objUser.userFlags Xor UF_DONTEXPIRE_PASSWD objUser.SetInfo

The Active Directory provider does not have a UserFlags property, but it has a userAccountControl property that provides similar options. You can use the flags that are available for the Windows NT provider.

See Also

For more information, read the MSDN Library article "Modifying User Properties" (http://msdn.microsoft.com/library/en-us/netdir/adsi/modifying_user_properties.asp).

Listing Groups

Problem

You want to list all objects from a group.

Solution

To list the members of a group using the WinNT provider, you can reference the group object and then loop through each member of the Members collection property:

Set objGroup = GetObject("WinNT://Acme/Domain Users,group") 'display name of each object in group For Each objUser In objGroup.Members Wscript.Echo objUser.Name Next

The LDAP provider allows for the enumeration of groups in the same way as the WinNT provider:

'get a reference to the Auditors group under the Accounting organizational unit Set objGroup = GetObject("LDAP://cn=Auditors,ou=Accounting,DC=Acme,DC=com") 'display name of each object in group For Each objUser In objGroup.Members Wscript.Echo objUser.Name Next

Discussion

A group object exposes a Members collection, which contains all members of the group.

You may want to determine what groups a user is a member of. This is easily accomplished by enumerating the Groups collection property of the User object:

Set objUser = GetObject("LDAP://CN=Administrator,CN=Users,DC=Acme,DC=com") 'display name of each group user is member of For Each objGroup In objUser.Groups Wscript.Echo objGroup.Name Next

The preceding example demonstrates enumerating the Administrators groups using the LDAP provider. The WinNT provider exposes the same property.

See Also

For more information, read the 15 Seconds FAQ "How do I get all the users in a group using ADSI?" at http://local.15seconds.com/faq/ADSI/623.htm.

Creating or Deleting a User Group

Problem

You want to create or delete a user group.

Solution

Using the WinNT provider, you can reference a domain or computer and then invoke the Create method, specifying the group class as the object that you want to create:

Const ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP = 4 Set objDomain = GetObject("WinNT://Acme") Set objGroup = objDomain.Create("group", "Acctusers") objGroup.groupType = ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP objGroup.Description = "Accounting Users" objGroup.SetInfo

Discussion

To create a new group using the Windows NT provider, first reference the domain or computer object in which you want to create the user.

Then invoke the object's Create method, specifying the Group class as the object you want to create:

Set objGroup = objDomain.Create("Group", strGroupName)

strGroupName identifies the group.

A groupType property must be set for the new group being created. For the NT provider, there is either the global or local group. Table 14-9 lists the different group types and associated values.

Table 14-9: Group Types

PROPERTY

VALUE

DESCRIPTION

GROUP_TYPE_GLOBAL_GROUP

2

Can contain user accounts from the current domain.

GROUP_TYPE_LOCAL_GROUP

4

Can contain local accounts and global groups.

GROUP_TYPE_UNIVERSAL_GROUP

8

Windows 2000 only. Can contain accounts and account groups from any domain, but not local groups.

Once a group has been created, invoke the object's SetInfo method to save the changes. No additional properties are needed for the new group. An optional Description parameter can be set to provide a more detailed description of the group.

Creating a new group using the Active Directory provider is similar to creating one using the Windows NT provider.

First, get a reference to the container you want to add the new group in and create the group using the Create method, specifying the Group object as the class you want to create and the RDN for the group you want to create.

You need to set the samAccountName property for the new group. This property represents the name that appears to Windows NT and Windows 9x machines:

Const GROUP_TYPE_SECURITY_ENABLED = &h80000000 Dim objContainer, objGroup Set objContainer = GetObject("LDAP://CN=Users,DC=Acme,DC=com") 'create the group Set objGroup = objContainer.Create("Group", "CN=Accounting Group") 'set the SAM account name for compatibility with existing NT and 'Win9x clients objGroup.samAccountName = "Acctusers" objGroup.groupType = GROUP_TYPE_GLOBAL_GROUP Or GROUP_TYPE_SECURITY_ENABLED objGroup.SetInfo

There are more group options available to Windows 2000 than to Windows NT. Windows 2000 has security groups and distribution groups. A security group is the same as a group in Windows NT. A distribution group is the same as a security group except it cannot be used to apply security access-control lists to Active Directory objects.

To create a security group, set the group type to local, global, or universal, and OR the GROUP_TYPE_SECURITY_ENABLED flag. The GROUP_TYPE_SECURITY_ENABLED flag has a value of -2147483648.

To delete a group using the WinNT provider, get a reference to the container the group exists in (domain or computer) and call the Delete method. You need to specify the group as the class type and the name of the group you are deleting:

Set objDomain = GetObject("WinNT://Acme") Set objGroup = objDomain.Delete("group", "Acctusers")

Similar steps are required to delete an Active Directory group:

Set objCN = GetObject("LDAP://ou=Accounting,DC=Acme,DC=com") objCN.Delete "group"," cn=Accounting Group"

See Also

For more information, read the MSDN Library article "Creating Groups" (http://msdn.microsoft.com/library/en-us/netdir/adsi/creating_groups.asp).

Adding a User or Group to a Group

Problem

You want to add a user or a group to a group.

Solution

You can reference the group object to which you want to add a user or local group and then invoke the Add method:

Dim objGroup 'get the group to add objects to.. Set objGroup = GetObject("WinNT://Acme/Acctusers") objGroup.add "WinNT://Acme/freds,user" 'add a user objGroup.add "WinNT://Acme/joeb" 'add a another user 'add a group - can only add other groups to Local groups, not Global objGroup.add "WinNT://Acme/finance,group"

Discussion

To add groups or users to a group using the Windows NT provider, reference the group to which you want to add. Then invoke the Add method:

objGroup.add (strObjectPath)

strObjectPath represents the ADSI path to the user or group you want to add.

To add an object to an Active Directory group, get a reference to the container to which you want to add. Then invoke the Add method specifying the DN path to the object you want to add to the group:

'get the group to add objects to.. Set objGroup = _ GetObject("LDAP://CN=Accounting Group,CN=Users,DC=Acme,DC=com") 'add a user objGroup.add "LDAP://CN=Fred Smith,CN=Users,DC=Acme,DC=com"

To delete an object from a group using the WinNT provider, get a reference to the group you want to delete the object from and invoke the Remove method, specifying the ADSI path to the object you want to delete:

'get the group to remove objects from Set objGroup = GetObject("WinNT://Acme/Acctusers") objGroup.Remove("WinNT://Acme/freds ") 'remove the user freds

Removing an object from an Active Directory group using the LDAP provider involves the same steps as removing an object using the WinNT provider:

'get the group to remove the object from Set objGroup = _ GetObject("LDAP://CN=Accounting Group,CN=Users,DC=Acme,DC=com") 'remove a user from the group objGroup.Remove "LDAP://CN=Fred Smith,CN=Users,DC=Acme,DC=com"

See Also

For more information, read the MSDN Library article "Adding Domain Groups to Machine Local Groups on Member Servers and Windows 2000 Professional" (http://msdn.microsoft.com/library/en-us/netdir/ad/adding_domain_groups_to_machine_local_groups_on_member_servers_and_windows_2000_professional.asp).

Determining Group Membership

Problem

You want to determine group membership at logon.

Solution

Use the Group object's IsMember method to check group membership:

Set objNetwork = Wscript.Create("Wscript.Network") Set objGroup= GetObject("WinNT://Acme/Accounting Users,group") ' If (objGroup.IsMember("WinNT://ACME/" & objNetwork.UserName)) Then 'connect to printer End If

Discussion

The IsMember method provides the ability to check if a user is a member of a specific group or not.

To use the IsMember method get a reference to the group you want to check the membership of. Invoke the IsMember method, specifying the name of the user you are checking the membership for:

bFlag = objGroup.IsMember(strMember)

The strMember parameter is the name of the user to check for. IsMember returns True if the user is a member of the group. For the WinNT provider, specify the full path to the name to check for (e.g., WinNT://Acme/Freds).

For Active Directory, get a reference to the group you want to check and invoke the IsMember method, specifying the full LDAP path to the object you are checking group membership for:

Set objGroup = _ GetObject("LDAP://CN=Accounting Users,OU=Accounting,DC= Acme,DC=com") 'check group membership.. If (objGroup.IsMember("LDAP://CN=FredS,OU=Accounting,DC=Acme,DC=com")) Then 'do something. . . End If

See Also

For more information, read the MSDN Library article "IADsGroup::IsMember" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsgroup_ismember.asp).

Querying Active Directory Values

Problem

You want to query the Active Directory of a Windows 2000/XP server.

Solution

You can use the ADSI ADO interface to execute queries against Active Directory:

Discussion

The Active Directory provider includes an OLE DB interface that can perform queries against the Active Directory namespace. Using this interface together with ADO, you can execute SQL queries to return information on Active Directory objects.

To use the provider, you must have a recent version of ADO installed. Before a query can be executed against the provider, you must create an ADO Connection object and specify the provider:

'create a ADO connection object Set objConn = CreateObject("ADODB.Connection") 'select the provider objConn.Provider = "ADsDSOObject" 'open the connection - the string here can be anything objConn.Open "Active Directory Provider"

Once the Connection object is created, you can execute queries against the provider. The query contains standard SQL statements. The data source is the distinguished name of a LDAP container you want to search in (e.g., LDAP://ou=Sales,dc=Acme,dc=COM).

The query criteria can contain any Active Directory object properties. You can use the asterisk (*) wildcard to perform partial searches on fields:

Set objCmd = CreateObject("ADODB.Command") Set objCmd.ActiveConnection = objConn 'Return all items where the name starts with F objCmd.CommandText = "SELECT cn, name,sn, street, l, st FROM "& _ "'LDAP://OU=Sales,dc=acme,dc=COM' WHERE objectClass='user'" & _ "AND objectCategory='person' AND Name='F*'"

Initially when a search is performed, the source container and all containers below it are searched. If you only want to search a single container, set the SearchScope property of the ADO Command object to ADS_SCOPE_ONELEVEL, which has the value of 1. The default is ADS_SCOPE_SUBTREE, which has the value of 2.

The following sample searches the Sales organizational unit of the Acme domain for all objects in the container:

Const ADS_SCOPE_ONELEVEL = 1 'create a Connection object Set objConn = CreateObject("ADODB.Connection") objConn.Provider = "ADsDSOObject" objConn.Open "Active Directory Provider" 'create a command object Set objCmd = CreateObject("ADODB.Command") Set objCmd.ActiveConnection = objConn 'search the one level only objCmd.Properties("searchscope") = ADS_SCOPE_ONELEVEL objCmd.CommandText = _ "SELECT cn FROM 'LDAP://OU=Sales,DC=Acme,DC=COM' WHERE objectClass='*'" Set objRst = objCmd.Execute While Not objRst.Eof Wscript.Echo objRst("cn") objRst.MoveNext Wend objRst.Close objConn.Close

The ability to execute a query against the provider allows for the whole directory tree to be searched. If you specify the domain-level root object, the search starts from the root and iterates through all subcontainers (as long as the SearchScope property hasn't been changed).

Criteria must be specified in the search string, so if you want to list all object classes from the whole domain, you have to include criteria to include all objects:

SELECT name FROM 'LDAP://DC=Acme,DC=COM' WHERE objectClass='*'

The adsiqry.vbs script is a command-line script that executes a query against the Active Directory provider and outputs the results to standard output. It takes an Active Directory query as a parameter and outputs the results in comma-delimited format:

adsiqry.wsf "SELECT cn, name,sn, street, l, st FROM 'LDAP://OU=Sales,DC=Acme, DC=COM' WHERE objectClass='user' AND objectCategory='person' ORDER BY NAME"

The script has three optional parameters. By default, all subcontainers are searched—if you specify /s, only the source container is searched. If you specify /h, the field names are included in the header. The default comma delimiter can be changed by specifying the /d:X parameter, where X represents an alternative delimiter:

adsiqry.wsf "SELECT name FROM 'LDAP://DC=Acme,DC=COM' WHERE objectClass='*'" /s /h /d:;

You can't query the Windows NT provider using SQL queries through ADO. WinNT provider objects have a Filter property that allows the filtering of object types:

objObject.Filter = aPropArray

aPropArray is an array of object class names. Once a filter is applied to an object, any enumeration operations performed on the object will only return object types specified by the filter. You cannot specify detailed criteria.

'get a reference to a domain object Set objDomain = GetObject("WinNT://Acme") 'filter on the user objects objDomain.Filter = Array("user") For Each objUser In objDomain Wscript.Echo objUser.Name Next

See Also

For more information, read the MSDN Library article "Searching with ActiveX Data Objects (ADO)" (http://msdn.microsoft.com/library/en-us/netdir/adsi/searching_with_activex_data_objects_ado.asp).

Controlling NT Services

Problem

You want to check if a service has stopped.

Solution

You can use the WinNT ADSI provider to reference Windows NT or Windows 2000/XP services. Services can be queried, stopped, started, or paused. The following command-line script enumerates, starts, stops, or pauses services for a specified computer:

Discussion

The WinNT provider provides access to NT services. To get a reference to a service, use the following line:

objService = GetObject("WinNT://Computer/servicename,Service")

Servicename is the internally stored name of the service—it is not the name that appears under Control Panel > Services.

To list all services installed on a computer, get a reference to the computer and filter all class objects of type Service:

'list all services on the computer Odin 'get a reference to a computer Set objComputer = GetObject("WinNT://Odin") 'filter on the Service object class objComputer.Filter = Array("Service") 'enumerate the services For Each objService In objComputer Wscript.Echo objService.Name, objService.DisplayName Next

Table 14-10 lists Service object properties.

Table 14-10: Service Object Properties

PROPERTY

DESCRIPTION

DisplayName

Friendly name that appears in Control Panel.

Dependencies

Array of values. Lists all services that service is dependent upon.

LoadOrderGroup

Name of load order group.

Path

String. Path to service executable.

HostComputer

String. Computer the service resides on.

ServiceAccountName

String. Systems account used to log on user.

ServiceType

Service type. Combination of any value that is listed in Table 14-11.

StartType

Indicates how service is started. Valid values are listed in Table 14-12.

StartupParameters

Parameters pass to service executable at startup.

Status

Determines the operational status of the server (stopped, paused, and so on). Valid values are listed in Table 14-13.

Table 14-11: ServiceType Values

NAME

VALUE

ADS_KERNEL_DRIVER

1

ADS_FILE_SYSTEM_DRIVER

2

ADS_OWN_PROCESS

16

ADS_SHARE_PROCESS

32

Table 14-12: Service Start Type Values

NAME

VALUE

DESCRIPTION

ADS_BOOT_START

0

Starts by OS loader

ADS_SYSTEM_START

1

Starts during OS initialization

ADS_AUTO_START

2

Starts by Service Control Manager during system startup

ADS_DEMAND_START

3

Manual startup

ADS_DISABLED

4

Service disable

Table 14-13: Service Status Values

NAME

VALUE

DESCRIPTION

ADS_SERVICE_STOPPED

1

Service stopped

ADS_SERVICE_START_PENDING

2

Service start initialized

ADS_SERVICE_STOP_PENDING

3

Service stop initialized

ADS_SERVICE_RUNNING

4

Service is running

ADS_SERVICE_CONTINUE_PENDING

5

Service in the process of continuing

ADS_SERVICE_PAUSE_PENDING

6

Service in the process of pausing

ADS_SERVICE_PAUSED

7

Service paused

ADS_SERVICE_ERROR

8

Error occurred

Table 14-11 lists ServiceType values.

Table 14-12 lists service start type values.

Table 14-13 lists service status values.

A service can be started/stopped/paused by invoking the Start, Stop, or Pause method respectively on a Service object. Make sure that any dependant services are stopped before stopping the service. The Stop method provides the option to stop dependant services such as Control Panel.

When you perform multiple service operations (starting/stopping/pausing) where operations in the sequence are dependant on the successful completion of the previous service operation, checks should be made to determine that the service is in the assumed state before continuing.

For example, if a service is stopped using the Stop method, the code might continue executing before the service has been completely stopped. Following code might not execute correctly if it is dependant upon the service having been stopped.

'get a reference to the Exchange Internet Mail Connector service Set objService = GetObject("WinNT://odin/MSExchangeIMC") objService.Stop 'wait until the service has completely stopped While objService.Status = ADS_SERVICE_STOPPED Wscript.Sleep 100 Wend

The svcmaint.wsf script can stop, start, resume, and list services.

svcmaint.wsf computer [service] [operation] [/l]

Computer is the name of computer the service resides on. Service is the name of the service to manipulate. Operation represents start, stop, or pause. If you pass a /l switch, all services will be listed.

For example, the following stops Exchange server services:

svcmaint.wsf odin MSExchangeIMC stop svcmaint.wsf odin MSExchangeMTA stop svcmaint.wsf odin MSExchangeIS stop svcmaint.wsf odin MSExchangeDS stop svcmaint.wsf odin MSExchangeSA stop

See Also

For more information, read the MSDN Library article "IADsServiceOperations" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsserviceoperations.asp).

Listing Connected Resources

Problem

You want to list all connected sessions.

Solution

Using the WinNT provider, you can enumerate connected resources through the Sessions collection of the LanmanServer object for a specified computer:

'lstcnusers.vbs 'list connected users to specified server Dim objFileService, objSession, strComputer 'check argument count If Not Wscript.Arguments.Count = 1 Then ShowUsage Wscript.Quit End If 'get the file service object strComputer = Wscript.Arguments(0) 'get a reference to the LanmanServer service Set objFileService = GetObject("WinNT://" & strComputer & "/LanmanServer") 'loop through each session and display any connected users For Each objSession In objFileService.sessions 'check if the session user ID is not empty If Not objSession.user = "" Then [<> ?] Wscript.StdOut.WriteLine objSession.user End If Next Sub ShowUsage WScript.Echo "lstcnusers.vbs lists connected users "& vbCrLf & _ "Syntax:" & vbCrLf & _ "lstcnusers.vbs computer" & vbCrLf & _ "computer computer to enumerate connected users" End Sub

Discussion

The Windows NT provider exposes the connected session resources through the FileService object. A reference can be retrieved from the NT network management (Lanman) object of a server.

A connected session can represent either a user or a computer. To get a list of all connected sessions, get a reference to the LanmanServer service of the computer you want to check:

'list all sessions on computer odin 'get a reference to the LanmanServer service Set objFileService = GetObject("WinNT://odin/LanmanServer") 'enumerate all sessions For Each objSession In objFileService.sessions objTextStream.WriteLine objSession.Name Next

Table 14-14 lists Session object's properties.

Table 14-14: Session Object Properties

PROPERTY

DESCRIPTION

Computer

Name of connected computer

ConnectTime

Time connected in seconds

Name

Name in format userCOMPUTER (e.g., administratorODIN)

IdleTime

Idle time in seconds

User

User name

See Also

For more information, read the MSDN Library articles "IADsFileServiceOperations" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsfileserviceoperations.asp) and "IADsSession" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadssession.asp).

Determining User RAS Access

Problem

You want to determine all users' RAS access.

Solution

User RAS access is not directly exposed through the ADSI User object. The ADSI 2.5 SDK includes an extension DLL that allows for the setting of RAS access permissions. The following command-line script sets RAS settings for a specified user:

Discussion

To use the extensions, register the ADsRAS.dll file that is included with the ADSI 2.5 SDK.

The DLL extends the WinNT provider's User object with a number of RAS-related properties. Table 14-15 lists the ADsRAS properties.

Table 14-15: ADsRAS.dll Properties

PROPERTY

DESCRIPTION

DialinPrivilege

Boolean. If True, user has RAS dial-in access.

GetRasCallBack

Integer value. Determines user call back type.

ADS_RAS_NOCALLBACK

1

ADS_RAS_ADMIN_SETCALLBACK

2

ADS_RAS_CALLER_SETCALLBACK

4

GetRasPhoneNumber

Returns the RAS phone number used for call back.

To allow a user RAS, set the DialinPrivilege property to True. If you need to set specific call back access, invoke the SetCallBack method, specifying the type of call back the user has:

Dim objuser Set objUser = GetObject("WinNT://acme/freds") 'all the user dial in access objUser.DialinPrivilege = True 'set users call back access objUser.SetRasCallBack ADS_RAS_CALLER_SETCALLBACK objUser.SetInfo

See Also

Search for the document Rtk.HTM in the ADSI 2.5 SDK directory.

Listing Network Shares

Problem

You want to list all network shares.

Solution

You can reference the computer on which the file shares reside and then enumerate all FileShare objects from the LanmanServer service object:

Dim objFileService, objSession 'get the file service object Set objFileService = GetObject("WinNT://odin/LanmanServer") 'filter on file shares objFileService.Filter = Array("FileShare") 'loop through and display description of all file shares For Each objFileShare In objFileService Wscript.Echo objFileShare.Name Next

Discussion

The WinNT provider exposes network share information. You can enumerate, create, and delete shares.

To reference a file share, reference the LanmanServer service on the computer on which the share resides, followed by the share name:

Set objFileShare = GetObject("WinNT://computer/LanmanServer/sharename")

Sharename is the name of the share you want to reference. The Share object returned exposes the properties listed in Table 14-16.

Table 14-16: Share Object Properties

PROPERTY

DESCRIPTION

Name

Name of share.

Description

Share description.

Path

Path share represents.

CurrentUserCount

Current connected users.

MaxUserCount

Maximum number of users allowed to connect to the share. For unlimited users, set to -1.

See Also

For more information, read the MSDN Library article "IADsFileShare" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsfileshare.asp).

Creating or Deleting a Network Share

Problem

You want to create a file share.

Solution

You can retrieve a reference to the LanmanServer service on the computer on which you want to create the share using the WinNT provider and then call the Create method, specifying the FileShare class as the object type you want to create:

' 'get the file service object Set objFileService = GetObject("WinNT://Odin/LanmanServer") Set objFileShare = objFileService.create("FileShare", "AcctData") objFileShare.Path = "d:dataaccounting" objFileShare.Description = "Accounting Data" objFileShare.MaxUserCount = -1 'set unlimited users objFileShare.SetInfo

Discussion

To create a file share, you can reference the LanmanServer service on the machine on which you want to create the share. Next, invoke the Create method, specifying the FileShare class as the object you want to create, followed by a share name. Then set the path the share will point to.

To delete a share, reference the LanmanServer service on the machine from which you want to delete the share. Invoke the Delete method, specifying the object class type as FileShare followed by the name of the share you want to delete:

'get the file service object Set objFileService = GetObject("WinNT://odin/LanmanServer") 'delete the share AcctData objFileService.Delete "FileShare", "AcctData"

See Also

For more information, read the MSDN Library article "IADsFileShare" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsfileshare.asp).

Print Queue Operations

Problem

You want to purge a print queue of current print jobs.

Solution

You can reference the queue object using the WinNT provider and call the Purge method. The following command-line script pauses or resumes a specified print queue:

Discussion

The Windows NT provider exposes computer-shared printers (print queues). To get a reference to a print queue, use the following line:

Set objPrintQ = GetObject("WinNT://Computer/PrintQ)

Computer is the name of the computer the print queue resides on. PrintQ is the name of the queue represented by the shared name of the printer.

The PrintQueue object provides a Purge method to remove all print jobs from the queue. The queue can also be paused using the Pause method, which prevents any further jobs from being printed. To resume a paused queue, invoke the Resume method.

The PrintQueue object exposes a Status property that identifies the current status of the printer servicing the queue. Table 14-17 lists the values the Status property can return.

Table 14-17: Status Property Values

CONSTANT

VALUE

ADS_PRINTER_PAUSED

1

ADS_PRINTER_PENDING_DELETION

2

ADS_PRINTER_ERROR

3

ADS_PRINTER_PAPER_JAM

4

ADS_PRINTER_PAPER_OUT

5

ADS_PRINTER_MANUAL_FEED

6

ADS_PRINTER_PAPER_PROBLEM

7

ADS_PRINTER_OFFLINE

8

ADS_PRINTER_IO_ACTIVE

256

ADS_PRINTER_BUSY

512

ADS_PRINTER_PRINTING

1024

ADS_PRINTER_OUTPUT_BIN_FULL

2048

ADS_PRINTER_NOT_AVAILABLE

4096

ADS_PRINTER_WAITING

8192

ADS_PRINTER_PROCESSING

16384

ADS_PRINTER_INITIALIZING

32768

ADS_PRINTER_WARMING_UP

65536

ADS_PRINTER_TONER_LOW

131072

ADS_PRINTER_NO_TONER

262144

ADS_PRINTER_PAGE_PUNT

524288

ADS_PRINTER_USER_INTERVENTION

&h00100000

ADS_PRINTER_OUT_OF_MEMORY

&h00200000

ADS_PRINTER_DOOR_OPEN

&h00400000

ADS_PRINTER_SERVER_UNKNOWN

&h00800000

ADS_PRINTER_POWER_SAVE

&h01000000

See Also

For more information, read the MSDN Library article "IADsPrintQueueOperations" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsprintqueueoperations.asp).

Listing Print Jobs

Problem

You want to list current print jobs.

Solution

You can reference a queue object using the WinNT provider and enumerate the PrintJobs collection to list any jobs and related information:

Discussion

A print queue contains a list of print jobs currently being processed. Each job is represented as a PrintJob object and can be paused or resumed, but not deleted.

To enumerate a print queue, get a reference to the print queue you want to process. The PrintQueue object has a PrintJobs property that contains a list of active jobs in the form of a PrintJob object.

Table 14-18 lists the PrintJob object's properties.

Table 14-18: PrintJob Object Properties

PROPERTY

DESCRIPTION

User

User name of the user who submitted the print job.

TimeSubmitted

Time the job was submitted.

TotalPages

Number of pages in the document.

Size

Size in bytes.

Description

Name of the document being printed.

Priority

The higher the number, the greater the priority.

StartTime

Start of the time range when the document can be printed.

UntilTime

End of the time range when the document can be printed.

Notify

Name of the user to notify when the job is complete.

TimeElapsed

Time elapsed in seconds of the current active print job.

PagesPrinted

Number of pages in the current active job.

Position

Position of the job in the queue.

Status

Status of the job in the queue. Any combination of the status flag values listed in Table 14-19.

Table 14-19: PrintJob Status Flags

NAME

VALUE

DESCRIPTION

ADS_JOB_PAUSED

1

Job is paused

ADS_JOB_ERROR

2

Job is in error status

ADS_JOB_DELETING

4

Job is being deleted

ADS_JOB_PRINTING

16

Job is being printed

ADS_JOB_OFFLINE

32

Printer offline

ADS_JOB_PAPEROUT

64

Printer is out of paper

ADS_JOB_PRINTED

128

Job is printed

ADS_JOB_DELETED

256

Job is deleted

A PrintJob object can be paused by invoking the Pause method and resumed by invoking the Resume method. You cannot delete a print job.

PrintJob properties such as priority and start and end times can be modified. The following example increases the priority of jobs with less than 50 pages so they print before larger jobs:

Dim objPrintQ, objJob 'get a reference to a print queue Set objPrintQ = GetObject("WinNT://Odin/HP5") 'enumerate all print jobs For Each objJob In objPrintQ.printjobs 'if the number of pages is less than 50, increase priority If objJob.TotalPages < 50 Then objJob.Priority = 10 objJob.SetInfo End If Next

Table 14-19 lists PrintJob status flags.

See Also

For more information, read the MSDN Library article "IADsPrintJobOperations" (http://msdn.microsoft.com/library/en-us/netdir/adsi/iadsprintjoboperations.asp).

Категории