0

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.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Abby Jangir
  • 55
  • 2
  • 12
  • how are the version and upgrade codes connected? how would one use the upgrade code to identify the product? – Lee_Dailey Dec 10 '18 at 05:00
  • I am very new to powershell and these updates. What I was thinking is that for every product (in my case MSI) there will be one upgrade code (and product, package code as well). Can I somehow use that upgrade code and know the product version. I am trying to do it using WQL. Please suggest if there is any other way to find product version. – Abby Jangir Dec 10 '18 at 05:59
  • i don't know what a "package code" or an "upgrade code" _is_. [*blush*] therefore, i can't make even the most vague of guesses as to how to use such to find an installed application. _please_, would you kindly explain what you are talking about? – Lee_Dailey Dec 10 '18 at 06:10
  • Hi Marc, Thanks for the edit :) @Lee please "see https://blogs.msdn.microsoft.com/pusu/2009/06/10/what-are-upgrade-product-and-package-codes-used-for/" – Abby Jangir Dec 10 '18 at 08:49
  • 1
    I think this should help you https://stackoverflow.com/questions/46637094/how-can-i-find-the-upgrade-code-for-an-installed-msi-file – montonero Dec 10 '18 at 14:28
  • @AbbyJangir - thanks for the info! [*grin*] ///// it looks like monotero has answered your question. – Lee_Dailey Dec 10 '18 at 16:35

1 Answers1

3

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.

Paul G
  • 1,219
  • 7
  • 14
  • Hi Paul, thank you for the reply. Time is a prime concern to me. For your second suggestion, how do I know if the package is for 32 bit or 64 bit so the path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" will be different for 32bit packages. – Abby Jangir Dec 14 '18 at 06:43
  • Hey @AbbyJangir I've just added an addition function and property to give PS ParentPath and Bitness. Does that get you all the information you need? – Paul G Dec 14 '18 at 18:18
  • Hi @Paul G, Thank you for your time. It works for :) :) – Abby Jangir Dec 17 '18 at 06:26