At callout A in Listing 3, the foreach loop processes the command-line arguments. Not including the question mark, the script supports eight optional command-line switches: d=TargetDomain, A=MaxPasswordAge, a=MinPasswordAge, l=MinPasswordLength, h=PasswordHistoryLength, b=MaxBadPass-wordsAllowed, u=AutoUnlockInterval, and o=LockoutObservationInterval.
Through each loop iteration, the script uses Perl's default scalar, $_, to compare an element of the command-line argument array to a regular expression that corresponds to a specific command-line switch. When a match occurs, the script evaluates the second part of the expression. Except for the target domain scalar $strDomain, the second part of each expression creates and initializes a third hash, %AccountPolicy.
The %AccountPolicy hash stores the changes that you specify on the command line. Like the Old and New Account Policy hashes, the names of the %AccountPolicy's keys are identical to the ADSI Domain object property names. Unlike the other two hashes, the script creates and initializes elements (key > value pairs) for only those options defined on the command line. For example, if you specify only three command-line options, the hash will only contain three key > value pairs. This approach makes it easy to determine which properties to change.
The split operator is used in each expression to separate the switch from the value. The [1] subscript tells Perl's split operator to return only the value used to initialize the corresponding key in the %AccountPolicy hash.
After exiting the foreach loop, the script tests the target domain scalar $strDomain. If this scalar didn't get set via the command line, the script sets the scalar to the value of the USERDOMAIN environment variable. However, if the user has logged on locally to a workstation or member server, this value will contain the local machine name rather than the domain name, causing the subsequent GetObject call to fail because GetObject is expecting a domain name. The bottom line is that you must either specify the target domain using the d=TargetDomain switch or be logged on to the target domain. ADSI supports changing the Account Policy on domains, but not on member servers and workstations.
Next, the script calls the Win32::OLE module's GetObject method. Recall that this method takes only one argument in the form of a moniker. When you use ADSI, the moniker identifies an ADSI registered namespace. In AcntPlcy.pl, the moniker identifies the WinNT namespace. (The ADSI namespace identifiers are case sensitive.) On success, GetObject returns a reference to the object that the argument identifies, which initializes $oDomain.
After $oDomain contains a valid object reference, the script uses this reference to access the object's methods and properties. Recall that in the beginning of the script, you created two hashes (%OldAccountPolicy and %NewAccountPolicy) using the ADSI Domain Object's property names as the hash keys. The foreach loop at B in Listing 3 traverses the hash keys in the %OldAccountPolicy hash. Through each iteration, the foreach loop uses Perl's keys operator to assign the hash key name to the $key scalar. The $key scalar identifies the property to fetch and specifies where to store the property in the original hash. The $oDomain reference invokes the ADSI Domain Object's Get method, which retrieves the value of the property and stores it in the hash element referenced by the current value of $key references. The end result is a hash that contains the Account Policy settings before you make any changes.
The if defined statement that follows the foreach loop determines whether the %AccountPolicy hash has a value. Recall that the command-line argument loop at A created the %AccountPolicy hash and corresponding key > value pairs for only those options you specified on the command line. If you fail to provide a valid Account Policy argument, the script doesn't create a %AccountPolicy hash, which causes the if defined statement to fail. As a result, the script sends the message No change(s) specified via command line. Printing active account policy with the target domain's Account Policy to STDOUT. The script then exits, and Perl destroys the $oDomain object reference.
If you specify at least one valid Account Policy argument, the script creates an %AccountPolicy hash, satisfying the if defined statement. The script then updates the cached Account Policy properties it retrieved earlier. This time around, the foreach loop traverses the keys in the %AccountPolicy hash. Through each iteration, the script assigns a hash key name to the $key scalar and uses the $key scalar to update the appropriate $oDomain object property with the new value from the %AccountPolicy hash. At this point, the script updates only the locally cached copies of the $oDomain object's properties, not the underlying directory.
To write the changes to the directory, you need to invoke the SetInfo method. You then fetch the updated values using the same approach you used at B, storing the values in the %NewAccountPolicy hash.
You use the old and new hashes to print the changes to STDOUT. The code at C in Listing 3 produces output similar to Screen 1. Screen 2 shows the resulting changes made to the target domain's Account Policy. The script then exits, and Perl destroys the $oDomain object reference.
Not all the Account Policy properties behave as you might expect. The AcntPlcy.pl script on Windows NT Magazine's Web site identifies the properties you need to be aware of in the script's usage instructions. In addition, you might want to check out the Win32::OLE module, which features other methods and functions that I didn't discuss here. The module is part of ActiveState Tool's ActivePerl software. You can find information about this module in the online HTML documentation included with the ActivePerl distribution at http://www.activestate.com.
A New Frontier
Although command-line utilities will continue to play an important role in scripting, the applications and components that expose automation interfaces represent a new frontier for administrators of automated systems. Furthermore, automation represents an opportunity for independent software vendors looking for ways to enhance and extend their applications' functionality. Imagine if you could access and control your backup and systems management software from OLE-capable scripting languages. You just might find yourself automating tasks you never thought possible.