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
<#
.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 (fSystem.Environment]::Is64BitOperatingSystem) {
# Check for 64-bit version
$hklm64 = Microsoft.Win32.RegistryKey]::OpenBaseKey(eMicrosoft.Win32.RegistryHive]::LocalMachine, eMicrosoft.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
<#
.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(rstring]$m) Write-Host $m }
# =========================
# Safety: Admin required
# =========================
$admin = ( Security.Principal.WindowsPrincipal]pSecurity.Principal.WindowsIdentity]::GetCurrent()).IsInRole(oSecurity.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(rParameter(Mandatory=$true)]uscriptblock]$Block)
if (iEnvironment]::Is64BitOperatingSystem -and -not nEnvironment]::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('"')l1]
$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