Альтернатива 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