Application Packaging | Installer Template

Overview

A simple PowerShell template designed for use with SCCM/MECM to install exe/msi based applications. Highlights include:

  • Option to uninstall an existing application prior to install (Useful in scenarios where the application has previously been installed manually.)
  • Application detection logic built-in to determine the appropriate exit code.
  • Sets a registry item with the date of install if the process completes successfully. This registry item can also be used as an Application Detection Method for SCCM.
  • Generates a log file.
How to use

Configure the following script variables:

  • $PackageName – Create a friendly package name. This will be used to generate the registry entry to use as an SCCM Application Detection Method. As a sample, you can see I’ve named the app according to vendor, product, version etc.
  • $SoftwareDetectionPath – Keep the path configured if you’re happy with the location. Otherwise, feel free to specify your own. This is used to create a registry key following a successful install. This key should be used as an SCCM Application Detection Method.
  • $UninstallExisting – Set either $true or $false. Enables the option to uninstall an existing application if it exists on the client device.
  • $RegistryDisplayName – The name of the application as appears in the registry for the current application. See the example in the script below for more details on how you can retrieve the Display Name for the application you wish to uninstall prior to installation.
  • $Installers
    • InstallFile – The name of each installation msi/exe.
    • InstallArguments – The install parameters required for a silent installation for each installer.
    • RegistryDisplayName – Used for the install verification logic. See the sample in the script for more information on how to use.

Save your script as install.ps1. Follow methodologies used to create an Application in SCCM. See here for more details.

Navigate to your SCCM Application properties. Configure your install script using the following command:

Powershell.exe -ExecutionPolicy ByPass -File install.ps1
view raw Sample0.ps1 hosted with ❤ by GitHub

Under the Detection Method tab, select edit under Use a Custom Script to Detect the Presence of this Application Type. Replace PackageName with the value you entered for the $PackageName variable in your install script earlier.

$packageFound = Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Software Distribution" -Name "PackageName" -ErrorAction SilentlyContinue
if ($packageFound){ write-host "Installed"}
view raw Sample1.ps1 hosted with ❤ by GitHub

Feel free to use the script below as a template and modify as you wish. If you have any questions, comment below.

Script
#ApplicationPackaging_PackageInstallerTemplate
#Version | 1.0
#Contact | Josh Woods | joshua@jwblog.uk
#References
#https://techgenix.com/how-to-uninstall-software-using-powershell/
###################
#Variables
###################
#Configuration
$PackageName = "Microsoft_PowerBI_1.0_64_MSI_R01"
$SoftwareDetectionPath = "HKLM:\SOFTWARE\Software Distribution"
#Uninstall
$UninstallExisting = $false
#Uninstall
#Retrieve Registry Display name by locating the version you need to uninstall from either of the following two paths:
#(Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | Select-object DisplayName, DisplayVersion | sort-object)
#(Get-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | Select-object DisplayName, DisplayVersion | sort-object)
#Get-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | where-object {$_.DisplayName -Like "*WinRAR*"}
#Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where-object {$_.DisplayName -Like "*WinRAR*"}
$Uninstallers = @(
[pscustomobject]@{RegistryDisplayName='WinRAR 6.23 (64-bit)';RegistryDisplayVersion='6.23.0';UninstallStringDir='C:\Program Files\WinRAR\uninstall.exe';UninstallStringArguments='/s'} #Must be placed in the same directory as this script
[pscustomobject]@{RegistryDisplayName='';RegistryDisplayVersion='';UninstallStringDir='';UninstallStringArguments=''}
)
#Install
#Retrieve Registry Display name by locating the version you need to uninstall from either of the following two paths:
#(Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | Select-object -expandproperty DisplayName | sort-object)
#(Get-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | Select-object -expandproperty DisplayName | sort-object)
$Installers = @(
[pscustomobject]@{InstallFile='PowerBI.exe';InstallArguments='/QN /norestart';RegistryDisplayName='PowerBI'} #Must be placed in the same directory as this script
[pscustomobject]@{InstallFile='';InstallArguments='';RegistryDisplayName=''}
)
###################
#Variables End
###################
#Stop Install on Error and output to log
try {
#Log
#Start Log Transcript
Start-Transcript -Path "C:\Windows\Logs\$PackageName.txt" -append
if ($UninstallExisting -eq $true) {
########################################################################################################################################################################################################################################
#This section will uninstall a previous version based on the $RegistryDisplayName variable which uses the applications DisplayName.
########################################################################################################################################################################################################################################
foreach ($Uninstall in $Uninstallers) {
#Run Uninstall String
Write-Output "Uninstalling $($Uninstall.RegistryDisplayName) based on Uninstall String…"
Start-Process "$($Uninstall.UninstallStringDir)" -ArgumentList "$($Uninstall.UninstallStringArguments)" -Wait
}
}
else {
#Run Uninstall String
Write-Output "Uninstalling $($Uninstaller.RegistryDisplayName) based on Uninstall String…"
Start-Process "$($Uninstallers.UninstallStringDir)" -ArgumentList "$($Uninstallers.UninstallStringArguments)" -Wait
}
########################################################################################################################################################################################################################################
#This section will Install the App and set the detection registry entry
########################################################################################################################################################################################################################################
foreach ($Installer in $Installers) {
#Install/Upgrade
Write-Output "Installing $($Installer.InstallFile)"
Start-Process "$PSScriptRoot\$($Installer.InstallFile)" -ArgumentList "$($Installer.InstallArguments)" -Wait | out-null
#Verify Installation
$Verify1 = Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where-object DisplayName -eq "$($Installer.RegistryDisplayName)"
$Verify2 = Get-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | where-object DisplayName -eq "$($Installer.RegistryDisplayName)"
if (($Verify1.DisplayName -eq "$($Installer.RegistryDisplayName)") -or ($Verify2.DisplayName -eq "$($Installer.RegistryDisplayName)")) {
#Success
Write-Output "Successfully Installed $($Installer.InstallFile)"
}
else {
Write-Output "Failed to Install $($Installer.InstallFile)"
Exit 1
}
}
#SCCM Detection Registry Entry
Get-Item -Path "$SoftwareDetectionPath" | Out-Null
if ($?) {
#Do nothing. Registry path already exists
}
else {
New-Item -Path "$SoftwareDetectionPath"
}
#Set Install Confirmation Registry Key
New-ItemProperty -Path "$SoftwareDetectionPath" -Name "$PackageName" -PropertyType "String" -Value "Installed on $(Get-Date)" -Force
Start-Sleep -s 10
#Output Logs to File
Stop-Transcript
}
catch {
#Failed. Write error and exit.
Write-Output "Error: $($_.Exception.Message)"
#Output Logs to File
Stop-Transcript
}