268 lines
8.4 KiB
Perl
268 lines
8.4 KiB
Perl
use Net::LDAP;
|
|
use Net::Ping;
|
|
use Date::Calc 'Add_Delta_DHMS';
|
|
use Getopt::Std;
|
|
#use POSIX;
|
|
|
|
sub usage{
|
|
print <<EOF;
|
|
|
|
"cleanworkstations.exe" will list or delete the older worstation accounts (workstations should connect to a DC once a week so 13 days would be the max time you should be out of contact with a workstation.
|
|
|
|
ex: cleanworkstations.exe -d domain -l ou -b base -y days -u username -p password -x -h
|
|
|
|
Where:
|
|
domain: Name of the closest Domain Controller to the machine
|
|
the script is being run from. This will get a list
|
|
of all DC's as well as use this machine to authenticate
|
|
from and delete from if you choose to purge accounts.
|
|
(Default is dc-00.mathworks.com)
|
|
*ou: Name of the OU to delete records from. ex... "AH"
|
|
Only required if no base is defined.
|
|
(no default defined)
|
|
*base: Alternate base DN: Defaults to
|
|
"ou=workstations,ou=$location,dc=ad,dc=mathworks,dc=com"
|
|
You will get an error if you don't define the base and
|
|
also don't provide a location OU.
|
|
*x: Flag to tell program if it should delete all accounts
|
|
over #days old otherwise it will just list accounts
|
|
that would have been deleted (Default is 30)
|
|
*days: Number of days since the machine has been seen that
|
|
will be up on the report or be deleted (30 day default)
|
|
!username: The admins netbios username that has rights to delete
|
|
accounts. (Only used if you are deleting accounts)
|
|
!password: Coresponding password for the administrator running
|
|
the script. (Only used if you are deleting accounts)
|
|
*h: Prints this help file
|
|
|
|
All *'s are optional and will be have the correct defaults for each domain
|
|
if left out.
|
|
All !'s are optional and are only used if you are going to programatically
|
|
delete accounts.
|
|
EOF
|
|
exit( 1 );
|
|
|
|
}
|
|
|
|
getopts( 'xhl:d:y:b:u:p:' );
|
|
my $delete = $Getopt::Std::opt_x;
|
|
my $location = $Getopt::Std::opt_l;
|
|
my $domain = ( length( $Getopt::Std::opt_d ) ) ? $Getopt::Std::opt_d : "dc-00.mathworks.com";
|
|
my $base = ( length( $Getopt::Std::opt_b ) ) ? $Getopt::Std::opt_b : "ou=workstations,ou=$location,dc=ad,dc=mathworks,dc=com";
|
|
my $days = ( length( $Getopt::Std::opt_y ) ) ? $Getopt::Std::opt_y : 30;
|
|
my $username = $Getopt::Std::opt_u;
|
|
my $password = $Getopt::Std::opt_p;
|
|
my $help = $Getopt::Std::opt_h;
|
|
|
|
chomp $domain;
|
|
chomp $location;
|
|
$domain = lc($domain);
|
|
$location = lc($location);
|
|
if (( $help ) || (((!length($location)) && ($base eq "ou=workstations,ou=$location,dc=ad,dc=mathworks,dc=com") )))
|
|
#if (( $help ) || (((!length($location)) && ($base = "ou=workstations,ou=,dc=ad,dc=mathworks,dc=com")))
|
|
{
|
|
usage();
|
|
exit( 1 );
|
|
};
|
|
|
|
print "\nDC = $domain\nWorkstation OU = $location\nNumber Days = $days\nDelete? = $delete\n\n";
|
|
|
|
# I think this can be 1 hour off due to changes in GMT over daylight savings etc...
|
|
#$changefromNTepoch = 11644473600; #diference between 1601 and 1970 in seconds
|
|
$changefromNTepoch = 116444736000000000; #diference between 1601 and 1970 in 100 milliseconds
|
|
$time = time;
|
|
$dc= $domain;
|
|
$~ = OutputFormat;
|
|
$^ = OutputFormat_Top;
|
|
$= = 9999;
|
|
$outfile = "expiredworkstations.$location.txt";
|
|
|
|
print "Retrieving list of domain controllers";
|
|
getDCs();
|
|
|
|
print "\nComplete list of domain controllers obtained from $dc:\n";
|
|
foreach $DC ( sort @DCs ) { print "$DC\n";};
|
|
|
|
print "\nQuerying domain controllers (Can take a while - one tick per DC)";
|
|
foreach $DC ( sort @DCs ) {
|
|
print ".";
|
|
if (ping($DC)) {
|
|
# my $base = "ou=workstations,ou=$location,dc=ad,dc=mathworks,dc=com";
|
|
my $search = "(&(objectCategory=computer))";
|
|
|
|
my $ldap = new Net::LDAP("$DC", port=>389) or die "$@";
|
|
$ldap->bind(version=>3);
|
|
my $msg = $ldap->search(
|
|
base => "$base",
|
|
filter=>"$search",
|
|
callback => \&callback);
|
|
|
|
die $msg->error if $msg->code;
|
|
|
|
sub callback {
|
|
my ($search, $entry) = @_;
|
|
$entry = $search->shift_entry;
|
|
my @results=();
|
|
if (defined $entry and $entry->get_value("cn") and $entry->get_value("lastLogon")) {
|
|
$cn = $entry->get_value("cn");
|
|
$lastlogon = $entry->get_value("lastLogon");
|
|
push @output, "$cn,$lastlogon,$DC";
|
|
}
|
|
}
|
|
$ldap->unbind;
|
|
}
|
|
else { print "\n\nCannot access $dc!!!! Exiting program, will not be accurate unless all dc\'s are accessible.\n\n"; exit }
|
|
}
|
|
print "\n\n";
|
|
|
|
foreach $line ( sort @output) {
|
|
my @data = split/,/, $line;
|
|
my @pdata = split/,/, $pline;
|
|
|
|
if (@data[0] eq @pdata[0] && @data[1] > @pdata[1]) {
|
|
pop @outputdata;
|
|
push @outputdata, "@data[0],@data[1],@data[2]";
|
|
}
|
|
if (@data[0] ne @pdata[0]) {
|
|
push @outputdata, "@data[0],@data[1],@data[2]";
|
|
}
|
|
|
|
$pline = $line;
|
|
}
|
|
|
|
open (outfile,">$outfile") || die "Can't open output file: $!\n"; # open results to print out
|
|
|
|
if ($delete) {
|
|
|
|
# bind to DC anon and look up DN of admin running delete option
|
|
|
|
$ldap = new Net::LDAP("$domain", port=>389) or die "$@";
|
|
$ldap->bind( version => 3 );
|
|
my $search = "(&(cn=$username)(objectClass=user))";
|
|
my $attrib = "distinguishedName";
|
|
|
|
my $result = $ldap->search(base=>"dc=ad,dc=mathworks,dc=com", filter=>"$search",attrs=>"$attrib");
|
|
die $result->error if $result->code;
|
|
|
|
my @entries = $result->entries;
|
|
foreach $entry (@entries) { @dn = $entry->get(distinguishedName);};
|
|
$admindn= @dn[0];
|
|
print "\nAdminDN:$admindn\n";
|
|
$ldap->unbind;
|
|
|
|
# Rebind as admin in order to delete objects
|
|
|
|
$ldap = new Net::LDAP("$domain", port=>389) or die "$@";
|
|
$ldap->bind( version => 3,
|
|
dn => $admindn,
|
|
password => $password,
|
|
);
|
|
};
|
|
|
|
foreach $line ( sort @outputdata) {
|
|
my @data = split/,/, $line;
|
|
$netbiosname = @data[0];
|
|
$lastloggedon = dateconv(@data[1]);
|
|
$rawdate = @data[1];
|
|
$logondc = @data[2];
|
|
$cutofftime = $days*24*60*60;
|
|
$age = ($time - (($data[1]-$changefromNTepoch)/10000000));
|
|
if ( $age > $cutofftime )
|
|
{
|
|
$kill = "Delete";
|
|
$fulldn = "cn=$netbiosname,$base";
|
|
if ($delete)
|
|
{
|
|
# delete record from AD
|
|
$ldap->delete( $fulldn ) or die "$@";
|
|
}
|
|
write;
|
|
write outfile;
|
|
}
|
|
else {
|
|
write;
|
|
};
|
|
$kill = "";
|
|
};
|
|
|
|
close (outfile);
|
|
if ($delete ) {$ldap->unbind;};
|
|
|
|
sub ping {
|
|
my ($host) = @_;
|
|
my $p = Net::Ping->new("icmp");
|
|
return 1 if ($p->ping($host, 2));
|
|
return 0;
|
|
}
|
|
|
|
sub getDCs {
|
|
my $base = "ou=domain controllers,dc=ad,dc=mathworks,dc=com";
|
|
my $search = "(&(objectCategory=computer))";
|
|
|
|
my $ldap = new Net::LDAP("$dc", port=>389) or die "$@";
|
|
$ldap->bind(version=>3);
|
|
my $msg = $ldap->search(
|
|
base => "$base",
|
|
filter=>"$search",
|
|
callback => \&dccallback);
|
|
|
|
die $msg->error if $msg->code;
|
|
|
|
sub dccallback {
|
|
my ($search, $entry) = @_;
|
|
$entry = $search->shift_entry;
|
|
my @results=();
|
|
if (defined $entry and $entry->get_value("cn")) {
|
|
print ".";
|
|
$cn = $entry->get_value("cn");
|
|
push @DCs, "$cn";
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
sub dateconv {
|
|
my ($vt_filetime) = @_;
|
|
|
|
# Disregard the 100 nanosecond units (rounding might be better)
|
|
$vt_filetime = substr($vt_filetime, 0, 11);
|
|
|
|
my $days = int( $vt_filetime / (24*60*60) );
|
|
my $hours = int( ($vt_filetime % (24*60*60)) / (60*60) );
|
|
my $mins = int( ($vt_filetime % (60*60)) / 60 );
|
|
my $secs = $vt_filetime % 60 ;
|
|
|
|
my @date = Add_Delta_DHMS(1601, 1, 1, 0, 0, 0, $days, $hours, $mins, $secs);
|
|
|
|
$year = sprintf("%02d", @date[0]);
|
|
$month = sprintf("%02d", @date[1]);
|
|
$day = sprintf("%02d", @date[2]);
|
|
$hour = sprintf("%02d", @date[3]);
|
|
$minute = sprintf("%02d", @date[4]);
|
|
$second = sprintf("%02d", @date[5]);
|
|
|
|
return "$year-$month-$day $hour:$minute:$second";
|
|
}
|
|
|
|
|
|
format OutputFormat_Top =
|
|
Host Name Last Logon Logon DC delete?
|
|
-----------------------------------------------------------------
|
|
.
|
|
|
|
format OutputFormat =
|
|
@<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<< ^<<<<<<<<<<
|
|
$netbiosname $lastloggedon $logondc $kill
|
|
.
|
|
|
|
format outfile_TOP =
|
|
Host Name
|
|
---------------------------------------------------------------------------------------------------------------------
|
|
.
|
|
|
|
format outfile =
|
|
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<< ^<<<<<<<<<<
|
|
$fulldn $lastloggedon $logondc $kill
|
|
.
|
|
|