I happen to have a script which does exactly this.  But it is specific
to my organization.  I have trimmed it down and appended it (untested)
below; in theory, it reads hostnames from stdin and dumps each one's
software inventory.

I assume you are looking for the same stuff which shows up in
Add/Remove programs, right?  Look in the registry under
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/CurrentVersion/Uninstall.
You will see a bunch of subkeys there, some with recognizable names,
some without.  Ignore their names, and look for values under each
subkey called "DisplayName" and "DisplayVersion".  Or see the
get_all_installed() function below.

I also wanted to probe remotely for the vendor and serial number (or
Dell Service tag) of each machine.  See get_vendor_serial() below.  It
uses WMI, and it only works for systems running Win2k and above.

Although WMI does have a class (Win32_Product) which you can use to
enumerate installed programs, I did not use it because it only knows
about stuff installed by the Windows Installer service (.msi files).

Let me know if you need any help understanding the code.

 - Pat

======================================================================

use warnings;
use strict;
use Win32::OLE;
my %reg;
use Win32::TieRegistry (Delimiter => '/', TiedHash => \%reg);

# Warn but do not die if COM engine encounters any trouble.
Win32::OLE->Option ('Warn' => 1);

# Get all of the installed items on a machine.  That is, find all of
# the things which show up in "Add/Remove Programs".  Return a list of
# informational hashes, including the DisplayName and DisplayVersion
# (if any).
sub get_all_installed ($) {
    my ($s_key) = @_;

    my $uninstall_key =
        $s_key->{'Microsoft/Windows/CurrentVersion/Uninstall/'};

    defined $uninstall_key
        or die "Unable to open Uninstall key: $^E";

    my @results;

    foreach my $subkey_name ($uninstall_key->SubKeyNames) {
        my $subkey = $uninstall_key->{"$subkey_name/"};
        (exists $subkey->{'/DisplayName'}
         && exists $subkey->{'UninstallString'})
            or next;
        my $pkg = { };
        $pkg->{'DisplayName'} = $subkey->{'/DisplayName'};
        (exists $subkey->{'/DisplayVersion'})
            and $pkg->{'DisplayVersion'} = $subkey->{'/DisplayVersion'};

        push @results, $pkg;
    }

    return @results;
}

# Get the vendor and serial number of a host's BIOS, if possible.
# Return a pair of question marks if not.
sub get_vendor_serial ($) {
    my ($wmi) = @_;

    my ($vendor, $serial) = ('?', '?');
    if (defined $wmi) {
        # Get BIOS objects from WMI.  See
        # <http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_bios.asp>
        my @bioses = Win32::OLE::Enum->All
            ($wmi->InstancesOf ('Win32_BIOS'));
        my $bios = $bioses[0];
        my @enclosures = Win32::OLE::Enum->All
            ($wmi->InstancesOf ('Win32_SystemEnclosure'));
        my $enclosure = $enclosures[0];

        my $new_vendor = $bios->{'Manufacturer'};
        defined $new_vendor
            and $vendor = $new_vendor;
        my $new_serial = $bios->{'SerialNumber'};
        if (defined $new_serial && $new_serial =~ /\S/) {
            $serial = $new_serial;
        }
        else {
            $new_vendor = $enclosure->{'Manufacturer'};
            defined $new_vendor
                and $vendor = $new_vendor;
            foreach my $key ('SerialNumber', 'SMBIOSAssetTag') {
                $new_serial = $enclosure->{$key};
            defined $new_serial && $new_serial =~ /\S/
                and $serial = $new_serial;
            }
        }
    }
    return ($vendor, $serial);
}

sub os_version ($$) {
    my ($wmi, $s_key) = @_;

    my $ret;

    if (defined $wmi) {
        # We have a working WMI connection, so use it.
        my @oses = Win32::OLE::Enum->All
            ($wmi->InstancesOf ('Win32_OperatingSystem'));
        $ret = $oses[0]->{'Caption'};
    }
    else {
        my $base_ver =
            $s_key->{'Microsoft/Windows NT/CurrentVersion//CurrentVersion'};

        if ($base_ver eq '4.0') {
            $ret = 'Windows NT 4.0 (UNKNOWN)';
        }
        else {
            # Other versions of Windows should support WMI.
            die "Unrecognized base Windows version $base_ver";
        }
    }

    return $ret;
}

# Sample loop which reads host names and dumps their software
# inventory.
while (my $target = <>) {
    chomp $target;

    print "($target)\n";

    # Get a connection to the WMI service on the remote machine, if
    # possible.
    my $wmi  = Win32::OLE->GetObject ("WinMgmts://$target");

    my ($vendor, $serial) = get_vendor_serial ($wmi);
    print "--\nVendor: $vendor\nSerial: $serial\n";

    # Get handle to HKLM/Software key on remote system
    my $software_key = $reg{"//$target/HKEY_LOCAL_MACHINE/Software/"};

    print "\n", os_version ($wmi, $software_key), "\n\n";

    my @pkgs = get_all_installed ($software_key);

    foreach my $pkg (@pkgs) {
        my ($name, $version) =
            ($pkg->{'DisplayName'}, $pkg->{'DisplayVersion'});
        print "Name: $name  Version: $version\n";
    }

    print "--\n";
}
_______________________________________________
Perl-Win32-Admin mailing list
[EMAIL PROTECTED]
To unsubscribe: http://listserv.ActiveState.com/mailman/mysubs

Reply via email to