Win32::OLE and Active Directory

From STLPM

Jump to: navigation, search
# ad_connector.pl
# Created by Michele Berg with advice from Bill Odom
# michele.r.berg@gmail.com
#
# This will connect to Active Directory
# and return a record set of objects as
# specified in the ldap connection parameters
 
use warnings;
use strict;
 
use Win32::OLE;
 
# Call Carp::croak if a Win32::OLE error occurs.
# You can set lower warning levels to only log
# errors, or ignore them altogether.
#
$Win32::OLE::Warn = 3;
 
# Load the Active Directory constants to avoid
# "magic numbers"
#
use Win32::OLE::Const 'Active DS';
 
 
# Specify ldap connection parameters
#
my $domain = 'dc=subdomain,dc=your_company,dc=com';
 
my $ldap_base = '<LDAP://' . $domain . '>;';
 
# Specify what to filter on
#
my $filter = '(&'
    . '(objectclass=user)'      # return only objects of the user class
    . '(objectcategory=person)' # and of the subclass person
    . '(sn=Smith)'              # return all users with last name Smith 
    . ');';
 
# Specify the ldap attributes to return.  These are used for
# searching for subsets of users in the record set, not for
# manipulating data on user objects.  ADSPath is the only of
# these required if you'll be connecting to user objects
# directly...see below.
#
my $attributes  = 'ADSPath, userAccountControl,'
                 .'givenName, sn, displayName;';
 
# A scope of subtree will search the domain specified
# and all subdomains/containers/OUs.  Other options
# are onelevel and base.
#
my $scope  = 'subtree';
 
# Create the AD connection object and open it
#
my $cnxn_obj = Win32::OLE->CreateObject('ADODB.Connection');
$cnxn_obj->{Provider} = 'ADsDSOObject';
$cnxn_obj->Open;
 
# Create a command object, so we can set some extra parameters
# for the search.
#
my $cmd_obj = Win32::OLE->new('ADODB.Command');
 
# Assign the AD connection to this command object 
#
$cmd_obj->{ActiveConnection} = $cnxn_obj;
 
# Put together the components of the command
#
$cmd_obj->{CommandText}
    = $ldap_base . $filter . $attributes . $scope;
 
# Tell the command object to always chase referrals, so
# sub-domain searches will work. "Referrals" are what the AD
# returns when you ask it about objects that aren't within the
# current domain, like if you asked your_company.com about an
# object within subdomain.your_company.com.
#
$cmd_obj->Properties('Chase Referrals')->{Value}
    = ADS_CHASE_REFERRALS_ALWAYS;
 
# Set a page size, to convince the AD to return all the records.
# (If you don't do this, you only get back *some* of the
# records.) Notice that this doesn't set the limit of the number 
# of records returned by the query, but the number of records
# returned at a time.
#
$cmd_obj->Properties('Page Size')->{Value} = 1000;
 
# End Create connection parameters
 
# This would has created a record set of all existing AD accounts that
# match the specified filter, if the sn wasn't specified.  You could now 
# loop over the record set and manipulate the records according to 
# your needs.
#
my $rcd_set = $cmd_obj->Execute();
 
$rcd_set->MoveFirst;
 
 
while (not $rcd_set->EOF) 
{
    # Decide here what you want to do with this information
    # Print it to a file, select only users who match additional
    # criteria, make changes to the user object to save back
    # to AD, etc.
 
    # There are two methods of getting attributes from the returned 
    # objects
    #
    # Directly accessing the object hash...
    #
    my $path = uc $rcd_set->Fields('ADSPath')->Value;
 
    print "\$path set to: $path\n";
 
    # To use ADSI methods, you need to retrieve the specific object.
    # You'll also need to retrieve to save changes to the object
    # in the directory. (Otherwise you're just manipulating cached values.)
    #
    my $user_obj = Win32::OLE->GetObject($path);
 
    print "\$user_obj is a $user_obj\n";
 
    # Note that once you've retrieved the object, you can access 
    # its attributes as hash values...but it can still sometimes 
    # be tricky when dealing with bit flags like in this attribute.
    # Active Directory constants can help...
    #
    my $status;
 
    if ($user_obj->{'userAccountControl'} & (ADS_UF_ACCOUNTDISABLE)) 
    {
        $status = 'Disabled';
    } 
    else 
    {
        $status = 'Active';
    }
 
    print "\$status set to $status\n";
 
 
    # One ADSI method is Get...
    #
    my $first_name  = $user_obj->Get('givenName');
    my $last_name   = $user_obj->Get('sn');
 
    print "\$first_name and \$last_name set to $first_name and $last_name\n";
 
 
    # Modifying attributes of retrieved objects.
    #
    # You can directly change the attribute in the hash
    #
    $first_name =~ s/\s/\./g;
 
    $user_obj->{'sn'} = 'Doe';
 
    print "givenName and sn set to $first_name and $user_obj->{'sn'}\n";
 
    # Or using ADSI's "put" method
    #
    $user_obj->Put('displayName', "$user_obj->{'sn'}\, $first_name");
 
    print "\$displayName set to $user_obj->{'displayName'}\n";
 
    # The Put method can't be used for clearing an attribute; for that,
    # you need to use the PutEx method.
    #
    $user_obj->PutEx(ADS_PROPERTY_CLEAR,'displayName', '') 
        if ($status eq 'Disabled');
 
 
    # Don't forget to save those changes back to Active Directory!
    #
    eval {$user_obj->SetInfo()};
 
    if (Win32::OLE->LastError()) 
    {
        # Print a warning if there was an error saving
        #
        print "User object $user_obj->{samAccountName} not saved: "
            . Win32::OLE->LastError();
 
        # And clear the Win32::OLE error cache
        #
        Win32::OLE->LastError(0);
 
    }
 
    # Move through each object in the record set until EOF is reached
    #
    $rcd_set->MoveNext;  
}
 
 
# For more information on using ADSI, I really like the following books:
#    - Active Directory Cookbook, 
#       by Robbie Allen, Laura E. Hunter
#    - Active Directory, 
#        by Joe Richards, Robbie Allen, Alistair G. Lowe-Norris
#    - Perl for System Administration, 
#        by David N. Blank-Edelman (small but good ADSI section)
 
# Additionally, KB article 260251 on Microsoft's website provides 
#    some useful info on ADSI methods.
Personal tools