Link to home
Start Free TrialLog in
Avatar of kgbsd
kgbsd

asked on

Need a free utility or script to find old computers in AD

I need a free utility or script that will find old computers that are still listed in AD.  I have a ton that are not used, but don't want to sit and ping each machine to see if it is online.  Thanks
SOLUTION
Avatar of elbereth21
elbereth21
Flag of Italy image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Chris Dent

I don't if it's good enough for general release, but I wrote something to do it. Unfortunately it requires you to feed it a list of computers (export from AD) so it has something to test. I haven't had time to try and make that kind of functionality automatic.

To run it you need Perl from www.activestate.com, and Net::DNS module (which doesn't ship with Active State's version). Provided you have access through the filewall you can use Perl Package Manager to get it, type ppm at the command prompt then (I think):

search net::dns
install net::dns

Beyond that, run it from the command prompt with perl ComputerScanner.pl

The rest of this message contains the code needed to run it (which should be copied to a text file ComputerScanner.pl), the comments that top show exactly what the script does:

#!/usr/bin/perl

# Computer Scanner Version 3.1.0
#
# Author: Chris Dent
# Last Modified: 29/09/2004
#
# Description:
#
# This is a basic network scanner. It does the following:
# 1. Reads in a list of Computer Names (tab delimited export from AD will do)
# 2. Pings each Computer Name to find out if its running
# 3. Runs a DNS Lookup on all running Computers to pull out the IP Address
# 4. Attempts to connect to the WMI and gather the UserName, Operating System
# and Computer Name
# 5. Checks for some common services (Web, Mail, FTP, etc)
# 6. Dumps it all out to a ; delimited text file called hosts.txt which can be
# imported into Excel
#
# NOTE: The DNS Resolver Module used in this script can only use DNS Server
# addresses assigned by DHCP (Updated Module (0.48) is untested for this bug)

# Included Modules

use Net::Ping;
use Net::DNS;
use IO::Socket;
use Win32::OLE qw(in);

# Syntax Pragma

use strict;

# Debugging Options

use warnings;
use diagnostics;

# Global Variables ("my" for variables only in the main code, "our" for global)

my $InputFile = "computers.txt";
my $OutputFile = "hosts.txt";

our (@ComputerList, @Input, @Ports);
our (%PortList);
our ($Host, $PingHost, $NSLookup, $Address, $PortName, $Port, $Result);
our ($WMIUserName, $WMIOSName, $WMIComputerName);

# This is the port list, required syntax is portnumber:portname:

my $List = "21:FTP:23:Telnet:25:SMTP:80:HTTP:443:HTTPS:";

# Functions and Subroutines

sub PingHost

# Pings the host by name and returns True or False

{
      my $PingObject = Net::Ping->new("icmp");
      if ($PingObject->ping($Host, 2))
      {
            $PingHost = 1;
      }
      else
      {
            $PingHost = 0;
      }
}

sub NSLookup

# Perform DNS Lookup for computer name - if it can't find an IP
# lookuperror is used to stop WMI Connection and PortScan attempts

{
      my $Resolver = Net::DNS::Resolver->new;
      my $Query = $Resolver->search("$Host");
      if ($Query)
      {
            foreach my $Record ($Query->answer)
            {
                  next unless $Record->type eq "A";
                  $Address = $Record->address;
                  $NSLookup = 1;
            }
      }
      else
      {
            $NSLookup = 0;
      }
}

sub GrabWindowsData

# Connect to the Windows Management Instrumentation and try to grab
# Current User and OS Name

{
      my $WMIServices = Win32::OLE->GetObject("winmgmts:{impersonationLevel=impersonate,(security)}//$Host");
      my $ComputerSystem = $WMIServices->instancesof("Win32_ComputerSystem");
      foreach my $Data (in($ComputerSystem))
      {
            $WMIUserName = $Data->{UserName};
      }
      my $OperatingSystem = $WMIServices->instancesof("Win32_OperatingSystem");      
      foreach my $Data (in($OperatingSystem))
      {
            my $OSName = $Data->{Name};
            my @OSData = split(/\|/, $OSName);
            $WMIOSName = $OSData[0];
            $WMIComputerName = $Data->{CSName};
      }
}

sub PortScan

# Attempt to create a socket connection to the ports in PortList

{
      my $Sock = IO::Socket::INET->new("$Host:$Port");
      if ($Sock)
      {
            $Result = 'Open';
      }
      else
      {
            $Result = 'Closed';
      }
}

sub CleanData

# Sub to Sort the Computer List from the original file data
# Requires extending to deal with multiple formats

{
      my $Counter = 0;
      
      foreach my $Line (@Input)
      {
            my @ComputerElement = split(/\t/, $Line);
            if ($ComputerElement[0] ne "\n")
            {
                  $ComputerList[$Counter] = $ComputerElement[0];
                  $Counter = ++$Counter;
            }
      }
}

sub ByNumber

# This sorts the elements in the hash (by port number in this case)
# Just makes the port scan run in numerical order

{
      if ( $a < $b )
      {
            -1
      }
      elsif ( $a > $b )
      {
            1
      }
      else
      {
            0
      }
}

# Main Script begins here

# Open Input File and Read Contents

open(INPUTFILE, "$InputFile") or die("Can't open $InputFile: $!");
@Input = <INPUTFILE>;
close(INPUTFILE);

# Open Output File for Writing (>) - a nice character delimited list
# Locks the file for exclusive access (you can't see it until it's done)

open(OUTPUTFILE, ">$OutputFile") or die("Can't open $OutputFile: $!");
flock(OUTPUTFILE, 2);

my $Resolver = Net::DNS::Resolver->new;
my @NameServers = $Resolver->nameservers;

print OUTPUTFILE "Using DNS Servers: @NameServers;\n";

print OUTPUTFILE "Host;Ping;NS Lookup;WMI: OS;WMI: Username;WMI: Computer Name;Port Scan;\n";

# Data cleaning - assumes data is tab delimited (default export from AD)
# or just a simple list, either way, it's going to chop off everything
# except the computer name

CleanData();

# Skip the first line - default export contains column names

shift(@ComputerList);

# Dig out lots of information

foreach $Host (@ComputerList)
{
      chomp($Host);
      print "$Host";
      print OUTPUTFILE "Ping Name: $Host;";
      
      PingHost();
      if ($PingHost == 1)
      {
            print OUTPUTFILE "Ping Succeeded;";
      }
      else
      {
            print OUTPUTFILE "Ping Failed;";
      }
      
      NSLookup();
      if ($NSLookup == 1)
      {
            print OUTPUTFILE "NSLookup Succeeded: $Address;";
      }
      else
      {
            print OUTPUTFILE "NSLookup Fail;";
      }
      
      # No point in doing the rest if we can't get an IP address so...
      
      if ($NSLookup == 1 and $PingHost == 1)
      {
            eval { GrabWindowsData(); };
            if ($@)
            {
                  print ": WMI could not establish a connection";
                  print OUTPUTFILE ";;;"
            }
            else
            {
                  print OUTPUTFILE "$WMIOSName;";
                  print OUTPUTFILE "WMI Name: $WMIComputerName;";
                  print OUTPUTFILE "$WMIUserName;";
            }
            
            # Make the port list usable
      
            %PortList = split(/:/, $List);
            @Ports = sort ByNumber keys %PortList;
            
            foreach $Port (@Ports)
            {
                  PortScan();
                  if ($Result eq "Open")
                  {
                        $PortName = $PortList { $Port };
                        print OUTPUTFILE "$PortName: Open;";
                  }
                  else
                  {
                        $PortName = $PortList { $Port };
                        print OUTPUTFILE "$PortName: Closed;";
                  }
            }
      }
      elsif ($NSLookup == 1 and $PingHost == 0)
      {
            print ": Unable to find IP Address";
            print OUTPUTFILE ";;;";
      }
      else
      {
            print ": Unable to Ping Host";
      }
      print "\n";
      print OUTPUTFILE "\n";
}

close(OUTPUTFILE);
Avatar of kgbsd
kgbsd

ASKER

The link from Elbereth gathers all the computers, but isn't correct.  I had a few that were over 300 and I was able to ping them still.  So it doesn't look like it works correctly.  Don't have time to do the perl thing...  Any other ideas?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I used netpwage in my own domain, I think it works well and pretty fast; you only need to be careful with laptops, but since I think you know perfectly well what to look for, among your machines, this shouldn't be much of a problem.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of kgbsd

ASKER

I tried the oldcmp utility to the same effect.  Pulls up a lot of machines, but some are still pingable even though it says they are really old...  Maybe it is a lost cause on this one..
Did you try also netPWage?
the term OLD in the AD lingo means... the computers didn't change their password in last "X" days....

on this criteria... oldcmp... finds the "OLD COMPUTERS".... :-)

and since, by default every computer in the domain is required to change their password in i guess. 30 days...
you can safely assume, computers who didn't change their password in say 90 days are dead... respect to Domain...

so generally, machine which are renamed or scraped their computer accounts would have higher value for "password last changed on date" value...

But, I guess, in your case, the machines are just taken out of domain... so they keep the same name but, don't change their password on "computer accounts" in domain... giving high value for "password last changed on date"...

I hope this clears something...