Securing Computers and Active Directory - Finding Inactive or Unused Computers
(Page 3 of 4 )
Problem
You want to find inactive computer accounts in a domain.
Solution
These solutions might only apply to Windows-based machines. Other types of machines—e.g., Unix, Mac, Network Attached Storage (NAS)—that have accounts in Active Directory might not update their login timestamps or passwords, which are used to determine inactivity.
Using a command-line interface
The following query will locate all inactive computers in the current forest:
> dsquery computer forestroot -inactive <NumWeeks>
You can also usedomainrootin combination with the
-doption to query a specific domain:
> dsquery computer domainroot -d <DomainName> -inactive <NumWeeks>
or you can target your query at a specific container:
> dsquery computer ou=MyComputers,dc=rallencorp,dc=com
-inactive <NumWeeks>
These commands can only be run against a Windows Server 2003 domain functional level or higher domain.
You can also use the OldCmp joeware utility to create a report of all computer accounts whose passwords are older than a certain number of days (90 by default) by using the following syntax:
> oldcmp -report
To specify an alternate password age witholdcmp, use the–age x switch. You can also use the–lltsswitch to use thelastLogonTimeStampattribute to perform the age calculations. (Without this switch,oldcmpwill usepwdLastSetby default.)
Using Perl
#!perl
#----------------------
# Script Configuration
#----------------------
# Domain and container/OU to check for inactive computer accounts
my $domain = 'amer.rallencorp.com';
# set to empty string to query entire domain
my $computer_cont = 'cn=Computers,';
# Number of weeks used to find inactive computers
my $weeks_ago = 30;
#----------------------
# End Configuration
#----------------------
use strict;
use Win32::OLE;
$Win32::OLE::Warn = 3;
use Math::BigInt;
# Must convert the number of seconds since $weeks_ago
# to a large integer for comparison against lastLogonTimestamp
my $sixmonth_secs = time - 60*60*24*7*$weeks_ago;
my $intObj = Math::BigInt->new($sixmonth_secs);
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $sixmonth_int = Math::BigInt->new(
$intObj->badd('116 444 736 000 000 000'));
$sixmonth_int =~ s/^[+-]//;
# Set up the ADO connections
my $connObj = Win32::OLE->new('ADODB.Connection');
$connObj->{Provider} = "ADsDSOObject";
$connObj->Open;
my $commObj = Win32::OLE->new('ADODB.Command');
$commObj->{ActiveConnection} = $connObj;
$commObj->Properties->{'Page Size'} = 1000;
# Grab the default root domain name
my $rootDSE = Win32::OLE->GetObject(LDAP://$domain/RootDSE);
my $rootNC = $rootDSE->Get("defaultNamingContext");
# Run ADO query and print results
my $query = "<LDAP://$domain/$computer_cont$rootNC>;";
$query .= "(&(objectclass=computer)";
$query .= "(objectcategory=computer)";
$query .= "(lastlogontimestamp<=$sixmonth_int));";
$query .= "cn,distinguishedName;";
$query .= "subtree";
$commObj->{CommandText} = $query;
my $resObj = $commObj->Execute($query);
die "Could not query $domain: ",$Win32::OLE::LastError,"\n"
unless ref $resObj;
print "\nComputers that have been inactive for $weeks_ago weeks or more:\n";
my $total = 0;
while (!($resObj->EOF)) {
my $cn = $resObj->Fields(0)->value;
print "\t",$resObj->Fields("distinguishedName")->value,"\n";
$total++;
$resObj->MoveNext;
}
print "Total: $total\n";
Discussion
Using a command-line interface
The dsquery computer command is very handy for finding inactive computers that have not logged in to the domain for a number of weeks or months. You can pipe the results of the query to the dsrm command-line utility if you want to remove the inactive computer objects from Active Directory in a single command. Here is an example that would delete all computers in the current domain that have been inactive for 12 weeks or longer:
> for /F "usebackq" %i in (`dsquery computer domainroot -inactive 12`) do dsrm
%i
You can also use OldCmp to disable inactive accounts, and then either delete them or move them to an alternate OU. OldCmp has a number of safeties built into the utility to prevent you from deleting a large number of computer accounts without meaning to. For example, OldCmp will not delete an account unless it has first been disabled, it will not modify more than 10 objects at a time unless you manually specify a higher limit, and it simply will not do anything at all to a domain controller computer account under any circumstances. Unless you have a requirement for quickly removing unusedcomputer objects, we’d recommend allowing them to remain inactive for at least three months before removing them. If you don’t really care when the objects get removed, use a year (i.e., 52 weeks) to be on the safe side.
Using Perl
With Windows 2000 Active Directory, the only way you can determine if a computer is inactive is to query either the pwdLastSet or lastLogon attributes. The pwdLastSet attribute is a 64-bit integer that translates into the date and time the computer last updated its password. Since computers are supposed to change their password every 30 days, you could run a query that finds the computers that have not changed their password in several months. This is difficult with VBScript because it does not handle 64-bit integer manipulation very well. There are third-party add-ons that provide 64-bit functions, but none of the built-in VBScript functions can do it and it is nontrivial to implement without an add-on.
ThelastLogonattribute can also be used to find inactive computers because that attribute contains a 64-bit integer representing the last time the computer logged into the domain. The problem with thelastLogonattribute is that it is not replicated. Since it is not replicated, you have to query every domain controller in the domain to find the most recentlastLogonvalue. As you can imagine, this is less than ideal, especially if you have a lot of domain controllers.
Fortunately, in Windows Server 2003, Microsoft added a new attribute calledlastLogonTimestamptouserandcomputerobjects. This attribute contains the approximate last logon timestamp (again in a 64-bit, large-integer format) for the user or computer and is replicated to all domain controllers. It is the approximate last logon because the domain controllers will update the value only if it hasn’t been updated for a certain period of time (such as a week). This prevents the attribute from being updated constantly and causing a lot of unnecessary replication traffic.
Since VBScript was out of the question, we turned to our first love—Perl. It is very rare to find a problem that you can’t solve with Perl and this is no exception. The biggest issue is manipulating a number to a 64-bit integer, which we can do with theMath::BigIntmodule.
First, we determine the time in seconds from 1970 for the date that we want to query computer inactivity against. That is, take the current time and subtract the number of weeks we want to go back. Then we have to convert that number to a big integer. The last step is simply to perform an ADO query for all computers that have alastLogonTimestampless than or equal to the value we just calculated.
See Also
Recipe 6.31 for finding users whose accounts are about to expire
Next: Changing the Maximum Number of Computers a User Can Join to the Domain >>
More Windows Scripting Articles
More By O'Reilly Media
|
This article is excerpted from chapter eight of the Active Directory Cookbook, Second Edition, written by Robbie Allen and Laura E. Hunter (O'Reilly; ISBN: 059610202X). Check it out today at your favorite bookstore. Buy this book now.
|
|