Get-MsiPropertyValue.ps1

<#PSScriptInfo
 
.VERSION
    1.0
.GUID
    dc64634e-86a9-4ed5-bc7f-f2a55fa3bb0a
.AUTHOR
    Thomas Malkewitz @dotps1
.TAGS
    MSI, PSCustomObject
.PROJECTURI
    https://gist.github.com/dotps1/2c6f21ed0c9d47dae300ea712cfe8f4c
.RELEASENOTES
    Initial Release.
 
#>


<#
.SYNOPSIS
    Gets a property value from a Windows Installer Database.
.DESCRIPTION
    Opens a Windows Installer Database (.msi) and querys for the specified property value.
.INPUTS
    System.String.
.OUTPUTS
    System.String.
.PARAMETER Path
    The location of the Windows Installer Database.
.PARAMETER Property
    The Property to get the value of.
.EXAMPLE
    PS C:\> Get-MsiPropertyValue -Path .\jre1.8.0_121.msi -Property ProductVersion, ProductCode
 
    Name ProductVersion ProductCode
    ---- -------------- -----------
    jre1.8.0_121.msi 8.0.1210.13 {26A24AE4-039D-4CA4-87B4-2F32180121F0}
.EXAMPLE
    PS C:\> Get-ChildItem -Path ".\Installers" -Filter "*.msi" | Select -ExpandProperty FullName | Get-MsiPropertyValue -Property ProductVersion
     
    Name ProductVersion ProductCode
    ---- -------------- -----------
    jre1.8.0_101.msi 8.0.1010.13 {26A24AE4-039D-4CA4-87B4-2F32180101F0}
    jre1.8.0_111.msi 8.0.1110.14 {26A24AE4-039D-4CA4-87B4-2F32180111F0}
    jre1.8.0_121.msi 8.0.1210.13 {26A24AE4-039D-4CA4-87B4-2F32180121F0}
.LINK
    https://dotps1.github.io
#>


    
[CmdletBinding()]
[OutputType(
    [PSCustomObject]
)]

param (
    [Parameter(
        Mandatory = $true,
        ValueFromPipeLine = $true,
        ValueFromPipelineByPropertyName = $true
    )]
    [ValidateScript({
        if (([System.IO.FileInfo]$_).Extension -eq ".msi") {
            $true
        } else {
            throw "Path must be a Windows Installer Database (*.msi) file."
        }
    })]
    [String[]]
    $Path,

    [Parameter(
        Mandatory = $true,
        ValueFromPipelineByPropertyName = $true
    )]
    [String[]]
    $Property
)

begin {
    $windowsInstaller = New-Object -ComObject WindowsInstaller.Installer
}

process {
    foreach ($pathValue in $Path) {
        try {
            $item = Get-Item -Path $pathValue -ErrorAction Stop
                
            $database = $windowsInstaller.GetType().InvokeMember(
                "OpenDatabase", "InvokeMethod", $null, $windowsInstaller, ($item.FullName, 0)
            )

            $output = [PSCustomObject]@{
                Name = $item.Name
            }

            foreach ($propertyValue in $Property) {
                $view = $database.GetType().InvokeMember(
                    "OpenView", "InvokeMethod", $null, $database, "SELECT Value FROM Property WHERE Property = '$propertyValue'"
                )

                $view.GetType().InvokeMember(
                    "Execute", "InvokeMethod", $null, $view, $null
                ) | Out-Null

                $record = $view.GetType().InvokeMember(
                    "Fetch", "InvokeMethod", $null, $view, $null
                )

                $value = $record.GetType().InvokeMember(
                    "StringData", "GetProperty", $null, $record, 1
                )

                Add-Member -InputObject $output -Name $propertyValue -Value $value -MemberType NoteProperty
            }

            # Close the database, else it will be locked.
            $database.GetType().InvokeMember(
                "Close", "InvokeMethod", $null, $view, $null
            ) | Out-Null

            Write-Output -InputObject $output
        } catch {
            Write-Error $_
            continue
        }
    }
}

end {
    [Void][System.Runtime.InteropServices.Marshal]::ReleaseComObject(
        $windowsInstaller
    )
}