Alternative to Win32_Product?

Good good. After playing around with the Win32_Product query to find the software version, I couldn't figure out why the results were so slow. Up to 15 times slower than Win32_service or Win32_process requests. So when I came here to see if I was missing something, I found that others had reported the same problem and this article explains why.

The most commonly suggested alternative to finding installed software is asking for a registry entry or three. This was my first decision, except that my company hadn't moved on to configure the servers to accept PSRemoting. Any reg requests just return Kerberos authentication errors. I can enable PSRemoting on individual servers, but my team supports 30K systems. So the solution doesn't work.

At the bottom line, we are upgrading Symantec Endpoint Protection from version 11 to version 12, and I need a simple check to see which version is installed on the server. Are there alternatives for finding a version other than Win32_Product and Registry queries?

+3


source to share


4 answers


I am using the registry remotely, without PSRemoting. Here's a function that I write and use on a daily basis to query software.

Function Get-RemoteSoftware{
<#
.SYNOPSIS 
Displays all software listed in the registry on a given computer.

.DESCRIPTION
Uses the SOFTWARE registry keys (both 32 and 64bit) to list the name, version, vendor, and uninstall string for each software entry on a given computer.

.EXAMPLE
C:\PS> Get-RemoteSoftware -ComputerName SERVER1
This shows the software installed on SERVER1.
#>

param (
    [Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
    # Specifies the computer name to connect to
    $ComputerName
)

Process {
    foreach ($Computer in $ComputerName)
    {
        #Open Remote Base
        $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)

        #Check if it got 64bit regkeys
        $keyRootSoftware = $reg.OpenSubKey("SOFTWARE")
        [bool]$is64 = ($keyRootSoftware.GetSubKeyNames() | ? {$_ -eq 'WOW6432Node'} | Measure-Object).Count
        $keyRootSoftware.Close()

        #Get all of they keys into a list
        $softwareKeys = @()
        if ($is64){
            $pathUninstall64 = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
            $keyUninstall64 = $reg.OpenSubKey($pathUninstall64)
            $keyUninstall64.GetSubKeyNames() | % {
                $softwareKeys += $pathUninstall64 + "\\" + $_
            }
            $keyUninstall64.Close()
        }
        $pathUninstall32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
        $keyUninstall32 = $reg.OpenSubKey($pathUninstall32)
        $keyUninstall32.GetSubKeyNames() | % {
            $softwareKeys += $pathUninstall32 + "\\" + $_
        }
        $keyUninstall32.Close()

        #Get information from all the keys
        $softwareKeys | % {
            $subkey=$reg.OpenSubKey($_)
            if ($subkey.GetValue("DisplayName")){
                $installDate = $null
                if ($subkey.GetValue("InstallDate") -match "/"){
                    $installDate = Get-Date $subkey.GetValue("InstallDate")
                }
                elseif ($subkey.GetValue("InstallDate").length -eq 8){
                    $installDate = Get-Date $subkey.GetValue("InstallDate").Insert(6,".").Insert(4,".")
                }
                New-Object PSObject -Property @{
                    ComputerName = $Computer
                    Name = $subkey.GetValue("DisplayName")
                    Version = $subKey.GetValue("DisplayVersion")
                    Vendor = $subkey.GetValue("Publisher")
                    UninstallString = $subkey.GetValue("UninstallString")
                    InstallDate = $installDate
                }
            }

            $subkey.Close()
        }
        $reg.Close()
    }
}  

      



}

+4


source


That Get-RemoteSoftware works great - if the remote registry service is running on the remote system. If not, you will receive an error message. I always check if it is running and start it if not, then stop it when done. I wonder if this is why the otherwise great function got down-votes.

Below is a small version that checks and starts the remote registry service and stops when finished.



Function Get-WmiCustom2([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15,[string]$whereclause='') 
{
#Function Get-WMICustom2 by MSFT Daniele Muscetta 
#This is a modified version to add where clause parameter, optional
#Original function: http://blogs.msdn.com/b/dmuscett/archive/2009/05/27/get_2d00_wmicustom.aspx

$ConnectionOptions = new-object System.Management.ConnectionOptions
$EnumerationOptions = new-object System.Management.EnumerationOptions
$timeoutseconds = new-timespan -seconds $timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $computername + "\" + $namespace

$Scope = new-object System.Management.ManagementScope $assembledpath, $ConnectionOptions

try {
    $Scope.Connect()
} catch {
    $result="Error Connecting " + $_
    return $Result 
}

$querystring = "SELECT * FROM " + $class + " " + $whereclause
$query = new-object System.Management.ObjectQuery $querystring
$searcher = new-object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope

trap { $_ } $result = $searcher.get()

return $result
}

Function Get-RemoteSoftware{
<#
.SYNOPSIS 
Displays all software listed in the registry on a given computer.

.DESCRIPTION
Uses the SOFTWARE registry keys (both 32 and 64bit) to list the name, version, vendor, and uninstall string for each software entry on a given computer.

.EXAMPLE
C:\PS> Get-RemoteSoftware -ComputerName SERVER1
This shows the software installed on SERVER1.
#>

param (
[Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
# Specifies the computer name to connect to
$ComputerName
)

Process {
    foreach ($Computer in $ComputerName)
    {
        $ChangeStateBack=$False
        $RemoteRegistryObj=""
        $ServiceWMIObj=@(get-wmicustom2 -class "win32_service" -namespace "root\cimv2" -whereclause "WHERE name='RemoteRegistry'" -computername $computername –timeout 60 -erroraction stop)
        if ($ServiceWMIObj.Count -gt 0) {
        $RemoteRegistryObj =  $ServiceWMIObj[0]
        if ($RemoteRegistryObj.State -ne 'Running') {
                $ChangeStateBack=$true
                $RemoteRegistryObj.InvokeMethod("StartService",$null) | Out-Null
                Start-Sleep -m 1800     
                #give it a chance to actually start. 1.5 second delay
        }
}
    #Open Remote Base
    $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)

    #Check if it got 64bit regkeys
    $keyRootSoftware = $reg.OpenSubKey("SOFTWARE")
    [bool]$is64 = ($keyRootSoftware.GetSubKeyNames() | ? {$_ -eq 'WOW6432Node'} | Measure-Object).Count
    $keyRootSoftware.Close()

    #Get all of they keys into a list
    $softwareKeys = @()
    if ($is64){
        $pathUninstall64 = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
        $keyUninstall64 = $reg.OpenSubKey($pathUninstall64)
        $keyUninstall64.GetSubKeyNames() | % {
            $softwareKeys += $pathUninstall64 + "\\" + $_
        }
        $keyUninstall64.Close()
    }
    $pathUninstall32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
    $keyUninstall32 = $reg.OpenSubKey($pathUninstall32)
    $keyUninstall32.GetSubKeyNames() | % {
        $softwareKeys += $pathUninstall32 + "\\" + $_
    }
    $keyUninstall32.Close()

    #Get information from all the keys
    $softwareKeys | % {
        $subkey=$reg.OpenSubKey($_)
        if ($subkey.GetValue("DisplayName")){
            $installDate = $null
            if ($subkey.GetValue("InstallDate") -match "/"){
                $installDate = Get-Date $subkey.GetValue("InstallDate")
            }
            elseif ($subkey.GetValue("InstallDate").length -eq 8){
                $installDate = Get-Date $subkey.GetValue("InstallDate").Insert(6,".").Insert(4,".")
            }
            New-Object PSObject -Property @{
                ComputerName = $Computer
                Name = $subkey.GetValue("DisplayName")
                Version = $subKey.GetValue("DisplayVersion")
                Vendor = $subkey.GetValue("Publisher")
                UninstallString = $subkey.GetValue("UninstallString")
                InstallDate = $installDate
            }
        }

        $subkey.Close()
    }
    $reg.Close()
    if ($ChangeStateBack){
                            $RemoteRegistryObj.InvokeMethod("StopService",$null)  | Out-Null
    }
}
}  
}

      

This uses a custom WMI wrapper that someone wrote in MSFT, so if this snippet is completely copied it will work as it is. You can change it back to the standard get-wmiobject function, but there is no timeout for that. In some [not all rare] situations, the WMI remote responder will hang indefinitely with the default WMI, so this adds a timeout. -Dane

+1


source


I would recommend reading this article for scriptwriters why Win32_Product is bad and alternatives. I usually use Win32Reg_AddRemovePrograms as we are using SCCM which installs this class. Unless you are using an SCCM stick with a registry query like the one posted by @Chris N.

PS:\>Measure-Command {gwmi win32reg_addremoveprograms}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 162
Ticks             : 1623758
TotalDays         : 1.87934953703704E-06
TotalHours        : 4.51043888888889E-05
TotalMinutes      : 0.00270626333333333
TotalSeconds      : 0.1623758
TotalMilliseconds : 162.3758

      

0


source


Actually there is a sequel Hey! Scripting Guys Use PowerShell to find installed software , which discusses other better ways to capture this information. In short, use one of two commands:

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
    Format-Table –AutoSize

      

Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
    Format-Table –AutoSize

      

-1


source







All Articles