LISTING 3: Excerpts from the AcntPlcy.pl Script

# AcntPlcy.pl
use Win32::OLE;
# If first command line argument contains a '?', call Usage() subroutine and exit.
($ARGV[0] =~ /\?/) and Usage();
# CALLOUT A
# Hashes used to store target domains Account Policy before and after
# changes. Note the key names represent the actual property names
# defined on the ADSI Domain object.
%OldAccountPolicy = %NewAccountPolicy = (MaxPasswordAge             => 0,
                                         MinPasswordAge             => 0,
                                         MinPasswordLength          => 0,
                                         PasswordHistoryLength      => 0,
                                         MaxBadPasswordsAllowed     => 0,
                                         AutoUnlockInterval         => 0,
                                         LockoutObservationInterval => 0);
                     
# CALLOUT B
# Initialize target domain scalar $strDomain and %AccountPolicy hash based on
# the command line arguments supplied to the script.
foreach (@ARGV) {
   (/^d=/i) and $strDomain = (split(/=/, $_, 2))[1];
   (/^A=/)  and $AccountPolicy{MaxPasswordAge}             = (split(/=/, $_, 2))[1];
   (/^a=/)  and $AccountPolicy{MinPasswordAge}             = (split(/=/, $_, 2))[1];
   (/^l=/i) and $AccountPolicy{MinPasswordLength}          = (split(/=/, $_, 2))[1];
   (/^h=/i) and $AccountPolicy{PasswordHistoryLength}      = (split(/=/, $_, 2))[1];
   (/^b=/i) and $AccountPolicy{MaxBadPasswordsAllowed}     = (split(/=/, $_, 2))[1];
   (/^u=/i) and $AccountPolicy{AutoUnlockInterval}         = (split(/=/, $_, 2))[1];
   (/^o=/i) and $AccountPolicy{LockoutObservationInterval} = (split(/=/, $_, 2))[1];
}
# If no target domain specified, fetch local domain name from USERDOMAIN environment
# variable. Note that if the current interactive user is not logged on to the domain,
# this value will contain the machine name which will cause GetObject to fail.
# GetObject is expecting a Domain name as indicated by the class identifier below
# because ADSI only supports changing the Account Policy on a domain. ADSI does NOT
# support changing the Account Policy on Member Servers and Workstations.
($strDomain) or ($strDomain = $ENV{USERDOMAIN});
# CALLOUT C
# Fetch a domain object.
$oDomain = Win32::OLE->GetObject("WinNT://$strDomain,Domain") or
           die "Unable to connect to Domain: $strDomain.\n";
# CALLOUT D
# Fetch target domains current Account Policy. Notice that we're using the hash
# key names to tell ADSI which property to fetch. Also be aware that this
# operation caches the information locally.
foreach $key (keys %OldAccountPolicy) {
   $OldAccountPolicy{$key} = $oDomain->Get($key);
}
# CALLOUT E
if(defined %AccountPolicy) {
   # Update the values according to the supplied command line arguments. Note that
   # this operation updates the cache only and not the underlying directory. It's
   # not until we invoke SetInfo below that the actual directory is updated.
   foreach $key (keys %AccountPolicy) {
      $oDomain->{$key} = $AccountPolicy{$key};
   }
   # CALLOUT F
   # Write changes back to the underlying directory.
   $oDomain->SetInfo;
   # Fetch the new Account Policy.
   foreach $key (keys %NewAccountPolicy) {
      $NewAccountPolicy{$key} = $oDomain->Get($key);
   }
   # Print updated Account Policy.
   print "The command completed successfully.\n\n".
         "Updated Account Policy for Domain: $strDomain (Before => After)\n".
         "--------------------------------------------------------------------\n";
   foreach $key (sort keys %NewAccountPolicy) {
      print "$key: $OldAccountPolicy{$key} => $NewAccountPolicy{$key}\n";
   }
} else {
   # No changes specified via command line. Print current Account Policy.
   print "No change(s) specified via command line. Printing active account policy.\n\n".
         "Active Account Policy for Domain: $strDomain\n".
         "-------------------------------------------------\n";
   foreach $key (sort keys %OldAccountPolicy) {
      print "$key: $OldAccountPolicy{$key}\n";
   }
}
exit(1);
sub Usage {
print <<USAGE;
Account Policy Usage
--------------------
Usage: c:\\> perl acntplcy.pl [d=TargetDomain] [A=MaxPasswordAge]
             [a=MinPasswordAge] [l=MinPasswordLength]
             [h=PasswordHistoryLength] [b=MaxBadPasswordsAllowed]
             [o=LockoutObservationInterval] [u=AutoUnlockInterval]
AcntPlcy.pl is an ActivePerl script that can be used to change an NT
Domains Account Policy. The script uses ActivePerl's OLE extension
and ADSI to make the change. As such, the script must be run on a
system with the ADSI 2.0 runtime installed. The script also assumes
the user has the necessary privileges to alter the account policy,
e.g. Administrator. Note that only Domains are supported; Member
Servers and Workstations are not due to a limitation in ADSI. The
script accepts the following optional command line arguments:
   d=   Target domain. Local (logged on) domain is the default.
   A=   MaxPasswordAge. 'Password Restrictions | Maximum Password
        Age' measured in number of seconds (1 day = 86400). Set this
        option to -1 to enable 'Password Never Expires'.
   a=   MinPasswordAge. 'Password Restrictions | Minimum Password
        Age' measured in number of seconds (1 day = 86400). Set this
        option to 0 to enable 'Allow Changes Immediately'.
   l=   MinPasswordLength. 'Password Restrictions | Minimum Password
        Length' measured in number of characters. Set this option to
        0 to enable 'Permit Blank Password'.
   h=   PasswordHistoryLength. 'Password Restrictions | Password
        Uniqueness' measured in number of passwords to remember. Set
        this option to 0 to enable 'Do Not Keep Password History'.
   b=   MaxBadPasswordsAllowed. 'Account lockout | Lockout after n
        bad logon attempts' where n represents the number of bad
        logons.
   o=   LockoutObservationInterval. 'Account lockout | Reset count
        after n minutes' specified in number of seconds (1 hour =
        3600).
   u=   AutoUnlockInterval. 'Account lockout | Lockout Duration |
        Duration n minutes' specified in number of seconds (1 hour =
        3600). Set this option to -1 to enable 'Lockout Duration |
        Forever (until admin unlocks)'.
   Notes:
   + If no Account Policy arguments are supplied, the script will
     print the target domains current account policy settings.
   + Setting MaxBadPasswordsAllowed, LockoutObservationInterval or
     AutoUnlockInterval to 0 will disable 'Account lockout'.
   Example:
   c:\\>perl acntplcy.pl d=Lab A=3628800 a=86400 l=6 h=5 b=5 o=1800 u=1800
   Sets the Lab domain to the NT default Account Policy settings where:
      A=3628800 sets MaxPasswordAge to 42 days.
      a=86400   sets MinPasswordAge to 1 day.
      l=6       sets MinPasswordLength to 6 characters.
      h=5       sets PasswordHistoryLength to remember 5 passwords.
      b=5       sets MaxBadPasswordAllowed to 5 bad logon attempts.
      o=1800    sets LockoutObservationInterval to 30 minutes.
      u=1800    sets AutoUnlockInterval to 30 minutes.
USAGE
   exit(0);
}