Querying MSI packages
Using PowerShell and SQL to get the features and components contained in an installer
Published in Articles on by Michiel van Oosterhout ~ 3 min read
When automating the installation of software you often need to select the features that you want to install. But the command-line parameter values for individual features are often not documented. Luckily, for MSI packages we can use Windows Installer's automation interfaces to query the installer database contained within.
The MSI database
First we need to understand the core tables group of the schema which contains data related to features and components, as well as the SQL syntax used by the installer database.
The Features
table might give enough information about what constitutes each feature, but when joined to the Components
table we often get an even better understanding of each feature. The query below joins these tables:
SELECT Feature, Title, Display, Component
FROM Feature, FeatureComponents, Component
WHERE Feature_ = Feature
AND Component_ = Component
Notice that there is no support for the JOIN
keyword, we can only perform inner joins by selecting from multiple tables and using the WHERE
clause to filter the records to those that should be joined.
Running queries with PowerShell
To run this query we can use Windows PowerShell to create the WindowsInstaller.Installer
COM object. The Windows PowerShell script below executes a simple query:
# Open MSI database
$installer = New-Object -ComObject "WindowsInstaller.Installer"
$database = $installer.OpenDatabase("path\to\installer.msi", 0)
# Open a view
$query = "SELECT * FROM Feature"
$view = $database.OpenView($query)
# Execute the query
$parameters = $installer.CreateRecord(0)
$view.Execute($parameters)
# Release database resources
$view.Close()
Notice that we need to call $view.Execute()
to execute the query, and that we should call $view.Close()
when we are done.
Getting a list of features and their components
What is left is to display the results. For this we need to call $view.Fetch()
in a loop untill it returns $null
, and then format the result. The Windows PowerShell script below prints a table of features and their components:
# Open MSI database
$installer = New-Object -ComObject "WindowsInstaller.Installer"
$database = $installer.OpenDatabase("absolute\path\to\installer.msi", 0)
# Define the query
$query = "SELECT Feature, Title, Display, Component"
$query += " FROM Feature, FeatureComponents, Component"
$query += " WHERE Feature_ = Feature"
$query += " AND Component_ = Component"
# Open a view
$view = $database.OpenView($query)
# Execute the query
$parameters = $installer.CreateRecord(0)
$view.Execute($parameters)
# Define result
$result = @()
# Fetch the first record and start iterating
$record = $view.Fetch()
while ($record -ne $null)
{
# Add feature to result
$result += @{
ADDLOCAL = $record.StringData(1)
Title = $record.StringData(2)
IsHidden = $record.IntegerData(3) -eq 1
COMPADDLOCAL = $record.StringData(4)
}
# Fetch next record
$record = $view.Fetch()
}
# Release database resources
$view.Close()
# Display result as a table
$result | ForEach { [PSCustomObject]$_ } | Format-Table ADDLOCAL,IsHidden,Title,COMPADDLOCAL
With the result you can now use the values in the ADDLOCAL
column to select exactly those features you want to install by setting the corresponding properties via the command line (e.g. msiexec /i path\to\installer.ms ADDLOCAL=server,agent
), or indirectly select features using the COMPADDLOCAL
column.
Summary
We don't need to install any additional tools nor perform a one-time installation of an MSI package to discover the features that are contained in the package. We can simply write a PowerShell script that uses Windows Installer's automation to directly query the MSI package's database.