#!perl
#
# This script runs against the PDC to get a user list. Then, the user
+list
# array has a last logoff lookup to each DC as well as a lookup agains
+t
# your Exchange server. Yes, it's ugly and amateurish. It outputs a li
+st
# to stdout as a comma-delimited list, suitable for piping to a file f
+or
# sorting in the spreadsheet of your choice.
#
# You'll need:
# Win32::AdminMisc from www.roth.net
# Net::LDAP
# Win32::NetAdmin
#
# In my case, I stuck with AS 5.6 so the roth ppm install would work.
+And,
# because I've seen the question before, install the perl-ldap package
# with PPM3, not perldap.
#
# A word about last logoff times. They ain't gospel, or at least I hav
+e
# yet to convince myslef that they are. Only a domain logon counts in
+changing
# that date. Authentication for Exchange and other apps won't roll the
+ clock.
# And, by example, real road warriors may go weeks without a logon bec
+ause
# of the cached nt logon on their laptops.
#
# I've also added a column for password age. It is presented in second
+s,
# divided by 86400 to turn it into days and truncated to remove any
# decimals. I don't care if my password is 29.4 days old. I just care
+that it's
# 29 days old, because our auditors only care that it's 29 days old.
#
# As always, YMMV.
# But you knew the job was dangerous when you took it.
# Bok bok bok bok!
# (Superchicken... look it up, Fred)
#
#
#
use Win32::AdminMisc;
use Net::LDAP;
use Win32::NetAdmin;
# ldap variables... everything else is automatic
# don't forget to make this your ldap server's FQDN!
#
my $ldapsrv = 'your.exchangeserver.com';
my $ldapbase = 'c=US';
my @attrs = (
'title',
'department',
'physicalDeliveryOfficeName',
'Extension-Attribute-1' # see comment below
);
# The main reason for the LDAP lookup in our case is to tie an
# employee ID that we add as a custom attribute to our Exchange
# accounts. You can leave it alone, and the value will be null
# for you. The only side effect will be an empty column in your
# spreadsheet. Or, comment it out here and in the two other following
# locations (search for the comment EA1 to find them).
my $PDC;
my $domain = Win32::DomainName or die "Unable to obtain the domain nam
+e\n";
Win32::NetAdmin::GetDomainController("","",$PDC);
Win32::NetAdmin::GetServers($PDC,$domain,SV_TYPE_DOMAIN_CTRL,\@pdc);
Win32::NetAdmin::GetServers($PDC, $domain, SV_TYPE_DOMAIN_BAKCTRL, \@b
+dc);
my @dc = (@pdc, @bdc);
my $server = "\\\\$pdc[0]";
# EA1: Here's one... delete
# \"Emplid\", (including the following comma)
# if you don't want an empty column where my employee IDs would normal
+ly be
print "\"Logon\",\"Username\",\"Emplid\",\"Details\",\"Title\",\"Depar
+tment\",\"Office\",\"Status\",\"Last Logoff\",\"Password Age\"\n";
my %hash;
Win32::AdminMisc::GetUsers($server, "" , \@array)
or die "GetUsers() failed: $!";
my %months = (
"Jan" => "01",
"Feb" => "02",
"Mar" => "03",
"Apr" => "04",
"May" => "05",
"Jun" => "06",
"Jul" => "07",
"Aug" => "08",
"Sep" => "09",
"Oct" => "10",
"Nov" => "11",
"Dec" => "12"
);
@keys = sort {lc $a cmp lc $b} (@array);
foreach $key (@keys) {
my $logoff;
my $emplid;
my $title;
my $dept;
my $office;
my $ldap;
my $mesg;
$value = $key;
Win32::AdminMisc::UserGetAttributes(
$server,
$key,
$fullname,
$password,
$passwordAge,
$privilege,
$homeDir,
$comment,
$flags,
$scriptPath)
or die "UserGetAttributes() failed: $!";
# using Net::LDAP, not PerLDAP
my $filter = "(uid=$key)";
$ldap = Net::LDAP->new( $ldapsrv ) or die "$@";
$mesg = $ldap->bind ;
$mesg = $ldap->search(
base => $ldapbase,
filter => $filter,
attrs => @attrs
);
foreach $entry ($mesg->entries) {
# EA1: Here's another... comment out the line below if you don't want
+
# an empty column where my employee IDs would normally be
chomp( $emplid = $entry->get_value('Extension-Attribute-1') );
chomp( $title = $entry->get_value('title') );
chomp( $dept = $entry->get_value('department') );
chomp( $office = $entry->get_value('physicalDeliveryOfficeName') )
+;
}
$mesg->code;
$mesg = $ldap->unbind;
my $logoff = "0";
foreach $dc (@dc) {
Win32::AdminMisc::UserGetMiscAttributes("\\\\$dc", $key, \%Has
+h)
or die "UserGetMiscAttributes() failed: $!";
if ($Hash{USER_LAST_LOGON} gt $logoff) {
$logoff = $Hash{USER_LAST_LOGON};
}
}
$logoff = localtime($logoff);
@lf = split(" ", $logoff);
$yr = $lf[4];
$mo = $lf[1];
$da = $lf[2];
if (10 > $da) { $da = "0" . $da; }
$logoff = join("", $yr, $months{$mo}, $da);
$passwordAgeDay = sprintf("%.0f", $passwordAge/86400);
# EA1: Here's the last... delete
# \"$emplid\", (including the following comma)
# if you don't want an empty column where my employee IDs would normal
+ly be
print "\"$key\",\"$fullname\",\"$emplid\",\"$comment\",\"$title\",
+\"$dept\",\"$office\",";
if (513 =~ $flags) {
print "\"enabled\",";
} elsif (515 =~ $flags) {
print "\"!DISABLED\",";
} elsif (579 =~ $flags) {
print "\"!nochpw\",";
} elsif (66049 =~ $flags) {
print "\"!noexpw\",";
} elsif (66113 =~ $flags) {
print "\"!noexpw, nochpw\",";
} elsif (66115 =~ $flags) {
print "\"!DISABLED\",";
} else {
print "\"$flags\",";
}
print "\"$logoff\",\"$passwordAgeDay\"\n";
}
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.