New Get-InstalledApplication and Remove-MSIApplications using 3.60 Codebase

Topics: Archive - Deployment Scripts
Dec 19, 2014 at 2:57 AM
The Get-InstalledApplication function would throw errors when the -ProductCode parameter was used. I used parameter sets to separate the -Name and -Exact parameters from the -ProductCode parameter. I also added logging to distinguish which parameter combinations were being used.

I added a -ProductCode parameter to the Remove-MSIApplications function with the same parameter set logic. Passing the Product Code to Remove-MSIApplications will be more robust when searching for a specific application with the Get-InstalledApplication function.
Dec 19, 2014 at 2:58 AM
#region Function Get-InstalledApplication
Function Get-InstalledApplication {
<#
.SYNOPSIS
    Retrieves information about installed applications.
.DESCRIPTION
    Retrieves information about installed applications by querying the registry. You can specify an application name, a product code, or both.
    Returns information about application publisher, name & version, product code, uninstall string, install source, location, date, and application architecture.
.PARAMETER Name
    The name of the application to retrieve information for. Performs a wild card match on the application display name.
.PARAMETER Exact
    Specifies to only match the exact name of the application.
.PARAMETER ProductCode
    The product code of the application to retrieve information for.
.PARAMETER IncludeUpdatesAndHotfixes
    Include matches against updates and hotfixes in results.
.EXAMPLE
    Get-InstalledApplication -Name 'Adobe Flash'
.EXAMPLE
    Get-InstalledApplication -ProductCode '{1AD147D0-BE0E-3D6C-AC11-64F6DC4163F1}'
.NOTES
.LINK
    http://psappdeploytoolkit.codeplex.com
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true,ParameterSetName='Param1')]
        [ValidateNotNullorEmpty()]
        [string[]]$Name,
        [Parameter(Mandatory=$true,ParameterSetName='Param2')]
        [ValidateNotNullorEmpty()]
        [string]$ProductCode,
        [Parameter(Mandatory=$false,ParameterSetName='Param1')]
        [switch]$Exact = $false,
        [Parameter(Mandatory=$false)]
        [switch]$IncludeUpdatesAndHotfixes
    )
    
    Begin {
        ## Get the name of this function and write header
        [string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
    }
    Process {
        If ($name -and !($exact)) {
            Write-Log -Message "Get information for installed Application Name(s) [$($name -join ', ')]..." -Source ${CmdletName}
        }
        If ($name -and $exact) {
            Write-Log -Message "Get exact information for installed Application Name(s) [$($name -join ', ')]..." -Source ${CmdletName}
        }
        If ($productCode) {
            Write-Log -Message "Get information for installed Product Code [$ProductCode]..." -Source ${CmdletName}
        }
        
        [psobject[]]$installedApplication = @()
        ForEach ($regKey in $regKeyApplications) {
            Try {
                If (Test-Path -Path $regKey -ErrorAction 'Stop') {
                    [psobject[]]$regKeyApplication = Get-ChildItem -Path $regKey -ErrorAction 'Stop' | ForEach-Object { Get-ItemProperty -LiteralPath $_.PSPath -ErrorAction 'SilentlyContinue' | Where-Object { $_.DisplayName } }
                    ForEach ($regKeyApp in $regKeyApplication) {
                        Try {
                            [string]$appDisplayName = ''
                            [string]$appDisplayVersion = ''
                            [string]$appPublisher = ''
                            
                            ## Bypass any updates or hotfixes
                            If (-not $IncludeUpdatesAndHotfixes) {
                                If ($regKeyApp.DisplayName -match '(?i)kb\d+') { Continue }
                                If ($regKeyApp.DisplayName -match 'Cumulative Update') { Continue }
                                If ($regKeyApp.DisplayName -match 'Security Update') { Continue }
                                If ($regKeyApp.DisplayName -match 'Hotfix') { Continue }
                            }
                            
                            ## Remove any control characters which may interfere with logging and creating file path names from these variables
                            $appDisplayName = $regKeyApp.DisplayName -replace '[^\u001F-\u007F]',''
                            $appDisplayVersion = $regKeyApp.DisplayVersion -replace '[^\u001F-\u007F]',''
                            $appPublisher = $regKeyApp.Publisher -replace '[^\u001F-\u007F]',''

                            ## Determine if application is a 64-bit application
                            [boolean]$Is64BitApp = If (($is64Bit) -and ($regKey -notmatch '^HKLM:SOFTWARE\\Wow6432Node')) { $true } Else { $false }
                            
                            If ($ProductCode) {
                                ## Verify if there is a match with the product code passed to the script
                                If ($regKeyApp.PSChildName -match [regex]::Escape($productCode)) {
                                    Write-Log -Message "Found installed application [$appDisplayName] version [$appDisplayVersion] matching product code [$productCode]" -Source ${CmdletName}
                                    $installedApplication += New-Object -TypeName PSObject -Property @{
                                        ProductCode = $regKeyApp.PSChildName
                                        DisplayName = $appDisplayName
                                        DisplayVersion = $appDisplayVersion
                                        UninstallString = $regKeyApp.UninstallString
                                        InstallSource = $regKeyApp.InstallSource
                                        InstallLocation = $regKeyApp.InstallLocation
                                        InstallDate = $regKeyApp.InstallDate
                                        Publisher = $appPublisher
                                        Is64BitApplication = $Is64BitApp
                                    }
                                }
                            }
                            
                            If ($name) {
                                ## Verify if there is a match with the application name(s) passed to the script
                                ForEach ($application in $Name) {
                                    $applicationMatched = $false
                                    If ($exact) {
                                        #  Check for an exact application name match
                                        If ($regKeyApp.DisplayName -eq $application) {
                                            $applicationMatched = $true
                                            Write-Log -Message "Found installed application [$appDisplayName] version [$appDisplayVersion] exactly matching application name [$application]" -Source ${CmdletName}
                                        }
                                    }
                                    #  Check for a partial application name match
                                    ElseIf ($regKeyApp.DisplayName -match [regex]::Escape($application)) {
                                        $applicationMatched = $true
                                        Write-Log -Message "Found installed application [$appDisplayName] version [$appDisplayVersion] matching application name [$application]" -Source ${CmdletName}
                                    }
                                    
                                    If ($applicationMatched) {
                                        $installedApplication += New-Object -TypeName PSObject -Property @{
                                            ProductCode = $regKeyApp.PSChildName
                                            DisplayName = $appDisplayName
                                            DisplayVersion = $appDisplayVersion
                                            UninstallString = $regKeyApp.UninstallString
                                            InstallSource = $regKeyApp.InstallSource
                                            InstallLocation = $regKeyApp.InstallLocation
                                            InstallDate = $regKeyApp.InstallDate
                                            Publisher = $appPublisher
                                            Is64BitApplication = $Is64BitApp
                                        }
                                    }
                                }
                            }
                        }
                        Catch {
                            Write-Log -Message "Failed to resolve application details from registry for [$appDisplayName]. `n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
                            Continue
                        }
                    }
                }
            }
            Catch {
                Write-Log -Message "Failed to resolve registry path [$regKey]. `n$(Resolve-Error)" -Severity 3 -Source ${CmdletName}
                Continue
            }
        }
        Write-Output $installedApplication
    }
    End {
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
    }
}
#endregion
Dec 19, 2014 at 2:59 AM
#region Function Remove-MSIApplications
Function Remove-MSIApplications {
<#
.SYNOPSIS
    Removes all MSI applications matching the specified application name.
.DESCRIPTION
    Removes all MSI applications matching the specified application name.
    Enumerates the registry for installed applications matching the specified application name and uninstalls that application using the product code, provided the uninstall string matches "msiexec".
.PARAMETER Name
    The name of the application to uninstall.
.PARAMETER ProductCode
    The product code of the application to retrieve information for.
.PARAMETER Exact
    Specifies whether to exactly match the name of the application
.PARAMETER ContinueOnError
    Continue if an exit code is returned by msiexec that is not recognized by the App Deploy Toolkit.
.EXAMPLE
    Remove-MSIApplications -Name 'Adobe Flash'
    Removes all versions of software that match the name "Adobe Flash"
.EXAMPLE
    Remove-MSIApplications -Name 'Adobe'
    Removes all versions of software that match the name "Adobe"
.NOTES
.LINK
    http://psappdeploytoolkit.codeplex.com
#>
    [CmdletBinding()]
    Param (
        [Parameter(Position=0,Mandatory=$true,ParameterSetName='Param1')]
        [ValidateNotNullorEmpty()]
        [string]$Name,
        [Parameter(Position=0,Mandatory=$true,ParameterSetName='Param2')]
        [ValidateNotNullorEmpty()]
        [string]$ProductCode,
        [Parameter(Mandatory=$false,ParameterSetName='Param1')]
        [ValidateNotNullorEmpty()]
        [switch]$Exact=$false,
        [Parameter(Mandatory=$false)]
        [ValidateNotNullorEmpty()]
        [boolean]$ContinueOnError = $true
    )
    
    Begin {
        ## Get the name of this function and write header
        [string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
    }
    Process {
        If ($ProductCode) {
            [psobject[]]$installedApplications = Get-InstalledApplication -ProductCode $ProductCode
        }
        If ($Exact -and $Name) {
            [psobject[]]$installedApplications = Get-InstalledApplication -Name $name -Exact
        }
        If ($Name -and !($ProductCode) -and !($Exact)) {
            [psobject[]]$installedApplications = Get-InstalledApplication -Name $name
        }

        
        If (($null -ne $installedApplications) -and ($installedApplications.Count)) {
            ForEach ($installedApplication in $installedApplications) {
                If ($installedApplication.UninstallString -match 'msiexec') {
                    Write-Log -Message "Remove application [$($installedApplication.DisplayName) $($installedApplication.Version)]." -Source ${CmdletName}
                    If ($ContinueOnError) {
                        Execute-MSI -Action Uninstall -Path $installedApplication.ProductCode -ContinueOnError $true
                    }
                    Else {
                        Execute-MSI -Action Uninstall -Path $installedApplication.ProductCode
                    }
                }
                Else {
                    Write-Log -Message "[$($installedApplication.DisplayName)] uninstall string [$($installedApplication.UninstallString)] does not match `"msiexec`", so removal will not proceed." -Severity 2 -Source ${CmdletName}
                }
            }
        }
        Else {
            Write-Log -Message 'No applications found for removal. Continue...' -Source ${CmdletName}
        }
    }
    End {
        Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
    }
}
#endregion