*Potentially* useful Dynamic Uninstall Script Logic

Hello All,

I want to start by saying that I’ve only been using PSADT for a couple months, so if you notice anything here that can be improved upon, or if PSADT has this native functionality available and I was just unaware of it, please do let me know! I believe it is best not to reinvent the wheel if that is the case. I am totally open to ideas. I did want to pass this along though, in case it is helpful to anyone.

Additionally, I feel the need to give credit where credit is due - I built this idea on the foundation of what
@jim discussed in Dynamic Uninstall - The Toolkit / Complete Deployment Scripts - PSAppDeployToolkit Community.

In any case, I believe I’ve found a way to dynamically identify any uninstall strings for a given application, then execute them. The logic below relies upon PSADT’s Get-InstalledApplication cmdlet, and will differentiate between a single object vs an array. Additionally, if the words ‘MsiExec’ are contained in the uninstall string, the script will simply call Remove-MSIApplications instead of Execute-Process. Otherwise, it will call Execute-Process with the appropriate app uninstall path and app uninstall params.

The one glaring weakness of this, in my opinion, is that it is currently calling the UninstallString natively provided by the Get-InstalledApplication function. I might be able to improve upon this by attempting to obtain the QuietUninstallString if one exists. Otherwise, additional parameters need to be added on a per-app basis to make the uninstall silent. If I were to rebuild this as a function, I suppose I could put an optional parameter in place to add the required silent parameters for the app.

        ## Store the current file version of the application name in a variable
        $AppInstallPresence = Get-InstalledApplication -Name 'VMware Horizon Client'

        ## If the application version matches the version being deployed, exit the script
        If ($AppInstallPresence) 
		{
			## Measure the version in a variable
			$AppInstallVersion = $AppInstallPresence.DisplayVersion

			## If the application version matches the version being deployed, write a log message and exit the script
			If ($AppInstallVersion -eq '8.4.0.24146')
			{
				Write-Log -Message "INFO: System is already running the current version of the application. The installation will not proceed."
            	Exit-Script 
			}
			## Otherwise, silently close the application process if it is running on the system, and prevent the executable from being opened until the script completes, and
			## run the uninstall process with a progress bar. The progress bar will remain for the duration of the script run.
			Else 
			{
				Show-InstallationWelcome -CloseApps 'vmware-view' -AllowDefer -DeferTimes '2'
				Show-InstallationProgress -StatusMessage 'Removing the current VMware Horizon Client Version...'
				$AppInstallPresenceType = $AppInstallPresence.GetType()
				$AppInstallPresenceTypeName = $AppInstallPresenceType.BaseType.Name

				If ($AppInstallPresenceTypeName -eq 'Object')
				{
					$AppUninstallString = ($AppInstallPresence.UninstallString -split '" ').Trim('"')
					$AppUninstallPath = $AppUninstallString[0]
					$AppUninstallParams = $AppUninstallString[1]

					Show-InstallationProgress -StatusMessage "$installPhase : Removing currently installed version of VMware Horizon Client..."
					If ($AppUninstallString -like '*MsiExec*')
					{
						Remove-MSIApplications -Name 'VMware Horizon Client'
					}
					Else
					{
						Execute-Process -Path "$AppUninstallPath" -Parameters "$AppUninstallParams /quiet"
					}
				}
				ElseIf ($AppInstallPresenceTypeName -eq 'Array')
				{
					ForEach ($App in $AppInstallPresence)
					{
						$AppUninstallString = ($App.UninstallString -split '" ').Trim('"')
						$AppUninstallPath = $AppUninstallString[0]
						$AppUninstallParams = $AppUninstallString[1]

						Show-InstallationProgress -StatusMessage "$installPhase : Removing currently installed version of VMware Horizon Client..."
						If ($AppUninstallString -like '*MsiExec*')
						{
							Remove-MSIApplications -Name 'VMware Horizon Client'
						}
						Else
						{
							Execute-Process -Path "$AppUninstallPath" -Parameters "$AppUninstallParams /quiet"
						}
					}
				}
			}  
        }

If there are any questions/concerns, ideas for improvement, or if you simply feel this isn’t going to work universally like I’m thinking it will, please don’t hesitate to point that out. I’m open to ideas!

Thanks!

1 Like

I’ve updated this script a little bit to “functionize” it - I placed it in my AppDeployToolkitExtensions.ps1 file and it seems to be working pretty well so far. I will say I have discovered some weaknesses which I’m going to try and work on - that is, if an uninstall string just contains the path to an uninstall executable without any parameters, it appears to fail, probably due to the way it is trimming the uninstall string from the uninstall parameters. If I come up with a way around that I’ll be sure to post it here.

Right now, in my environment, I am using this only when necessary. I prefer to use Remove-MSIApplications by default, then Execute-Process if an application generally has the same uninstall string regardless of versions but does not use msiexec. I only use this in scenarios where the UninstallString may be different from version to version - such as when the folder where the application is installed also references a specific version number, which can make removing multiple versions difficult.

Function Execute-DynamicUninstall 
{
<#
.SYNOPSIS
	Executes either Remove-MSIApplications or Execute-Process for each uninstall string identified for the application, with the appropriate
    path and parameters. Stores the uninstall strings in a variable using Get-InstalledApplication and a variety of substrings.
.DESCRIPTION
	Stores a variable for the application using Get-InstalledApplication, and then evaluates whether or not the variable that was created is
    an object or an array. If it is an object, it treats the variable as a single object and executes a single uninstall string. If it is
    an array, then it runs a foreach loop to handle every application stored within the array. If the uninstall string is detected as having
    the words 'msiexec' in them, then it will run Remove-MSIApplications without any special parameters. 
    Otherwise, it will run Execute-Process with the appropriate path and parameters.
.PARAMETER AppName
	The name of the application. This can be either an exact match or partial match.
.PARAMETER CorrectAppVersion
	The version that should not be removed if it is detected on the system (Or more likely, the version you are actively deploying). Simply set this to 0
    if you want it to remove any version found.
.PARAMETER AddUninstallParameters
	Additional uninstall parameters to add to the parameters identified in the uninstall string. This may be useful for scenarios where the UninstallString is not
    silent by default, and you have researched the parameters that need to be added to make the uninstall silent.
.EXAMPLE
	Execute-DynamicUninstall -AppName 'VMware Horizon Client' -CorrectAppVersion '8.4.0.24146' -AddUninstallParameters '/quiet'
    Removes VMware Horizon Client versions that are not 8.4.0.24146. Uses the /quiet parameter for any uninstalls performed with Execute-Process.
.EXAMPLE
	Execute-DynamicUninstall -AppName 'VMware Horizon Client -CorrectAppVersion '8.4.0.24146'
    Removes VMware Horizon Client versions that are not 8.4.0.24146.
.EXAMPLE
	Execute-DynamicUninstall -AppName 'VMware Horizon Client' -CorrectAppVersion '0'
    Removes any version of the VMware Horizon Client.
.NOTES
.LINK
	http://psappdeploytoolkit.com
#>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$AppName,
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$CorrectAppVersion,
        [Parameter(Mandatory=$false)]
        [string]$AddUninstallParameters
    )
    ## Store the current file version of the application name in a variable
    $AppInstallPresence = Get-InstalledApplication -Name "$AppName"

    ## If the application version matches the version being deployed, exit the script
    If ($AppInstallPresence) 
    {
        ## Measure the version in a variable
        $AppInstallVersion = $AppInstallPresence.DisplayVersion

        ## If the application version matches the version being deployed, write a log message and exit the script
        If ($AppInstallVersion -like "*$AppVersion*")
        {
            Write-Log -Message 'System is already running the current version of the application. The installation will not proceed.'
            Exit-Script 
        }
        ## Otherwise, silently close the application process if it is running on the system, and prevent the executable from being opened until the script completes, and
        ## run the uninstall process with a progress bar. The progress bar will remain for the duration of the script run.
        Else 
        {
            $AppInstallPresenceType = $AppInstallPresence.GetType()
            $AppInstallPresenceTypeName = $AppInstallPresenceType.BaseType.Name

            If ($AppInstallPresenceTypeName -eq 'Object')
            {
                $AppUninstallString = ($AppInstallPresence.UninstallString -split '" ').Trim('"')
                $AppUninstallPath = $AppUninstallString[0]
                $AppUninstallParams = $AppUninstallString[1]

                Show-InstallationProgress -StatusMessage "$installPhase : Removing currently installed version of $AppName..."
                If ($AppUninstallString -like '*MsiExec*')
                {
                    Remove-MSIApplications -Name "$AppName"
                }
                Else
                {
                    Execute-Process -Path "$AppUninstallPath" -Parameters "$AppUninstallParams $AddUninstallParameters"
                }
            }
            ElseIf ($AppInstallPresenceTypeName -eq 'Array')
            {
                ForEach ($App in $AppInstallPresence)
                {
                    $AppUninstallString = ($App.UninstallString -split '" ').Trim('"')
                    $AppUninstallPath = $AppUninstallString[0]
                    $AppUninstallParams = $AppUninstallString[1]

                    Show-InstallationProgress -StatusMessage "$installPhase : Removing currently installed version of $AppName..."
                    If ($AppUninstallString -like '*MsiExec*')
                    {
                        Remove-MSIApplications -Name "$AppName"
                    }
                    Else
                    {
                        Execute-Process -Path "$AppUninstallPath" -Parameters "$AppUninstallParams $AddUninstallParameters"
                    }
                }
            }
        }  
    }
}

Also, I replied to my post because it would appear my ability to edit is locked after a certain amount of time. That said, if I misunderstood that, just let me know.

Thank you!

1 Like