Альтернатива Win32_Product?

Так так. Поиграв с запросом Win32_Product, чтобы найти версию программного обеспечения, я не мог понять, почему результаты были такими медлительными. Целых 15 раз медленнее, чем запросы Win32_service или Win32_process. Итак, приехав сюда, чтобы посмотреть, пропустил ли я что-то, я обнаружил, что другие сообщали о той же проблеме, и в этой статье объясняется, почему.

Наиболее часто предлагаемой альтернативой для поиска установленного программного обеспечения является запрос записи в реестре или три. Это было мое первое решение, за исключением того, что моя компания еще не перешла на настройку серверов для приема PSRemoting. Любые запросы reg просто возвращают ошибки аутентификации Kerberos. Я могу включить PSRemoting на отдельных серверах, но моя команда поддерживает системы 30K. Так что это решение вышло.

В итоге мы обновляем Symantec Endpoint Protection с версии 11 до версии 12, и я хочу просто проверить, какая версия установлена ​​на сервере. Есть ли альтернативы, чтобы найти версию, отличную от Win32_Product и запросов реестра?

4 ответа

Решение

Я использую реестр удаленно, без PSRemoting. Вот функция, которую я написал и ежедневно использую для запроса программного обеспечения.

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's 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()
    }
}  

}

Это Get-RemoteSoftware отлично работает - при условии, что в удаленной системе запущена служба удаленного реестра. Если нет, вы получите ошибку. Я всегда проверяю, запущено ли это, и запускаю, если нет, а затем останавливаю, когда это сделано. Интересно, вот почему в противном случае отличная функция получила отрицательные голоса.

Вот слегка измененная версия, которая будет проверять и запускать удаленную службу реестра, а затем останавливаться по завершении.

Function Get-WmiCustom2([string]$computername,[string]$namespace,[string]$class,[int]$timeout=15,[string]$whereclause='') 
{
#Function Get-WMICustom2 by MSFT's 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's 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
    }
}
}  
}

Это использует пользовательскую оболочку получения WMI, написанную кем-то из MSFT, поэтому, если этот фрагмент полностью скопирован, он будет работать как есть. Вы можете изменить его обратно на стандартную функцию get-wmiobject, но для этого нет встроенного времени ожидания. В некоторых [не во всех редких] ситуациях удаленный ответчик WMI будет зависать бесконечно с WMI по умолчанию, поэтому это добавляет время ожидания. -Dane

Там на самом деле продолжение Эй! Статья для сценаристов Используйте PowerShell для поиска установленного программного обеспечения, в котором обсуждаются другие более эффективные способы получения этой информации. Вкратце, используйте одну из двух команд:

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

Я бы порекомендовал прочитать эту статью о сценариях, чтобы узнать, почему Win32_Product плох, а также есть альтернативы. Я обычно использую Win32Reg_AddRemovePrograms, так как мы используем SCCM, который устанавливает этот класс. Если вы не используете SCCM, используйте запрос в реестре, такой как @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
Другие вопросы по тегам