MSI - How to install, uninstall, and log silently

This is the first of my Packager-Specific Tips & Tricks articles.
Much of this is somewhat covered in the PSADT manual but techniques and logging is out of scope for the PSADT manual. I’ll try to update it as I can.

Future articles will talk about non-MSI packages and how to make them work in PSADT.
.

I usually place the following code before the [string]$installPhase = 'Pre-Installation' line so I only need to do it once. This way I can these variables in either Install or Uninstall phases. I refer to them in my examples below.

[string]$AppMSIName	= 'Filename.MSI' # actual name of the MSI in the \Files folder
[string]$AppMSICode	= '{01FE8DFA-275C-432A-B272-C53F27A1CAAD}' #The MSI's PRODUCTCODE (Use Orca and look in Property table)

.
MSI INSTALL EXAMPLES:

Launch MSI from the \Files sub directory, don’t check if the MSI is already installed, tell MSIexec to create a log based on $AppMSIName

Execute-MSI -Action Install -Path $AppMSIName -SkipMSIAlreadyInstalledCheck -ContinueOnError $False -LogName "${AppMSIName}_MSI"

.
Launch MSI + MST from the \Files sub directory, set MSI Properties using -AddParameters

[String]$AppMSITran = 'TransformFileName.mst'
[String]$AppMSIArgs = 'MSIPUBLICPROPERTY=1'
Execute-MSI -Action Install -Path $AppMSIName -Transform $AppMSITran -AddParameters $AppMSIArgs -ContinueOnError $False -LogName "${AppMSIName}_MSI"

.
Install an MSP patch from the \Files sub directory
Execute-MSI -Action Patch -Path “PatchName.msp” -ContinueOnError $False -LogName “PatchName”

.

MSI UNINSTALL EXAMPLES

Uninstall Using the MSI’s PRODUCTCODE, tell MSIexec to create a log based on $AppMSIName
Execute-MSI -Action Uninstall -Path $AppMSICode -ContinueOnError $False -LogName "${AppMSIName}_MSI"

.

MSI Q&A

What if the Setup.exe is in the same folder as the MSI? Do I NEED to use it?
90% of the time, NO. Just ignore it. If it totally blows up then maybe it’s an old InstallShield Setup.EXE
.

What if the MSI is buried inside an EXE? Solution1: EXTRACTION!
You can try to extract the MSI (and maybe other files). Sometimes the setup.exe is called something else. Here are 3 methods to extract the MSI file from an EXE.

*Use the setup.exe directly
setup.exe /s /x /b"C:\FolderInWhichMSIWillBeExtracted" /v"/qn"
setup.exe /a
setup.exe /s /extract_all
setup.exe /s /extract_all:[path]
setup.exe /stage_only
setup.exe /extract “C:\My work”

*Steal it during installation
-Clear your %TEMP% folder
-Launch the setup.EXE and watch the %TEMP% folder.
-You might need to go through the install gradually but eventually you’ll see an MSI file appear.
CAVEAT: If you end up going through the full install, you will need to copy it ASAP before the Setup.exe deletes the %TEMP% files.

*Use 7-Zip directly on the EXE
This rarely works but may show you that there is no MSI inside the EXE to begin with.
https://www.7-zip.org/download.html

What if the MSI is buried inside an EXE? Solution2: pass the parameters to the MSI inside the EXE

InstallShield Setup.exe EXAMPLES

#Launch InstallShield Setup.exe with MSI(s) inside. Force log files to the Temp logging folder. 
#To Use " in the log path, InstallShield needs \" but PS needs to escape the " so we get \`"
#NOTE: -SMS is for InstallShield Setup.exe to suppress error popup dialogs if an issue occurs. (InstallShield will hang the script otherwise)
Execute-Process -Path "setup.exe" -Parameters "/s /v`"ALLUSERS=1 /qn /L* \`"$logTempFolder\$installName.log\`"`" -SMS"

.

#Launch InstallShield Setup.exe. The -WaitForMsiExec waits for the msiexec engine to become available before starting the install
Execute-Process -Path "$dirFiles\Bin\setup.exe" -Parameters '/S' -WaitForMsiExec -WindowStyle Hidden

.

#complicated InstallShield Setup.exe example
#Shows how to set parameters with double-quotes by escaping them with `"
[String]$AppExeName = "$DirFiles\setup.exe"
[String]$AppExeArgs = "/V`"ALLUSERS=1 TRANSFORMSSECURE=1 REBOOT=ReallySuppress /L*V `"$logTempFolder\MsiLogFileName.log`" /qn /Norestart`"  "
Execute-Process -Path $AppExeName -Parameters $AppExeArgs -ContinueOnError $False

.

#Launch InstallShield Setup.exe. Force log files to the Temp logging folder
Execute-Process -Path "setup.exe" -Parameters "-s -f2`"$logTempFolder\$installName.log`" -SMS"

.
How do I get the options I select when I run the MSI manually without using a transform (MST)?
The authors of the MSI may have a configuration tool or packager tool to help you with.
MS, Adobe, Citrix and more have such tools for many of their apps.

If not , open the MSI with an MSI editor and look for the PROPERTY table.
Look for properties in ALLUPPERCASE. These are known as PUBLIC properties and can be set from the command line. Some Properties are not mentioned in the PROPERTY table.

A Third method is to look in the MSI log file:
-Force MSI to create log files for ANY MSI (Enable Windows Installer logging - Windows Client | Microsoft Docs)
-The files will be created in %TEMP% and named in the form of MSIxxxxx.LOG
-Clear your %temp% folder
-Run the package manually and configure as best you can.
-Locate the MSIxxxxx.LOG file
-Open the MSI log file and scroll to the very end
-look for the PUBLIC property called <?TBA?>
-You can also see what those PUBLIC properties are set to.

1 Like

Small updates:

What if the Setup.exe is in the same folder as the MSI? Do I NEED to use it?
90% of the time, NO. Just ignore it. If it totally blows up then maybe it’s an old InstallShield Setup.EXE

Sometimes the MSI needs a Property set to tell the MSI was launched by the setup.exe.
Something like ADINSTALL=1 or ADDEPLOY=1.
To obtain this property name:

  • -use the registry tweak to for MSI to create log files in %TEMP%
  • -Launch the setup.exe manually
  • -Look in %temp% for the newest MSE#####.log file

.
Can one PSADT script install multiple MSIs?
Yes! in the order you want, too.
You can also uninstall MSIs, run EXEs (setup.exe or others), do PowerShell stuff in between.
Depending what you do, you might need to add a small delay here and there.

Thanks for posting this, just checking do you add the above commands in deploy-application.ps1?

Yes. Usually each deploy-application.ps1 is different.

1 Like

In regards to the <?TBA?> in the original post above (that I can no longer edit!! :angry:), it should have said ADDLOCAL as in:

-look for the PUBLIC property called ADDLOCAL