Occasionally we need to find the Product Name, Product Code and more for an installed MSI. It’s also handy to find the Install Source so we can see where i was installed from.
There are various ways to achieve this such as querying the registry or WMI, but the fastest way to do this is to use the Windows Installer object like so:
cls
$Installer = New-Object -ComObject WindowsInstaller.Installer;
$InstalledProducts = @()
foreach($context in @(7,3)) { #per-machine 7 or per-user 3
$InstallerProducts = $Installer.ProductsEx("", "", $context);
$InstalledProducts += ForEach($Product in $InstallerProducts)
{
[PSCustomObject]@{
ProductCode = $Product.ProductCode();
LocalPackage = try { $Product.InstallProperty("LocalPackage") } catch [System.Runtime.InteropServices.COMException] { "Not Found" };
VersionString = try { $Product.InstallProperty("VersionString") } catch [System.Runtime.InteropServices.COMException] { "Not Found" };
Publisher = try { $Product.InstallProperty("Publisher") } catch [System.Runtime.InteropServices.COMException] { "Not Found" };
ProductName = try { $Product.InstallProperty("ProductName") } catch [System.Runtime.InteropServices.COMException] { "Not Found" };
InstallSource = try { $Product.InstallProperty("InstallSource") } catch [System.Runtime.InteropServices.COMException] { "Not Found" };
InstallDate = try { $Product.InstallProperty("InstallDate").substring(0,4) + "-" + $Product.InstallProperty("InstallDate").substring(2,2) + "-" + $Product.InstallProperty("InstallDate").substring(4,2) } catch [System.Runtime.InteropServices.COMException] { "Not Found" };
Context = $(if ($context -eq 7) { "Machine" } else { "User" })
}
}
}
$InstalledProducts