Maintaining Adobe Flash

Although as we all know Adobe Flash is a product which has caused quite a lot of security related news over the past number and years, and Adobe is to discontinue it in 2020, however for some organisations there is still a requirement to maintain the product.

Exit Code 1603

Recently I was asked to push out an upgraded version of the Flash PPAPI for use with Google Chrome, and quickly came up against the 1603 exit code issue that Nickolaj covers in an earlier post from 2013 (http://www.scconfigmgr.com/2013/05/23/upgrade-adobe-flash-player-11-7-x-fails-with-error-1603-in-configmgr-2012/).

AppEnforce Log Contents

I decided to write a PowerShell script to cater for upgrading Flash. The script perform the following actions:

  • Search the registry for Uninstall strings for versions earlier than the detected MSI version from both the “HKLM:SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall” and “HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall” locations
  • Run the MSIEXEC command silently with the uninstall string
  • Query WMI to obtain a list of registered versions
    Legacy Adobe Flash PPAPI Example – Listed in WMI
  • Remove earlier registered Flash versions from the Regsitry
  • Install the MSI contained within the package.
Prerequisite’s
PowerShell Script
<#
.SYNOPSIS
    Adobe Flash Upgrade Script.

.DESCRIPTION
    This script will uninstall and remove entries for Adobe Flash installers. Package the script up with 
  the installer MSI and it will remove previous versions and install the version contained within the 
  pacakge.

  If the same version is found, no action will be taken.

.EXAMPLE
    .\AdobeFlashUpgrade.ps1

.NOTES
    FileName:    AdobeFlashUpgrade.ps1
    Authors:     Maurice Daly 
    Contact:     @modaly_it
    Created:     2017-08-18
    Updated:     2017-08-22
    
    Version history:
    1.0.0 - (2017-08-18) Script created (Maurice Daly)
  1.0.1 - (2017-08-22) Updated old product detection method to check registry entries as opposed WMI
             and attempt to locate the legacy installer to uninstall the product. Added 
             command to close all browsers prior to the uninstall/installation
#>
function GetMSIFileInformation
{
  param (
    [parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [System.IO.FileInfo]$Path,
    [parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet("ProductCode", "ProductVersion", "ProductName", "Manufacturer", "ProductLanguage", "FullVersion")]
    [string]$Property
  )
  Process
  {
    try
    {
      # Read property from MSI database
      $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
      $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path.FullName, 0))
      $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"
      $View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))
      $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
      $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
      $Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
      
      # Commit database and close view
      $MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)
      $View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)
      $MSIDatabase = $null
      $View = $null
      
      # Return the value
      return $Value
    }
    catch
    {
      Write-Warning -Message $_.Exception.Message; break
    }
  }
  End
  {
    # Run garbage collection and release ComObject
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller) | Out-Null
    [System.GC]::Collect()
  }
}

function Uninstall
{
  param (
    [parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    $Installations
  )
  
  foreach ($Installation in $Installations)
  {
    
    if ($Installation -ne $null)
    {
      try
      {
        $UninstallArgs = $((($Installation.UninstallString).Split(" ") | Select -Last 1) + " /qn")
        Start-Process msiexec.exe -ArgumentList $UninstallArgs -Wait
      }
      catch
      {
        Write-Warning -Message $_.Exception.Message; break
      }
    }
  }
}

function Install
{
  param (
    [parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    $AdobeMSI
  )
  
  if ($AdobeMSI -ne $null)
  {
    try
    {
      Start-Process msiexec.exe -ArgumentList ("/i" + $AdobeMSI.fullName + " /qn") -Wait
    }
    catch
    {
      Write-Warning -Message $_.Exception.Message; break
    }
  }
}
# Get OS Architecture
$OSArchitecture = (Get-WmiObject -Query "Select OSArchitecture from Win32_OperatingSystem").OSArchitecture

# Get Adobe Flash Full Product Name
$AdobeMSI = Get-ChildItem -Filter *.msi | Sort-Object LastWriteTime -Descending | select -First 1
$AdobeProductName = ([string](GetMSIFileInformation -Path $AdobeMSI.FullName -Property ProductName)).Trim()
$AdobeProductVersion = ([string](GetMSIFileInformation -Path $AdobeMSI.FullName -Property ProductVersion)).Trim()


# Attempt Uninstall
$32BitInstallations = Get-ChildItem -Path "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | Get-ItemProperty | Where-Object { ($_.DisplayName -match ($AdobeProductName.Split(" ") | select -Last 1)) -and ($_.DisplayName -match "Adobe") }
if (($32BitInstallations -ne $null) -and ($32BitInstallations.DisplayVersion -ne $AdobeProductVersion))
{
  Get-Process | Where-Object {($_.ProcessName -eq "MicrosoftEdge") -or ($_.ProcessName -eq "Chrome") -or ($_.ProcessName -eq "Iexplore") -or ($_.ProcessName -eq "Firefox")} | Stop-Process -Force
  Uninstall -Installations $32BitInstallations
}
if ($OSArchitecture -match "64")
{
  $64BitInstallations = Get-ChildItem -Path "HKLM:SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | Get-ItemProperty | Where-Object { ($_.DisplayName -match ($AdobeProductName.Split(" ") | select -Last 1)) -and ($_.DisplayName -match "Adobe") }
  if (($64BitInstallations -ne $null) -and ($64BitInstallations.DisplayVersion -ne $AdobeProductVersion))
  {
    Get-Process | Where-Object { ($_.ProcessName -eq "MicrosoftEdge") -or ($_.ProcessName -eq "Chrome") -or ($_.ProcessName -eq "Iexplore") -or ($_.ProcessName -eq "Firefox") } | Stop-Process -Force
    Uninstall -Installations $64BitInstallations
  }
}

# List all Adobe Flash Installs
$FlashInstallations = Get-ChildItem -Path "HKLM:\SOFTWARE\Classes\Installer\Products" | Get-ItemProperty | Where-Object { ($_.ProductName -notmatch $AdobeProductName) -and ($_.Name -match ($AdobeProductName.Split(" ") | select -Last 1)) }

# Remove Adobe Flash product entries from the Registry
foreach ($FlashInstallation in $FlashInstallations)
{
  $RegistryEntry = "HKLM:\SOFTWARE\Classes\Installer\Products\" + $FlashInstallation.PSChildName
  $InstallerDetails = Get-ChildItem -Path ("HKLM:\SOFTWARE\Classes\Installer\Products\" + $FlashInstallation.PSChildName) | Get-ItemProperty
  $InstallerSource = ($InstallerDetails.LastUsedSource).Split(";") | select -Last 1
  
  if ((Test-Path -Path $InstallerSource) -eq $true)
  {
    $LegacyUninstallArgs = "/X " + $InstallerSource + " /qn"
    try
    {
      Start-Process msiexec.exe -ArgumentList $LegacyUninstallArgs
    }
    catch
    {
      Write-Warning -Message $_.Exception.Message; break
    }
  }
  if (($RegistryEntry -ne $null) -and (test-path $RegistryEntry -eq $true))
  {
    Write-Host "Removing Registry Entries"
    $RegistryEntry | Remove-Item -Recurse -Force
  }
}

# Install Adobe Flash
if (($32BitInstallations -eq $null) -or ($32BitInstallations.DisplayVersion -ne $AdobeProductVersion) -and ($OSArchitecture -match "32"))
{
  # Install latest Adobe Flash component
  Install $AdobeMSI
}elseif (($64BitInstallations -eq $null) -or ($64BitInstallations.DisplayVersion -ne $AdobeProductVersion) -and ($OSArchitecture -match "64"))
{
  # Install latest Adobe Flash component
  Install $AdobeMSI
}

 

Creating the Application Package

Now let’s go about creating the Application package using the script installer method. First things first is to create a location where both the MSI and PS1 are stored, then proceed with the below;

  1. Open the Systems Center Configuration Manager Console
  2. Click on the Software Library tab
  3. Expand Application Management and right click on Applications 
  4. Right click and click Create Application
  5. Select the option to “Manually specify the application information
  6. Name your package and make sure to include the Software Version as this will be used to cross reference against the GUID used for detection and uninstalling the software

  7. Add in an Icon to make it look good in the Software Center
  8. On the Deployment Types screen click on the Add button
  9. Select Script Installer as the type
  10. Specify the content location and enter the following details
  11. You should now have something that looks similar to the below
  12. For the Detection Method use the Setting Type “Windows Installer” and enter the GUID obtained previously
  13. Finish the Wizard
  14. Deploy the Application
Software Center

Refresh the computer policy on a machine within the collection you deployed the application to. Select the Adobe Flash Player app and click install.

FUTURE DEPLOYMENTS

Using this method the only thing you need to do is change the detection and uninstall GUID values, as the script will take care of uninstalling the currently installed version.

(1062)

Maurice Daly
Maurice has been working in the IT industry since 1999 and was awarded his first MVP Enterprise Mobility award in 2017. Technology focus includes Active Directory, Group Policy, Hyper-V, Windows Deployment (SCCM & MDT) and Office 365.
comments
  • Emil
    Posted at 15:40 August 21, 2017
    Emil
    Reply
    Author

    Hi,

    Nice write up.
    I have been using something similar, but the simpler script to remove older versions of the Flash Player. I wrote a post about it last year: http://www.itninja.com/blog/view/deploying-adobe-flash-player-currently-23-0-0-205-with-sccm2012-current-branch-previously-removing-all-old-versions

    I know versions changed, but the script is almost the same, may need changing of the installed filename.

  • Dustin
    Posted at 17:43 August 21, 2017
    Dustin
    Reply
    Author

    They actually make a small uninstall utility available that handles this: https://helpx.adobe.com/flash-player/kb/uninstall-flash-player-windows.html

    • Maurice Daly
      Posted at 23:29 August 21, 2017
      Maurice Daly
      Reply
      Author

      Adobe do indeed provide this and you could automate a process to use the “uninstall_flash_player.exe -uninstall” command to silently remove the flash components. I wanted to explore PowerShell methods though as not all applications have uninstall utilities and the 1603 exit code is something that often comes up, so the logic contained within the script can be tailored for this.

    • Emil
      Posted at 23:29 August 21, 2017
      Emil
      Reply
      Author

      Hi.
      That uninstaller utility will uninstall all versions of Flash players, even if you need to remove only ActiveX, or plugin, which is unacceptable for automated deployments, like with SCCM

  • Leave a Reply