AutomoxAutomox

Flash Removal Worklet for Windows

bachtenberg
August 26, 2025 at 5:47 pm UTC
#Worklets#Flash

Hello Automoxians!

I wanted to share a worklet I came up with after having some challenges with the existing Flash Removal worklet.  The worklet would run, but due to various ActiveX challenges and other issues, I was always dealing with things like the Flash.ocx control getting left behind which would continue to trigger our vulnerability scanner.  I finally took the Automox provided worklet and made some enhancements and came up with the following script that works every time (at least thus far!).  I hope this helps someone else out there!

Detection

powershell88 lines
<#

.SYNOPSIS
    Windows - Software Lifecycle - Uninstall Adobe Flash Player

.DESCRIPTION
    This worklet checks for instances of Adobe Flash Player, and if found, flags the device for remediation to uninstall the application.
    It will also clean up any residual files left behind by Adobe Flash Player.

    Since Adobe no longer supports Flash Player after December 31, 2020 and blocked Flash content from running in Flash Player beginning January 12, 2021,
    Adobe strongly recommends all users immediately uninstall Flash Player to help protect their systems.

.NOTES
    All parameters are predefined.
    Additional Activity Log output can be enabled by uncommenting $VerbosePreference.

.HISTORY
    Name: Manohar N
    Date: 18/02/2025
    Version: 2.1.2|
        - Changed Get-Win32App to Registry for better performance.

    Name: John Guarracino
    Date: 03/29/2024
    Version: 2.1.0
        - Fixed a bug where 64 bit artifacts were not cleaning up post uninstall.

    Name: John Guarracino
    Date: 02/26/2024
    Version: 2.0.0
        - Refactored to use WDK shared functions.
        - Added support for all Flash versions.
        - Updated worklet description and help comments.

    Name: John Guarracino
    Date: 06/23/2023
    Version: 1.0.0
        - Initial Release.

.LINK
    https://www.adobe.com/products/flashplayer/end-of-life.html

#>

#########################################
# PREDEFINED PARAMETERS

# Defining target application
$appName = 'Adobe Flash Player'

#########################################
# --! Evaluate Compliance !--

if ([System.Environment]::Is64BitOperatingSystem) {
    # Check for 64-bit version
    $hklm64 = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)
    $skey64 = $hklm64.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\")
    $unkeys64 = $skey64.GetSubKeyNames()
    $hklm64.Close()

    ForEach ($key in $unkeys64) {
        If ($skey64.OpenSubKey($key).GetValue('DisplayName') -match $appName) {
            Write-Output "$appName 64-bit was found. Flagging for remediation."
            Exit 1
        }
    }

    # Check for 32-bit version in Wow6432Node
    $skey32 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
    $unkeys32 = @(Get-ChildItem $skey32 -ErrorAction SilentlyContinue | Get-ItemProperty | Where-Object { ($_.DisplayName -match $appName) })

    If ($unkeys32) {
        Write-Output "$appName 32-bit was found. Flagging for remediation."
        Exit 1
    }
}

# Check for install on 32-bit system
$skey32 = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall'
$unkeys32 = @(Get-ChildItem $skey32 -ErrorAction SilentlyContinue | Get-ItemProperty | Where-Object { ($_.DisplayName -match $appName) })

If ($unkeys32) {
    Write-Output "$appName 32-bit was found. Flagging for remediation."
    Exit 1
}

Write-Output 'Device is compliant.'
exit 0

Remediation

 

powershell327 lines
<#
.SYNOPSIS
  Combined Adobe Flash Player remediation:
  - Run Adobe uninstall tool (forced)
  - Run any remaining recorded UninstallString(s)
  - Remove services, scheduled tasks, files, and registry keys (incl. ActiveX key)
  - Log to C:\Tools\FlashCleanup\

.NOTES
  Safe for Automox/Intune. Requires admin.
  Exit 0 = completed (cleanup attempted), nonzero = hard failure to start.
#>

$ErrorActionPreference = 'Continue'

# =========================
# Config / Constants
# =========================
$appName              = 'Adobe Flash Player'
$appArgs              = '-uninstall -force'
$url                  = 'https://fpdownload.macromedia.com/get/flashplayer/current/support/uninstall_flash_player.exe'
$workletID            = 'EC-392'     # Used for Automox WorkletCache (if available)
$processWaitTimeout   = 300          # seconds
$LogDir               = 'C:\Tools\FlashCleanup'
$LogFile              = Join-Path $LogDir ("FlashCleanup_{0}_{1}.log" -f $env:COMPUTERNAME, (Get-Date -Format 'yyyyMMdd_HHmmss'))

# =========================
# Logging
# =========================
if (-not (Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null }
Start-Transcript -Path $LogFile -Append | Out-Null
function Write-Log { param([string]$m) Write-Host $m }

# =========================
# Safety: Admin required
# =========================
$admin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $admin) {
  Write-Log "ERROR: Please run as Administrator."
  Stop-Transcript | Out-Null
  exit 1
}

# =========================
# Shell helper for sysnative
# =========================
function Shell {
  [CmdletBinding()]
  param([Parameter(Mandatory=$true)][scriptblock]$Block)
  if ([Environment]::Is64BitOperatingSystem -and -not [Environment]::Is64BitProcess) {
    & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -Command $Block
  } else {
    & $Block
  }
}

# =========================
# Cache directory (Automox-aware with fallback)
# =========================
$workletCacheDir = Join-Path $env:ProgramData "amagent\WorkletCache\$workletID"
try {
  if (Get-Command Get-WorkletCache -ErrorAction SilentlyContinue) {
    Write-Log "Using Automox Get-WorkletCache: $workletID"
    Get-WorkletCache -Name $workletID | Out-Null
  } else {
    if (-not (Test-Path $workletCacheDir)) {
      New-Item -Path $workletCacheDir -ItemType Directory -Force | Out-Null
    }
  }
} catch {
  if (-not (Test-Path $workletCacheDir)) {
    New-Item -Path $workletCacheDir -ItemType Directory -Force | Out-Null
  }
}
$uninstaller = Join-Path $workletCacheDir "uninstall_${appName}.exe"

# =========================
# Download Adobe uninstaller
# =========================
try {
  Write-Log "Downloading Adobe uninstaller to: $uninstaller"
  if (Get-Command Invoke-WebRequest -ErrorAction SilentlyContinue) {
    Invoke-WebRequest -Uri $url -OutFile $uninstaller -UseBasicParsing -ErrorAction Stop
  } else {
    (New-Object System.Net.WebClient).DownloadFile($url, $uninstaller)
  }
} catch {
  Write-Log "WARN: Failed to download uninstaller: $($_.Exception.Message)"
  # Proceed—other cleanup paths will still run
}

# =========================
# Run Adobe uninstaller (if present)
# =========================
if (Test-Path $uninstaller) {
  try {
    Write-Log "Running Adobe uninstaller..."
    $proc = Start-Process -FilePath $uninstaller -ArgumentList $appArgs -PassThru -WindowStyle Hidden
    $proc | Wait-Process -Timeout $processWaitTimeout -ErrorAction SilentlyContinue

    if (-not $proc.HasExited) {
      Write-Log "WARN: Uninstaller timeout; attempting to stop..."
      try { $proc | Stop-Process -Force } catch {}
    } elseif ($proc.ExitCode -notin 0, 3010, 1641) {
      Write-Log "WARN: Uninstaller returned non-success exit code: $($proc.ExitCode)"
    } else {
      Write-Log "Adobe uninstaller completed."
    }
  } catch {
    Write-Log "WARN: Failed to run uninstaller: $($_.Exception.Message)"
  }
} else {
  Write-Log "WARN: Uninstaller not found—skipping Adobe tool step."
}

# =========================
# Try vendor UninstallString(s) from registry
# =========================
$RegUninstallRoots = @(
  'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
  'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
)

function Run-UninstallString {
  param([string]$CommandLine)
  try {
    if ([string]::IsNullOrWhiteSpace($CommandLine)) { return }
    if ($CommandLine -match '(?i)msiexec\.exe') {
      Write-Log "Executing MSI uninstall: $CommandLine"
      Start-Process -FilePath "cmd.exe" -ArgumentList "/c $CommandLine /qn /norestart" -Wait -WindowStyle Hidden
    } else {
      $exe = $null; $args = ""
      if ($CommandLine.StartsWith('"')) {
        $exe = $CommandLine.Split('"')[1]
        $args = $CommandLine.Substring($exe.Length + 2).Trim()
      } else {
        $parts = $CommandLine.Split(' ',2)
        $exe = $parts[0]
        if ($parts.Count -gt 1) { $args = $parts[1] }
      }
      if ($args -notmatch '(?i)(/quiet|/qn|/silent|-silent|/S)') {
        $args = "$args /quiet /norestart"
      }
      Write-Log "Executing EXE uninstall: `"$exe`" $args"
      Start-Process -FilePath $exe -ArgumentList $args -Wait -WindowStyle Hidden
    }
  } catch {
    Write-Log "WARN: UninstallString failed: $($_.Exception.Message)"
  }
}

foreach ($root in $RegUninstallRoots) {
  if (Test-Path $root) {
    $items = Get-ChildItem $root | ForEach-Object {
      try { Get-ItemProperty $_.PsPath } catch {}
    } | Where-Object {
      $_.DisplayName -match '(?i)Adobe\s*Flash\s*Player'
    }

    foreach ($item in $items) {
      if ($item.DisplayName) { Write-Log "Found installed Flash entry: $($item.DisplayName)" }
      if ($item.UninstallString) { Run-UninstallString -CommandLine $item.UninstallString }
    }
  }
}

# =========================
# Remove services & scheduled tasks
# =========================
$ServiceNames = @('AdobeFlashPlayerUpdateSvc','AdobeFlashPlayerUpdateService')
foreach ($svc in $ServiceNames) {
  $s = Get-Service -Name $svc -ErrorAction SilentlyContinue
  if ($s) {
    try {
      if ($s.Status -ne 'Stopped') { Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue }
      Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
      sc.exe delete $svc | Out-Null
      Write-Log "Removed service: $svc"
    } catch {
      Write-Log "WARN: Failed removing service $svc : $($_.Exception.Message)"
    }
  }
}

$ScheduledTaskNamesLike = @(
  'Adobe Flash Player Updater',
  'Adobe Flash Player PPAPI Notifier',
  'Adobe Flash Player NPAPI Notifier',
  'Adobe Flash Player Notifier'
)
foreach ($name in $ScheduledTaskNamesLike) {
  try {
    $t = Get-ScheduledTask -TaskName $name -ErrorAction SilentlyContinue
    if ($t) {
      Unregister-ScheduledTask -TaskName $name -Confirm:$false -ErrorAction SilentlyContinue
      Write-Log "Removed scheduled task: $name"
    }
  } catch {
    Write-Log "WARN: Failed removing task $name : $($_.Exception.Message)"
  }
}

# =========================
# Unregister & remove binaries and folders
# =========================
$FlashDirs = @("$env:WINDIR\System32\Macromed\Flash", "$env:WINDIR\SysWOW64\Macromed\Flash")

# Try to run any FlashUtil*.exe removal switches if still present
Shell {
  $dirs = @('C:\Windows\System32\Macromed\Flash','C:\Windows\SysWOW64\Macromed\Flash')
  foreach ($dir in $dirs) {
    if (Test-Path $dir) {
      Get-ChildItem -Path $dir -Filter 'FlashUtil*.exe' -ErrorAction SilentlyContinue | ForEach-Object {
        try {
          Start-Process -FilePath $_.FullName -ArgumentList '-uninstall -force' -WindowStyle Hidden -Wait -ErrorAction SilentlyContinue
        } catch {}
      }
    }
  }
}

# Best-effort OCX unregister before delete
foreach ($path in $FlashDirs) {
  if (Test-Path $path) {
    Get-ChildItem -Path $path -Filter *.ocx -ErrorAction SilentlyContinue | ForEach-Object {
      try {
        Start-Process -FilePath "$env:WINDIR\System32\regsvr32.exe" -ArgumentList "/u /s `"$($_.FullName)`"" -Wait -WindowStyle Hidden -ErrorAction SilentlyContinue
      } catch {}
    }
  }
}

# Kill any processes locking the Flash folder (best effort)
try {
  Get-Process -ErrorAction SilentlyContinue | ForEach-Object {
    try {
      if ($_.Modules.FileName -like "*\Macromed\Flash\*") { Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue }
    } catch {}
  }
} catch {}

# --- Enhanced, ownership-aware removal ---
function Remove-FolderSafe {
  param([Parameter(Mandatory=$true)][string]$Path)

  try {
    if (-not (Test-Path -LiteralPath $Path)) { return }

    # Clear read-only/hidden attributes
    try {
      Get-ChildItem -LiteralPath $Path -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object {
        try { $_.Attributes = 'Normal' } catch {}
      }
    } catch {}

    # Take ownership & grant admins full control (via sysnative-safe Shell)
    try {
      Shell { param($p) takeown /f "$p" /r /d y | Out-Null } -Args $Path
      Shell { param($p) icacls "$p" /grant Administrators:F /t /c | Out-Null } -Args $Path
    } catch {
      Write-Log "WARN: Ownership/ACL grant failed on ${Path}: $($_.Exception.Message)"
    }

    # Final delete
    Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop
    Write-Log "Removed folder: ${Path}"
  }
  catch {
    Write-Log "WARN: Failed removing folder ${Path}: $($_.Exception.Message)"
  }
}

foreach ($d in $FlashDirs) { Remove-FolderSafe -Path $d }

# =========================
# Registry cleanup
# =========================
# Your specific ActiveX key:
Remove-Item -Path 'HKLM:\SOFTWARE\WOW6432NODE\MICROSOFT\WINDOWS\CURRENTVERSION\UNINSTALL\ADOBE FLASH PLAYER ACTIVEX' -Recurse -Force -ErrorAction SilentlyContinue

$FlashRegRoots = @(
  'HKLM:\SOFTWARE\Macromedia\FlashPlayer',
  'HKLM:\SOFTWARE\WOW6432Node\Macromedia\FlashPlayer',
  'HKLM:\SOFTWARE\Policies\Adobe\FlashPlayer',
  'HKLM:\SOFTWARE\WOW6432Node\Policies\Adobe\FlashPlayer'
)
foreach ($rk in $FlashRegRoots) {
  try {
    if (Test-Path $rk) {
      Remove-Item -Path $rk -Recurse -Force -ErrorAction Stop
      Write-Log "Removed registry path: $rk"
    }
  } catch {
    Write-Log "WARN: Failed removing registry path $rk : $($_.Exception.Message)"
  }
}

# Remove lingering Uninstall subkeys that look like Flash but may lack DisplayName
foreach ($root in $RegUninstallRoots) {
  if (Test-Path $root) {
    Get-ChildItem $root | ForEach-Object {
      try { Get-ItemProperty $_.PsPath } catch {}
    } | Where-Object {
      ($_.DisplayName -match '(?i)Adobe\s*Flash\s*Player') -or
      ($_.QuietDisplayName -match '(?i)Adobe\s*Flash\s*Player') -or
      ($_.InstallLocation -match '(?i)\\Macromed\\Flash')
    } | ForEach-Object {
      try {
        Remove-Item -Path $_.PsPath -Recurse -Force -ErrorAction Stop
        Write-Log "Removed lingering uninstall key: $($_.PsPath)"
      } catch {
        Write-Log "WARN: Failed removing lingering uninstall key $($_.PsPath) : $($_.Exception.Message)"
      }
    }
  }
}

# =========================
# Cleanup uninstaller
# =========================
try {
  if (Test-Path $uninstaller) { Remove-Item -Path $uninstaller -Force -ErrorAction SilentlyContinue }
} catch {}

Write-Log "Adobe Flash cleanup completed."
Stop-Transcript | Out-Null
exit 0

 

This discussion is archived and closed to replies. Continue it in the Automox Community Slack.

Discuss on Slack

Replies (1)

SteveMDecember 17, 2025
#1

​@bachtenberg thanks for this!  I want to test it out.  May I ask, are you running this with any sort of notifications configured for the end user?  Or is it done totally in the background?