Category: | NT Admin |
Author/Contact Info | BravoTwoZero |
Description: | We needed a way to find and sort all domain users by account status (enabled, disabled, etc) and/or last logoff time. It could be cleaner, simpler and take less time to run (45 minutes with 7 domain controllers). This takes the logon, adds details from Exchange and calculates the most recent last logoff time before dumping the whole mess to stdout as a comma-delimited list. The only variable you need to change is the $ldapsrv scalar near the beginning. Everything else will (hopefully) just work. Good luck... hope it helps... muchos gracias to everyone who ever wrote something related... I'm sure I looked at it a dozen times... feel free to improve it! 2004-08-25: Added pdc/bdc lookup (so you don't have to manually change the array) and fixed a problem with the headers. Thanks to Marza's domain disk space check program for the pointer to the obvious routine for adding the pdc and bdcs generically. 2004-11-16: Tweaked a lot of little stuff
|
#!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"; } |