I have to update some products (MSI) using their upgrade codes, I have list of all such products' upgrade codes. Now to push updates I need to compare every product's version.
How to find the product version in such scenarios?
Like:
gwmi win32_product | Where-Object {$_.Name -like "name"}
But this uses the name, I would like to find the version using the upgrade code only.
The simplest way to accomplish what you're looking for in PowerShell is by using the following WMI query to grab packages that belong to an UpgradeCode family.
$UpgradeCode = '{AA783A14-A7A3-3D33-95F0-9A351D530011}'
$ProductGUIDs = @(Get-WmiObject -Class Win32_Property | Where-Object {$_.Property -eq 'UpgradeCode' -and $_.value -eq $UpgradeCode}).ProductCode
Get-WmiObject -Class Win32_Product | Where-Object {$ProductGUIDs -Contains $_.IdentifyingNumber}
The only downside to this it that both the Win32_Property and Win32_Product classes are slow, if time isn't a huge factor you can use that. If you need faster performance you can get similar information from the registry like this
function Decode-GUID {
param( [string]$GUID )
$GUIDSections = @( 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 )
$Position = 0
$result = @()
ForEach($GUIDSection In $GUIDSections)
{ $arr = $GUID.SubString($Position, $GUIDSection) -split "";
[array]::Reverse($arr);
$result = $result +($arr -join '').replace(' ','');
$Position += $GUIDSection }
return "{$(($result -join '').Insert(8,'-').Insert(13, '-').Insert(18, '-').Insert(23, '-'))}"
}
function Encode-GUID {
param( [string]$GUID )
$GUID = $GUID -creplace '[^0-F]'
$GUIDSections = @( 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 )
$Position = 0
$result = ""
ForEach($GUIDSection In $GUIDSections)
{ $arr = $GUID.substring($Position, $GUIDSection) -split "";
[array]::Reverse($arr);
$result = $result + ($arr -join '').replace(' ','');
$Position += $GUIDSection }
return $result
}
function Get-Bitness {
param( [string]$Location )
if([Environment]::Is64BitOperatingSystem){
if($_.PSPath -match '\\SOFTWARE\\Wow6432Node'){
return '32'
}else{
return '64'
}
} else {
Return '32'
}
}
#Enter the UpgradeCode here
$UpgradeCode = Encode-GUID "{AA783A14-A7A3-3D33-95F0-9A351D530011}"
$ProductGUIDs = (Get-Item HKLM:"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\$UpgradeCode", HKLM:"SOFTWARE\Classes\Installer\UpgradeCodes\$UpgradeCode").Property |
Select-Object -Unique |
ForEach-Object {Decode-GUID $_}
Get-ChildItem HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall, HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
Where-Object {$ProductGUIDs -contains $_.PSChildName} |
Get-ItemProperty |
Select-Object -Property @{Name='PackageCode';Expression={$_.PSChildName}}, DisplayName, Publisher, DisplayVersion, InstallDate, PSParentPath, @{Name='Bitness';Expression={Get-Bitness $_.PSPath}}
In the registry the GUIDs used in the "Installer" sections are encoded to match the way C++ would natively use them. The Decode and Encode functions in the example above are based on the technique used in this Roger Zander blog post. Please excuse the messiness of some of the code, if you need any part of this explained please let me know. Hope this helps you.