From STLPM
# 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.