|
<# |
|
.SYNOPSIS |
|
Complete Google Chrome uninstaller for Windows with comprehensive cleanup. |
|
|
|
.DESCRIPTION |
|
This script completely removes Google Chrome from Windows systems including: |
|
- Chrome application files |
|
- User data (bookmarks, history, passwords, extensions) |
|
- Registry keys and policies |
|
- Scheduled tasks |
|
- Google Update services |
|
- Shortcuts and temporary files |
|
|
|
The script automatically elevates to administrator privileges and uses multiple |
|
uninstall methods with automatic fallback: |
|
1. winget (Windows Package Manager) - Modern, recommended |
|
2. Native Chrome setup.exe - Direct Chrome uninstaller |
|
3. Registry UninstallString - Alternative approach |
|
4. WMI/CIM - Last resort programmatic method |
|
5. Manual cleanup - Always performed to ensure complete removal |
|
|
|
.PARAMETER WhatIf |
|
Dry-run mode. Scans and reports what would be removed without making any changes. |
|
SAFE for testing on systems where Chrome needs to remain installed. |
|
|
|
.PARAMETER Verbose |
|
Enable detailed output showing every operation and file/registry key being processed. |
|
|
|
.PARAMETER Silent |
|
Silent mode with minimal console output (errors only). Useful for automation. |
|
Logging to file still occurs unless -NoLog is specified. |
|
|
|
.PARAMETER KeepUserData |
|
Preserve user data directory (bookmarks, passwords, history, extensions). |
|
Only removes the Chrome application and system-level components. |
|
|
|
.PARAMETER LogPath |
|
Custom path for the log file. Default: $env:TEMP\ChromeUninstall_yyyyMMdd_HHmmss.log |
|
|
|
.PARAMETER NoLog |
|
Disable logging to file. All operations will still be shown on console (unless -Silent). |
|
|
|
.EXAMPLE |
|
.\Uninstall-GoogleChrome.ps1 -WhatIf |
|
Scan and report what would be removed without making any changes (SAFE for testing). |
|
|
|
.EXAMPLE |
|
.\Uninstall-GoogleChrome.ps1 -WhatIf -Verbose |
|
Detailed scan with verbose output showing all detected components. |
|
|
|
.EXAMPLE |
|
.\Uninstall-GoogleChrome.ps1 |
|
Uninstall Chrome completely with default settings. |
|
|
|
.EXAMPLE |
|
.\Uninstall-GoogleChrome.ps1 -KeepUserData |
|
Uninstall Chrome but preserve bookmarks, passwords, and other user data. |
|
|
|
.EXAMPLE |
|
.\Uninstall-GoogleChrome.ps1 -Silent -LogPath "C:\Logs\chrome_removal.log" |
|
Silent uninstall with custom log file location (for automation). |
|
|
|
.EXAMPLE |
|
irm https://gist.githubusercontent.com/emilwojcik93/[GIST_ID]/raw/Uninstall-GoogleChrome.ps1 | iex |
|
Remote execution via Invoke-RestMethod (simple, no parameters). |
|
|
|
.EXAMPLE |
|
&([ScriptBlock]::Create((irm https://gist.githubusercontent.com/emilwojcik93/[GIST_ID]/raw/Uninstall-GoogleChrome.ps1))) -WhatIf -Verbose |
|
Remote execution with parameters using ScriptBlock pattern. |
|
|
|
.NOTES |
|
File Name : Uninstall-GoogleChrome.ps1 |
|
Author : Emil Wojcik (emilwojcik93) |
|
Prerequisite : PowerShell 5.1 or higher |
|
Version : 1.0 |
|
Date : 2025-12-29 |
|
|
|
Exit Codes: |
|
0 - Success (Chrome removed or not installed) |
|
1 - Chrome found but removal failed |
|
2 - Admin elevation failed or denied |
|
3 - Chrome processes locked and couldn't be stopped |
|
|
|
.LINK |
|
https://gist.github.com/emilwojcik93 |
|
#> |
|
|
|
[CmdletBinding(SupportsShouldProcess=$true)] |
|
param( |
|
[Parameter(Mandatory=$false)] |
|
[switch]$Silent, |
|
|
|
[Parameter(Mandatory=$false)] |
|
[switch]$KeepUserData, |
|
|
|
[Parameter(Mandatory=$false)] |
|
[ValidateScript({ |
|
$parentDir = Split-Path $_ -Parent |
|
if ($parentDir -and !(Test-Path $parentDir)) { |
|
throw "Directory does not exist: $parentDir" |
|
} |
|
$true |
|
})] |
|
[string]$LogPath, |
|
|
|
[Parameter(Mandatory=$false)] |
|
[switch]$NoLog |
|
) |
|
|
|
# Script version |
|
$script:ScriptVersion = "1.0" |
|
$script:ScriptName = "Chrome Uninstaller" |
|
|
|
# Global variables |
|
$script:LogFile = $null |
|
$script:ChromeDetected = $false |
|
$script:ChromeVersion = $null |
|
$script:ChromePath = $null |
|
$script:IsWhatIfMode = $false |
|
$script:IsVerboseMode = $false |
|
$script:IsSilentMode = $Silent.IsPresent |
|
$script:ComponentsFound = @{ |
|
Processes = @() |
|
Services = @() |
|
Directories = @() |
|
RegistryKeys = @() |
|
ScheduledTasks = @() |
|
Shortcuts = @() |
|
} |
|
|
|
#region Helper Functions |
|
|
|
function Write-Log { |
|
<# |
|
.SYNOPSIS |
|
Dual-output logging function (console + file) |
|
#> |
|
[CmdletBinding()] |
|
param( |
|
[Parameter(Mandatory=$true)] |
|
[string]$Message, |
|
|
|
[Parameter(Mandatory=$false)] |
|
[ValidateSet('INFO', 'SUCCESS', 'WARNING', 'ERROR', 'VERBOSE')] |
|
[string]$Level = 'INFO', |
|
|
|
[Parameter(Mandatory=$false)] |
|
[ConsoleColor]$Color |
|
) |
|
|
|
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' |
|
$logMessage = "[$timestamp] [$Level] $Message" |
|
|
|
# Write to log file if enabled |
|
if (-not $NoLog -and $script:LogFile) { |
|
try { |
|
Add-Content -Path $script:LogFile -Value $logMessage -ErrorAction SilentlyContinue |
|
} catch { |
|
# Silently ignore log file errors |
|
} |
|
} |
|
|
|
# Write to console unless Silent mode (except for errors) |
|
if (-not $IsSilentMode -or $Level -eq 'ERROR') { |
|
# Skip verbose messages unless in verbose mode |
|
if ($Level -eq 'VERBOSE' -and -not $IsVerboseMode) { |
|
return |
|
} |
|
|
|
# Set color based on level |
|
if (-not $Color) { |
|
$Color = switch ($Level) { |
|
'SUCCESS' { 'Green' } |
|
'WARNING' { 'Yellow' } |
|
'ERROR' { 'Red' } |
|
'VERBOSE' { 'Gray' } |
|
default { 'White' } |
|
} |
|
} |
|
|
|
Write-Host "[$Level] $Message" -ForegroundColor $Color |
|
} |
|
} |
|
|
|
function Initialize-Logging { |
|
<# |
|
.SYNOPSIS |
|
Initialize the logging system |
|
#> |
|
if ($NoLog) { |
|
Write-Log "Logging to file is disabled" -Level INFO |
|
return |
|
} |
|
|
|
if ($LogPath) { |
|
$script:LogFile = $LogPath |
|
} else { |
|
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss' |
|
$script:LogFile = Join-Path $env:TEMP "ChromeUninstall_$timestamp.log" |
|
} |
|
|
|
try { |
|
# Create log file |
|
$null = New-Item -Path $script:LogFile -ItemType File -Force -ErrorAction Stop |
|
|
|
# Write header |
|
$header = @" |
|
================================================================================ |
|
$script:ScriptName v$script:ScriptVersion |
|
Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') |
|
User: $env:USERNAME |
|
Computer: $env:COMPUTERNAME |
|
OS: $(Get-WmiObject Win32_OperatingSystem | Select-Object -ExpandProperty Caption) |
|
PowerShell: $($PSVersionTable.PSVersion) |
|
Mode: $(if($IsWhatIfMode){'WhatIf (Dry-Run)'}else{'Execute'}) |
|
================================================================================ |
|
|
|
"@ |
|
Add-Content -Path $script:LogFile -Value $header |
|
|
|
if (-not $IsSilentMode) { |
|
Write-Host "Logging to: $script:LogFile" -ForegroundColor Cyan |
|
} |
|
} catch { |
|
Write-Host "[WARNING] Could not create log file: $_" -ForegroundColor Yellow |
|
$script:LogFile = $null |
|
} |
|
} |
|
|
|
function Test-AdminRights { |
|
<# |
|
.SYNOPSIS |
|
Check if running with administrator privileges |
|
#> |
|
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) |
|
return $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) |
|
} |
|
|
|
function Invoke-Elevate { |
|
<# |
|
.SYNOPSIS |
|
Elevate the script to administrator privileges |
|
#> |
|
Write-Log "This script requires administrator privileges" -Level WARNING |
|
Write-Log "Attempting to relaunch with elevation..." -Level INFO |
|
|
|
# Build argument list from bound parameters |
|
$argList = @() |
|
$PSBoundParameters.GetEnumerator() | ForEach-Object { |
|
$argList += if ($_.Value -is [switch] -and $_.Value) { |
|
"-$($_.Key)" |
|
} elseif ($_.Value) { |
|
"-$($_.Key) `"$($_.Value)`"" |
|
} |
|
} |
|
|
|
# Detect execution context |
|
$script = if ($PSCommandPath) { |
|
# Local file execution |
|
"& { & `"$($PSCommandPath)`" $argList }" |
|
} else { |
|
# Remote execution via irm - reconstruct ScriptBlock |
|
# Note: This will work if the script was called via ScriptBlock pattern |
|
Write-Log "Remote execution detected - parameters will be preserved" -Level VERBOSE |
|
# For remote elevation, we need to embed the URL or pass it through |
|
# Since we can't determine the original URL, user needs to run with admin from the start |
|
# or we can try to re-execute with parameters |
|
Write-Log "For remote execution, please run PowerShell as Administrator first" -Level ERROR |
|
exit 2 |
|
} |
|
|
|
# Determine PowerShell command |
|
$powershellcmd = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" } |
|
|
|
# Determine terminal/console to use |
|
$processCmd = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { $powershellcmd } |
|
|
|
try { |
|
$process = Start-Process $processCmd -ArgumentList "$powershellcmd -ExecutionPolicy Bypass -NoProfile -Command $script" -Verb RunAs -PassThru |
|
Write-Log "Script relaunched with elevation" -Level INFO |
|
exit 0 |
|
} catch { |
|
Write-Log "Failed to elevate privileges: $($_.Exception.Message)" -Level ERROR |
|
exit 2 |
|
} |
|
} |
|
|
|
function Find-ChromeInstallation { |
|
<# |
|
.SYNOPSIS |
|
Detect Chrome installation using multiple methods |
|
#> |
|
Write-Log "Detecting Chrome installation..." -Level INFO |
|
|
|
$chromeInfo = @{ |
|
Installed = $false |
|
Version = $null |
|
InstallPath = $null |
|
RegistryPath = $null |
|
UninstallString = $null |
|
} |
|
|
|
# Method 1: Check registry (most reliable) |
|
$registryPaths = @( |
|
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome', |
|
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome', |
|
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome' |
|
) |
|
|
|
foreach ($regPath in $registryPaths) { |
|
if (Test-Path $regPath) { |
|
Write-Log "Found Chrome registry entry: $regPath" -Level VERBOSE |
|
try { |
|
$regKey = Get-ItemProperty -Path $regPath -ErrorAction Stop |
|
$chromeInfo.Installed = $true |
|
$chromeInfo.Version = $regKey.Version |
|
$chromeInfo.RegistryPath = $regPath |
|
$chromeInfo.UninstallString = $regKey.UninstallString |
|
|
|
# Try to determine install path from DisplayIcon or InstallLocation |
|
if ($regKey.DisplayIcon) { |
|
$chromeInfo.InstallPath = Split-Path (Split-Path $regKey.DisplayIcon -Parent) -Parent |
|
} elseif ($regKey.InstallLocation) { |
|
$chromeInfo.InstallPath = $regKey.InstallLocation |
|
} |
|
|
|
Write-Log "Chrome detected: Version $($chromeInfo.Version)" -Level SUCCESS |
|
break |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error reading registry: $errMsg" -Level VERBOSE |
|
} |
|
} |
|
} |
|
|
|
# Method 2: Check file system |
|
if (-not $chromeInfo.Installed) { |
|
$installPaths = @( |
|
"${env:ProgramFiles(x86)}\Google\Chrome\Application", |
|
"$env:ProgramFiles\Google\Chrome\Application", |
|
"$env:LOCALAPPDATA\Google\Chrome\Application" |
|
) |
|
|
|
foreach ($path in $installPaths) { |
|
if (Test-Path $path) { |
|
Write-Log "Found Chrome installation directory: $path" -Level VERBOSE |
|
$chromeInfo.Installed = $true |
|
$chromeInfo.InstallPath = $path |
|
|
|
# Try to get version from chrome.exe |
|
$chromeExe = Join-Path $path "chrome.exe" |
|
if (Test-Path $chromeExe) { |
|
try { |
|
$versionInfo = (Get-Item $chromeExe).VersionInfo |
|
$chromeInfo.Version = $versionInfo.FileVersion |
|
Write-Log "Chrome detected: Version $($chromeInfo.Version)" -Level SUCCESS |
|
} catch { |
|
Write-Log "Found Chrome but couldn't determine version" -Level WARNING |
|
} |
|
} |
|
break |
|
} |
|
} |
|
} |
|
|
|
if (-not $chromeInfo.Installed) { |
|
Write-Log "Chrome is not installed on this system" -Level INFO |
|
} else { |
|
Write-Log "Installation path: $($chromeInfo.InstallPath)" -Level VERBOSE |
|
} |
|
|
|
return $chromeInfo |
|
} |
|
|
|
function Get-ChromeComponents { |
|
<# |
|
.SYNOPSIS |
|
Scan all Chrome-related components on the system |
|
#> |
|
Write-Log "Scanning for Chrome components..." -Level INFO |
|
|
|
# Check for running processes |
|
$processes = Get-Process -Name chrome -ErrorAction SilentlyContinue |
|
if ($processes) { |
|
$script:ComponentsFound.Processes = $processes |
|
Write-Log "Found $($processes.Count) Chrome process(es) running" -Level VERBOSE |
|
} |
|
|
|
# Check for services |
|
$services = @('gupdate', 'gupdatem') | ForEach-Object { |
|
Get-Service -Name $_ -ErrorAction SilentlyContinue |
|
} | Where-Object { $_ } |
|
|
|
if ($services) { |
|
$script:ComponentsFound.Services = $services |
|
Write-Log "Found $($services.Count) Google Update service(s)" -Level VERBOSE |
|
} |
|
|
|
# Check for directories |
|
$directories = @( |
|
"$env:LOCALAPPDATA\Google\Chrome", |
|
"$env:LOCALAPPDATA\Google\CrashReports", |
|
"$env:LOCALAPPDATA\Google\Update", |
|
"$env:ProgramFiles\Google\Chrome", |
|
"${env:ProgramFiles(x86)}\Google\Chrome", |
|
"$env:APPDATA\Google", |
|
"$env:ProgramData\Google\Chrome", |
|
"$env:ProgramData\Google\CrashReports" |
|
) |
|
|
|
foreach ($dir in $directories) { |
|
if (Test-Path $dir) { |
|
try { |
|
$size = (Get-ChildItem $dir -Recurse -ErrorAction SilentlyContinue | |
|
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB |
|
$script:ComponentsFound.Directories += @{ |
|
Path = $dir |
|
SizeMB = [math]::Round($size, 2) |
|
} |
|
Write-Log "Found directory: $dir ($([math]::Round($size, 2)) MB)" -Level VERBOSE |
|
} catch { |
|
$script:ComponentsFound.Directories += @{ |
|
Path = $dir |
|
SizeMB = 0 |
|
} |
|
Write-Log "Found directory: $dir (size unknown)" -Level VERBOSE |
|
} |
|
} |
|
} |
|
|
|
# Check for registry keys |
|
$registryKeys = @( |
|
'HKCU:\Software\Google\Chrome', |
|
'HKCU:\Software\Google\Update', |
|
'HKLM:\SOFTWARE\Google\Chrome', |
|
'HKLM:\SOFTWARE\Google\Update', |
|
'HKLM:\SOFTWARE\WOW6432Node\Google\Chrome', |
|
'HKLM:\SOFTWARE\WOW6432Node\Google\Update', |
|
'HKLM:\SOFTWARE\Policies\Google\Chrome', |
|
'HKCU:\Software\Policies\Google\Chrome', |
|
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome', |
|
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome', |
|
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome' |
|
) |
|
|
|
foreach ($key in $registryKeys) { |
|
if (Test-Path $key) { |
|
$script:ComponentsFound.RegistryKeys += $key |
|
Write-Log "Found registry key: $key" -Level VERBOSE |
|
} |
|
} |
|
|
|
# Check for scheduled tasks |
|
try { |
|
$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | |
|
Where-Object { $_.TaskPath -like "*Google*" -or $_.TaskName -like "*Chrome*" -or $_.TaskName -like "*Google*" } |
|
|
|
if ($tasks) { |
|
$script:ComponentsFound.ScheduledTasks = $tasks |
|
Write-Log "Found $($tasks.Count) Google/Chrome scheduled task(s)" -Level VERBOSE |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not scan scheduled tasks: $errMsg" -Level VERBOSE |
|
} |
|
|
|
# Check for shortcuts |
|
$shortcutLocations = @( |
|
"$env:USERPROFILE\Desktop", |
|
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs", |
|
"$env:ProgramData\Microsoft\Windows\Start Menu\Programs", |
|
"$env:PUBLIC\Desktop" |
|
) |
|
|
|
foreach ($location in $shortcutLocations) { |
|
if (Test-Path $location) { |
|
$shortcuts = Get-ChildItem -Path $location -Filter "*Chrome*.lnk" -Recurse -ErrorAction SilentlyContinue |
|
if ($shortcuts) { |
|
$script:ComponentsFound.Shortcuts += $shortcuts |
|
Write-Log "Found $($shortcuts.Count) shortcut(s) in $location" -Level VERBOSE |
|
} |
|
} |
|
} |
|
|
|
# Summary |
|
$totalComponents = $script:ComponentsFound.Processes.Count + |
|
$script:ComponentsFound.Services.Count + |
|
$script:ComponentsFound.Directories.Count + |
|
$script:ComponentsFound.RegistryKeys.Count + |
|
$script:ComponentsFound.ScheduledTasks.Count + |
|
$script:ComponentsFound.Shortcuts.Count |
|
|
|
Write-Log "Component scan complete: $totalComponents item(s) found" -Level INFO |
|
|
|
return $totalComponents -gt 0 |
|
} |
|
|
|
function Stop-ChromeProcesses { |
|
<# |
|
.SYNOPSIS |
|
Stop all running Chrome processes |
|
#> |
|
$processes = Get-Process -Name chrome -ErrorAction SilentlyContinue |
|
|
|
if (-not $processes) { |
|
Write-Log "No Chrome processes running" -Level VERBOSE |
|
return $true |
|
} |
|
|
|
if ($IsWhatIfMode) { |
|
Write-Log "Would stop $($processes.Count) Chrome process(es)" -Level INFO |
|
return $true |
|
} |
|
|
|
Write-Log "Stopping $($processes.Count) Chrome process(es)..." -Level INFO |
|
|
|
try { |
|
$processes | Stop-Process -Force -ErrorAction Stop |
|
Start-Sleep -Seconds 2 |
|
|
|
# Verify processes stopped |
|
$remainingProcesses = Get-Process -Name chrome -ErrorAction SilentlyContinue |
|
if ($remainingProcesses) { |
|
Write-Log "Warning: $($remainingProcesses.Count) Chrome process(es) could not be stopped" -Level WARNING |
|
return $false |
|
} |
|
|
|
Write-Log "All Chrome processes stopped successfully" -Level SUCCESS |
|
return $true |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error stopping Chrome processes: $errMsg" -Level ERROR |
|
return $false |
|
} |
|
} |
|
|
|
function Stop-GoogleServices { |
|
<# |
|
.SYNOPSIS |
|
Stop Google Update services |
|
#> |
|
$serviceNames = @('gupdate', 'gupdatem') |
|
$stoppedCount = 0 |
|
|
|
foreach ($serviceName in $serviceNames) { |
|
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue |
|
|
|
if ($service) { |
|
if ($IsWhatIfMode) { |
|
Write-Log "Would stop service: $serviceName" -Level INFO |
|
$stoppedCount++ |
|
} else { |
|
try { |
|
if ($service.Status -eq 'Running') { |
|
Write-Log "Stopping service: $serviceName" -Level VERBOSE |
|
Stop-Service -Name $serviceName -Force -ErrorAction Stop |
|
$stoppedCount++ |
|
} else { |
|
Write-Log "Service $serviceName is already stopped" -Level VERBOSE |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not stop service ${serviceName}: $errMsg" -Level WARNING |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ($stoppedCount -gt 0) { |
|
Write-Log "Stopped $stoppedCount Google Update service(s)" -Level SUCCESS |
|
} |
|
|
|
return $true |
|
} |
|
|
|
function Invoke-WingetUninstall { |
|
<# |
|
.SYNOPSIS |
|
Try to uninstall Chrome using winget |
|
#> |
|
Write-Log "Attempting uninstall via winget..." -Level INFO |
|
|
|
# Check if winget is available |
|
$winget = Get-Command winget -ErrorAction SilentlyContinue |
|
if (-not $winget) { |
|
Write-Log "winget is not available on this system" -Level VERBOSE |
|
return $false |
|
} |
|
|
|
Write-Log "winget detected: $($winget.Version)" -Level VERBOSE |
|
|
|
if ($IsWhatIfMode) { |
|
Write-Log "Would execute: winget uninstall --id Google.Chrome --silent --force" -Level INFO |
|
return $true |
|
} |
|
|
|
try { |
|
Write-Log "Executing: winget uninstall --id Google.Chrome --silent --force --accept-source-agreements" -Level VERBOSE |
|
$output = & winget uninstall --id Google.Chrome --silent --force --accept-source-agreements 2>&1 |
|
|
|
if ($LASTEXITCODE -eq 0) { |
|
Write-Log "Chrome uninstalled successfully via winget" -Level SUCCESS |
|
return $true |
|
} else { |
|
Write-Log "winget uninstall failed with exit code: $LASTEXITCODE" -Level WARNING |
|
Write-Log "Output: $output" -Level VERBOSE |
|
return $false |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error during winget uninstall: $errMsg" -Level WARNING |
|
return $false |
|
} |
|
} |
|
|
|
function Invoke-NativeUninstall { |
|
<# |
|
.SYNOPSIS |
|
Try to uninstall Chrome using native setup.exe |
|
#> |
|
param( |
|
[Parameter(Mandatory=$true)] |
|
[hashtable]$ChromeInfo |
|
) |
|
|
|
Write-Log "Attempting uninstall via native setup.exe..." -Level INFO |
|
|
|
# Try to get version from registry |
|
try { |
|
$version = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome' -ErrorAction Stop).Version |
|
|
|
if ($version) { |
|
$uninstallerPath = "${env:ProgramFiles(x86)}\Google\Chrome\Application\$version\Installer\setup.exe" |
|
|
|
if (Test-Path $uninstallerPath) { |
|
Write-Log "Found native uninstaller: $uninstallerPath" -Level VERBOSE |
|
|
|
if ($IsWhatIfMode) { |
|
Write-Log "Would execute: $uninstallerPath --uninstall --multi-install --chrome --system-level --force-uninstall" -Level INFO |
|
return $true |
|
} |
|
|
|
try { |
|
Write-Log "Executing native uninstaller..." -Level VERBOSE |
|
$process = Start-Process -FilePath $uninstallerPath -ArgumentList "--uninstall --multi-install --chrome --system-level --force-uninstall" -Wait -PassThru -NoNewWindow |
|
|
|
if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 19) { |
|
# Exit code 19 means "already uninstalled" which is OK |
|
Write-Log "Chrome uninstalled successfully via native setup.exe" -Level SUCCESS |
|
return $true |
|
} else { |
|
Write-Log "Native uninstaller failed with exit code: $($process.ExitCode)" -Level WARNING |
|
return $false |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error executing native uninstaller: $errMsg" -Level WARNING |
|
return $false |
|
} |
|
} |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not locate native uninstaller: $errMsg" -Level VERBOSE |
|
} |
|
|
|
return $false |
|
} |
|
|
|
function Invoke-RegistryUninstall { |
|
<# |
|
.SYNOPSIS |
|
Try to uninstall Chrome using registry UninstallString |
|
#> |
|
param( |
|
[Parameter(Mandatory=$true)] |
|
[hashtable]$ChromeInfo |
|
) |
|
|
|
Write-Log "Attempting uninstall via registry UninstallString..." -Level INFO |
|
|
|
if ($ChromeInfo.UninstallString) { |
|
Write-Log "Found UninstallString: $($ChromeInfo.UninstallString)" -Level VERBOSE |
|
|
|
if ($IsWhatIfMode) { |
|
Write-Log "Would execute: $($ChromeInfo.UninstallString) --force-uninstall" -Level INFO |
|
return $true |
|
} |
|
|
|
try { |
|
# Parse the uninstall string |
|
$uninstallCmd = $ChromeInfo.UninstallString |
|
if ($uninstallCmd -match '"([^"]+)"') { |
|
$exePath = $matches[1] |
|
$args = "--force-uninstall" |
|
|
|
Write-Log "Executing registry uninstaller..." -Level VERBOSE |
|
$process = Start-Process -FilePath $exePath -ArgumentList $args -Wait -PassThru -NoNewWindow |
|
|
|
if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 19) { |
|
Write-Log "Chrome uninstalled successfully via registry method" -Level SUCCESS |
|
return $true |
|
} else { |
|
Write-Log "Registry uninstaller failed with exit code: $($process.ExitCode)" -Level WARNING |
|
return $false |
|
} |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error executing registry uninstaller: $errMsg" -Level WARNING |
|
return $false |
|
} |
|
} |
|
|
|
Write-Log "No registry UninstallString found" -Level VERBOSE |
|
return $false |
|
} |
|
|
|
function Invoke-WMIUninstall { |
|
<# |
|
.SYNOPSIS |
|
Try to uninstall Chrome using WMI/CIM |
|
#> |
|
Write-Log "Attempting uninstall via WMI/CIM..." -Level INFO |
|
|
|
try { |
|
$chrome = Get-CimInstance -ClassName Win32_Product -ErrorAction Stop | |
|
Where-Object { $_.Name -eq 'Google Chrome' } |
|
|
|
if ($chrome) { |
|
Write-Log "Found Chrome via WMI" -Level VERBOSE |
|
|
|
if ($IsWhatIfMode) { |
|
Write-Log "Would invoke WMI Uninstall method" -Level INFO |
|
return $true |
|
} |
|
|
|
Write-Log "Invoking WMI Uninstall method (this may take a while)..." -Level VERBOSE |
|
$result = Invoke-CimMethod -InputObject $chrome -MethodName Uninstall |
|
|
|
if ($result.ReturnValue -eq 0) { |
|
Write-Log "Chrome uninstalled successfully via WMI" -Level SUCCESS |
|
return $true |
|
} else { |
|
Write-Log "WMI uninstall failed with return value: $($result.ReturnValue)" -Level WARNING |
|
return $false |
|
} |
|
} else { |
|
Write-Log "Chrome not found in WMI" -Level VERBOSE |
|
return $false |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error during WMI uninstall: $errMsg" -Level WARNING |
|
return $false |
|
} |
|
} |
|
|
|
function Invoke-ChromeUninstall { |
|
<# |
|
.SYNOPSIS |
|
Main uninstall function with fallback chain |
|
#> |
|
param( |
|
[Parameter(Mandatory=$true)] |
|
[hashtable]$ChromeInfo |
|
) |
|
|
|
Write-Log "Starting Chrome uninstall process..." -Level INFO |
|
Write-Log "Uninstall method priority: winget -> native setup.exe -> registry -> WMI -> manual" -Level INFO |
|
|
|
# Stop processes and services first |
|
if (-not (Stop-ChromeProcesses)) { |
|
if (-not $IsWhatIfMode) { |
|
Write-Log "Warning: Some Chrome processes could not be stopped" -Level WARNING |
|
Write-Log "Uninstall may fail or require a system restart" -Level WARNING |
|
} |
|
} |
|
|
|
Stop-GoogleServices |
|
|
|
# Try each uninstall method in order |
|
$uninstallMethods = @( |
|
@{ Name = "winget"; Function = { Invoke-WingetUninstall } }, |
|
@{ Name = "Native setup.exe"; Function = { Invoke-NativeUninstall -ChromeInfo $ChromeInfo } }, |
|
@{ Name = "Registry UninstallString"; Function = { Invoke-RegistryUninstall -ChromeInfo $ChromeInfo } }, |
|
@{ Name = "WMI/CIM"; Function = { Invoke-WMIUninstall } } |
|
) |
|
|
|
$uninstallSuccessful = $false |
|
|
|
foreach ($method in $uninstallMethods) { |
|
Write-Log "Trying method: $($method.Name)" -Level VERBOSE |
|
|
|
if (& $method.Function) { |
|
Write-Log "Uninstall successful using: $($method.Name)" -Level SUCCESS |
|
$uninstallSuccessful = $true |
|
break |
|
} else { |
|
Write-Log "Method failed: $($method.Name), trying next method..." -Level VERBOSE |
|
} |
|
} |
|
|
|
if (-not $uninstallSuccessful -and -not $IsWhatIfMode) { |
|
Write-Log "All uninstall methods failed, proceeding with manual cleanup only" -Level WARNING |
|
} |
|
|
|
return $uninstallSuccessful |
|
} |
|
|
|
function Remove-ChromeDirectories { |
|
<# |
|
.SYNOPSIS |
|
Remove all Chrome-related directories |
|
#> |
|
Write-Log "Removing Chrome directories..." -Level INFO |
|
|
|
$directories = @( |
|
"$env:LOCALAPPDATA\Google\CrashReports", |
|
"$env:LOCALAPPDATA\Google\Update", |
|
"$env:ProgramFiles\Google\Chrome", |
|
"${env:ProgramFiles(x86)}\Google\Chrome", |
|
"$env:APPDATA\Google", |
|
"$env:ProgramData\Google\Chrome", |
|
"$env:ProgramData\Google\CrashReports", |
|
"${env:ProgramFiles(x86)}\Google\Update", |
|
"$env:ProgramFiles\Google\Update" |
|
) |
|
|
|
# Add user data directory unless KeepUserData is specified |
|
if (-not $KeepUserData) { |
|
$directories = @("$env:LOCALAPPDATA\Google\Chrome") + $directories |
|
} else { |
|
Write-Log "Preserving user data directory (KeepUserData flag set)" -Level INFO |
|
} |
|
|
|
$removedCount = 0 |
|
|
|
foreach ($dir in $directories) { |
|
if (Test-Path $dir) { |
|
if ($IsWhatIfMode) { |
|
try { |
|
$size = (Get-ChildItem $dir -Recurse -ErrorAction SilentlyContinue | |
|
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB |
|
Write-Log "Would remove: $dir ($([math]::Round($size, 2)) MB)" -Level INFO |
|
} catch { |
|
Write-Log "Would remove: $dir" -Level INFO |
|
} |
|
$removedCount++ |
|
} else { |
|
try { |
|
Write-Log "Removing: $dir" -Level VERBOSE |
|
Remove-Item -Path $dir -Recurse -Force -ErrorAction Stop |
|
Write-Log "Removed: $dir" -Level SUCCESS |
|
$removedCount++ |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not remove $dir : $errMsg" -Level WARNING |
|
} |
|
} |
|
} else { |
|
Write-Log "Directory not found: $dir" -Level VERBOSE |
|
} |
|
} |
|
|
|
Write-Log "Removed $removedCount director(ies)" -Level SUCCESS |
|
} |
|
|
|
function Remove-ChromeRegistry { |
|
<# |
|
.SYNOPSIS |
|
Remove all Chrome-related registry keys |
|
#> |
|
Write-Log "Removing Chrome registry keys..." -Level INFO |
|
|
|
$registryKeys = @( |
|
'HKCU:\Software\Google\Chrome', |
|
'HKCU:\Software\Google\Update', |
|
'HKLM:\SOFTWARE\Google\Chrome', |
|
'HKLM:\SOFTWARE\Google\Update', |
|
'HKLM:\SOFTWARE\WOW6432Node\Google\Chrome', |
|
'HKLM:\SOFTWARE\WOW6432Node\Google\Update', |
|
'HKLM:\SOFTWARE\Policies\Google\Chrome', |
|
'HKCU:\Software\Policies\Google\Chrome', |
|
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome', |
|
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome', |
|
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome' |
|
) |
|
|
|
$removedCount = 0 |
|
|
|
foreach ($key in $registryKeys) { |
|
if (Test-Path $key) { |
|
if ($IsWhatIfMode) { |
|
Write-Log "Would remove registry key: $key" -Level INFO |
|
$removedCount++ |
|
} else { |
|
try { |
|
Write-Log "Removing registry key: $key" -Level VERBOSE |
|
Remove-Item -Path $key -Recurse -Force -ErrorAction Stop |
|
Write-Log "Removed: $key" -Level SUCCESS |
|
$removedCount++ |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not remove $key : $errMsg" -Level WARNING |
|
} |
|
} |
|
} else { |
|
Write-Log "Registry key not found: $key" -Level VERBOSE |
|
} |
|
} |
|
|
|
Write-Log "Removed $removedCount registry key(s)" -Level SUCCESS |
|
} |
|
|
|
function Remove-ChromeScheduledTasks { |
|
<# |
|
.SYNOPSIS |
|
Remove Chrome/Google scheduled tasks |
|
#> |
|
Write-Log "Removing scheduled tasks..." -Level INFO |
|
|
|
try { |
|
$tasks = Get-ScheduledTask -ErrorAction SilentlyContinue | |
|
Where-Object { $_.TaskPath -like "*Google*" -or $_.TaskName -like "*Chrome*" -or $_.TaskName -like "*Google*" } |
|
|
|
if (-not $tasks) { |
|
Write-Log "No Chrome/Google scheduled tasks found" -Level VERBOSE |
|
return |
|
} |
|
|
|
$removedCount = 0 |
|
|
|
foreach ($task in $tasks) { |
|
if ($IsWhatIfMode) { |
|
Write-Log "Would remove scheduled task: $($task.TaskName)" -Level INFO |
|
$removedCount++ |
|
} else { |
|
try { |
|
Write-Log "Removing scheduled task: $($task.TaskName)" -Level VERBOSE |
|
Unregister-ScheduledTask -TaskName $task.TaskName -TaskPath $task.TaskPath -Confirm:$false -ErrorAction Stop |
|
Write-Log "Removed: $($task.TaskName)" -Level SUCCESS |
|
$removedCount++ |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not remove task $($task.TaskName): $errMsg" -Level WARNING |
|
} |
|
} |
|
} |
|
|
|
Write-Log "Removed $removedCount scheduled task(s)" -Level SUCCESS |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error scanning scheduled tasks: $errMsg" -Level WARNING |
|
} |
|
} |
|
|
|
function Remove-ChromeShortcuts { |
|
<# |
|
.SYNOPSIS |
|
Remove Chrome desktop and start menu shortcuts |
|
#> |
|
Write-Log "Removing shortcuts..." -Level INFO |
|
|
|
$shortcutLocations = @( |
|
"$env:USERPROFILE\Desktop", |
|
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs", |
|
"$env:ProgramData\Microsoft\Windows\Start Menu\Programs", |
|
"$env:PUBLIC\Desktop" |
|
) |
|
|
|
$removedCount = 0 |
|
|
|
foreach ($location in $shortcutLocations) { |
|
if (Test-Path $location) { |
|
$shortcuts = Get-ChildItem -Path $location -Filter "*Chrome*.lnk" -Recurse -ErrorAction SilentlyContinue |
|
|
|
foreach ($shortcut in $shortcuts) { |
|
if ($IsWhatIfMode) { |
|
Write-Log "Would remove shortcut: $($shortcut.FullName)" -Level INFO |
|
$removedCount++ |
|
} else { |
|
try { |
|
Write-Log "Removing shortcut: $($shortcut.FullName)" -Level VERBOSE |
|
Remove-Item -Path $shortcut.FullName -Force -ErrorAction Stop |
|
Write-Log "Removed: $($shortcut.Name)" -Level SUCCESS |
|
$removedCount++ |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not remove $($shortcut.Name): $errMsg" -Level WARNING |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
Write-Log "Removed $removedCount shortcut(s)" -Level SUCCESS |
|
} |
|
|
|
function Remove-ChromeTempFiles { |
|
<# |
|
.SYNOPSIS |
|
Remove Chrome temporary files |
|
#> |
|
Write-Log "Cleaning temporary files..." -Level INFO |
|
|
|
$tempPatterns = @( |
|
"$env:TEMP\chrome*", |
|
"$env:TEMP\scoped*" |
|
) |
|
|
|
$removedCount = 0 |
|
|
|
foreach ($pattern in $tempPatterns) { |
|
$items = Get-ChildItem -Path (Split-Path $pattern -Parent) -Filter (Split-Path $pattern -Leaf) -ErrorAction SilentlyContinue |
|
|
|
foreach ($item in $items) { |
|
if ($IsWhatIfMode) { |
|
Write-Log "Would remove: $($item.FullName)" -Level VERBOSE |
|
$removedCount++ |
|
} else { |
|
try { |
|
Remove-Item -Path $item.FullName -Recurse -Force -ErrorAction Stop |
|
Write-Log "Removed: $($item.Name)" -Level VERBOSE |
|
$removedCount++ |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Could not remove $($item.Name): $errMsg" -Level VERBOSE |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ($removedCount -gt 0) { |
|
Write-Log "Cleaned $removedCount temporary file(s)" -Level SUCCESS |
|
} else { |
|
Write-Log "No temporary files to clean" -Level VERBOSE |
|
} |
|
} |
|
|
|
function Remove-GoogleUpdateServices { |
|
<# |
|
.SYNOPSIS |
|
Remove Google Update services |
|
#> |
|
Write-Log "Removing Google Update services..." -Level INFO |
|
|
|
$serviceNames = @('gupdate', 'gupdatem') |
|
$removedCount = 0 |
|
|
|
foreach ($serviceName in $serviceNames) { |
|
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue |
|
|
|
if ($service) { |
|
if ($IsWhatIfMode) { |
|
Write-Log "Would remove service: $serviceName" -Level INFO |
|
$removedCount++ |
|
} else { |
|
try { |
|
# Stop service first if running |
|
if ($service.Status -eq 'Running') { |
|
Stop-Service -Name $serviceName -Force -ErrorAction SilentlyContinue |
|
} |
|
|
|
Write-Log "Removing service: $serviceName" -Level VERBOSE |
|
$output = sc.exe delete $serviceName 2>&1 |
|
|
|
if ($LASTEXITCODE -eq 0) { |
|
Write-Log "Removed service: $serviceName" -Level SUCCESS |
|
$removedCount++ |
|
} else { |
|
Write-Log "Could not remove service $serviceName : $output" -Level WARNING |
|
} |
|
} catch { |
|
$errMsg = $_.Exception.Message |
|
Write-Log "Error removing service $serviceName : $errMsg" -Level WARNING |
|
} |
|
} |
|
} else { |
|
Write-Log "Service not found: $serviceName" -Level VERBOSE |
|
} |
|
} |
|
|
|
if ($removedCount -gt 0) { |
|
Write-Log "Removed $removedCount service(s)" -Level SUCCESS |
|
} |
|
} |
|
|
|
function Invoke-ComprehensiveCleanup { |
|
<# |
|
.SYNOPSIS |
|
Perform comprehensive cleanup of all Chrome components |
|
#> |
|
Write-Log "Starting comprehensive cleanup..." -Level INFO |
|
|
|
Remove-ChromeDirectories |
|
Remove-ChromeRegistry |
|
Remove-ChromeScheduledTasks |
|
Remove-ChromeShortcuts |
|
Remove-ChromeTempFiles |
|
Remove-GoogleUpdateServices |
|
|
|
Write-Log "Comprehensive cleanup complete" -Level SUCCESS |
|
} |
|
|
|
function Test-ChromeRemoval { |
|
<# |
|
.SYNOPSIS |
|
Verify that Chrome has been completely removed |
|
#> |
|
Write-Log "Verifying Chrome removal..." -Level INFO |
|
|
|
$issuesFound = @() |
|
|
|
# Check for running processes |
|
$processes = Get-Process -Name chrome -ErrorAction SilentlyContinue |
|
if ($processes) { |
|
$issuesFound += "Chrome processes still running: $($processes.Count)" |
|
Write-Log "WARNING: $($processes.Count) Chrome process(es) still running" -Level WARNING |
|
} else { |
|
Write-Log "β No Chrome processes running" -Level SUCCESS |
|
} |
|
|
|
# Check key directories |
|
$keyDirs = @( |
|
"$env:LOCALAPPDATA\Google\Chrome", |
|
"$env:ProgramFiles\Google\Chrome", |
|
"${env:ProgramFiles(x86)}\Google\Chrome" |
|
) |
|
|
|
$remainingDirs = @() |
|
foreach ($dir in $keyDirs) { |
|
if (Test-Path $dir) { |
|
$remainingDirs += $dir |
|
$issuesFound += "Directory still exists: $dir" |
|
} |
|
} |
|
|
|
if ($remainingDirs.Count -eq 0) { |
|
Write-Log "β All Chrome directories removed" -Level SUCCESS |
|
} else { |
|
Write-Log "WARNING: $($remainingDirs.Count) director(ies) still exist" -Level WARNING |
|
foreach ($dir in $remainingDirs) { |
|
Write-Log " - $dir" -Level WARNING |
|
} |
|
} |
|
|
|
# Check key registry entries |
|
$keyRegKeys = @( |
|
'HKCU:\Software\Google\Chrome', |
|
'HKLM:\SOFTWARE\Google\Chrome', |
|
'HKLM:\SOFTWARE\WOW6432Node\Google\Chrome' |
|
) |
|
|
|
$remainingKeys = @() |
|
foreach ($key in $keyRegKeys) { |
|
if (Test-Path $key) { |
|
$remainingKeys += $key |
|
$issuesFound += "Registry key still exists: $key" |
|
} |
|
} |
|
|
|
if ($remainingKeys.Count -eq 0) { |
|
Write-Log "β All Chrome registry keys removed" -Level SUCCESS |
|
} else { |
|
Write-Log "WARNING: $($remainingKeys.Count) registry key(s) still exist" -Level WARNING |
|
foreach ($key in $remainingKeys) { |
|
Write-Log " - $key" -Level WARNING |
|
} |
|
} |
|
|
|
# Check for services |
|
$services = @('gupdate', 'gupdatem') | ForEach-Object { |
|
Get-Service -Name $_ -ErrorAction SilentlyContinue |
|
} | Where-Object { $_ } |
|
|
|
if ($services) { |
|
Write-Log "WARNING: $($services.Count) Google Update service(s) still exist" -Level WARNING |
|
$issuesFound += "Services still exist: $($services.Count)" |
|
} else { |
|
Write-Log "β No Google Update services found" -Level SUCCESS |
|
} |
|
|
|
return $issuesFound |
|
} |
|
|
|
function Show-WhatIfReport { |
|
<# |
|
.SYNOPSIS |
|
Display comprehensive WhatIf report |
|
#> |
|
Write-Host "" |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host " WhatIf Mode - Summary Report" -ForegroundColor White |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host "" |
|
|
|
Write-Host "Chrome Installation:" -ForegroundColor Yellow |
|
Write-Host " Version: $script:ChromeVersion" -ForegroundColor White |
|
Write-Host " Path: $script:ChromePath" -ForegroundColor White |
|
Write-Host "" |
|
|
|
if ($script:ComponentsFound.Processes.Count -gt 0) { |
|
Write-Host "Running Processes: $($script:ComponentsFound.Processes.Count)" -ForegroundColor Yellow |
|
} |
|
|
|
if ($script:ComponentsFound.Services.Count -gt 0) { |
|
Write-Host "Services: $($script:ComponentsFound.Services.Count)" -ForegroundColor Yellow |
|
foreach ($svc in $script:ComponentsFound.Services) { |
|
Write-Host " - $($svc.Name) ($($svc.Status))" -ForegroundColor Gray |
|
} |
|
} |
|
|
|
if ($script:ComponentsFound.Directories.Count -gt 0) { |
|
Write-Host "" |
|
Write-Host "Directories to Remove: $($script:ComponentsFound.Directories.Count)" -ForegroundColor Yellow |
|
$totalSize = 0 |
|
foreach ($dir in $script:ComponentsFound.Directories) { |
|
$totalSize += $dir.SizeMB |
|
Write-Host " - $($dir.Path) ($($dir.SizeMB) MB)" -ForegroundColor Gray |
|
} |
|
Write-Host " Total size: $([math]::Round($totalSize, 2)) MB" -ForegroundColor White |
|
} |
|
|
|
if ($script:ComponentsFound.RegistryKeys.Count -gt 0) { |
|
Write-Host "" |
|
Write-Host "Registry Keys to Remove: $($script:ComponentsFound.RegistryKeys.Count)" -ForegroundColor Yellow |
|
if ($IsVerboseMode) { |
|
foreach ($key in $script:ComponentsFound.RegistryKeys) { |
|
Write-Host " - $key" -ForegroundColor Gray |
|
} |
|
} |
|
} |
|
|
|
if ($script:ComponentsFound.ScheduledTasks.Count -gt 0) { |
|
Write-Host "" |
|
Write-Host "Scheduled Tasks to Remove: $($script:ComponentsFound.ScheduledTasks.Count)" -ForegroundColor Yellow |
|
foreach ($task in $script:ComponentsFound.ScheduledTasks) { |
|
Write-Host " - $($task.TaskName)" -ForegroundColor Gray |
|
} |
|
} |
|
|
|
if ($script:ComponentsFound.Shortcuts.Count -gt 0) { |
|
Write-Host "" |
|
Write-Host "Shortcuts to Remove: $($script:ComponentsFound.Shortcuts.Count)" -ForegroundColor Yellow |
|
if ($IsVerboseMode) { |
|
foreach ($shortcut in $script:ComponentsFound.Shortcuts) { |
|
Write-Host " - $($shortcut.Name)" -ForegroundColor Gray |
|
} |
|
} |
|
} |
|
|
|
Write-Host "" |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host " No changes were made (WhatIf mode)" -ForegroundColor Green |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host "" |
|
} |
|
|
|
function Show-FinalReport { |
|
<# |
|
.SYNOPSIS |
|
Display final execution report |
|
#> |
|
param( |
|
[Parameter(Mandatory=$false)] |
|
[array]$VerificationIssues = @() |
|
) |
|
|
|
Write-Host "" |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host " Chrome Uninstall Complete" -ForegroundColor White |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host "" |
|
|
|
if ($VerificationIssues.Count -eq 0) { |
|
Write-Host " β Chrome has been completely removed" -ForegroundColor Green |
|
Write-Host " β All components cleaned successfully" -ForegroundColor Green |
|
} else { |
|
Write-Host " β Chrome has been removed with warnings:" -ForegroundColor Yellow |
|
foreach ($issue in $VerificationIssues) { |
|
Write-Host " - $issue" -ForegroundColor Yellow |
|
} |
|
Write-Host "" |
|
Write-Host " Note: Some items may require a system restart to complete removal" -ForegroundColor Yellow |
|
} |
|
|
|
Write-Host "" |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host "" |
|
} |
|
|
|
#endregion |
|
|
|
#region Main Script |
|
|
|
# Initialize logging |
|
Initialize-Logging |
|
|
|
# Set WhatIf and Verbose modes from bound parameters |
|
$script:IsWhatIfMode = $PSBoundParameters.ContainsKey('WhatIf') -and $PSBoundParameters['WhatIf'] |
|
$script:IsVerboseMode = $PSBoundParameters.ContainsKey('Verbose') -and $PSBoundParameters['Verbose'] |
|
|
|
# Display banner |
|
if (-not $IsSilentMode) { |
|
Write-Host "" |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host " $script:ScriptName v$script:ScriptVersion" -ForegroundColor White |
|
if ($IsWhatIfMode) { |
|
Write-Host " WhatIf Mode: No changes will be made" -ForegroundColor Yellow |
|
} |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host "" |
|
} |
|
|
|
Write-Log "$script:ScriptName v$script:ScriptVersion started" -Level INFO |
|
|
|
if ($IsWhatIfMode) { |
|
Write-Log "Running in WhatIf mode - no changes will be made" -Level WARNING |
|
} |
|
|
|
# Check for admin rights (skip for WhatIf mode) |
|
if (-not $IsWhatIfMode) { |
|
if (-not (Test-AdminRights)) { |
|
Invoke-Elevate |
|
} else { |
|
Write-Log "Running with administrator privileges" -Level SUCCESS |
|
} |
|
} else { |
|
if (Test-AdminRights) { |
|
Write-Log "Running with administrator privileges" -Level VERBOSE |
|
} else { |
|
Write-Log "Running without administrator privileges (WhatIf mode)" -Level VERBOSE |
|
} |
|
} |
|
|
|
# Detect Chrome installation |
|
$chromeInfo = Find-ChromeInstallation |
|
$script:ChromeDetected = $chromeInfo.Installed |
|
$script:ChromeVersion = $chromeInfo.Version |
|
$script:ChromePath = $chromeInfo.InstallPath |
|
|
|
if (-not $script:ChromeDetected) { |
|
Write-Log "Google Chrome is not installed on this system" -Level INFO |
|
Write-Log "Nothing to uninstall" -Level SUCCESS |
|
Write-Log "Script completed successfully" -Level SUCCESS |
|
exit 0 |
|
} |
|
|
|
# Scan for all Chrome components |
|
Write-Log "Chrome installation detected" -Level SUCCESS |
|
$componentsExist = Get-ChromeComponents |
|
|
|
if (-not $componentsExist) { |
|
Write-Log "No Chrome components found to remove" -Level WARNING |
|
exit 0 |
|
} |
|
|
|
Write-Log "Detection complete" -Level SUCCESS |
|
|
|
# If WhatIf mode, show report and exit |
|
if ($IsWhatIfMode) { |
|
Show-WhatIfReport |
|
Write-Log "WhatIf scan complete - no changes were made" -Level SUCCESS |
|
|
|
if ($script:LogFile) { |
|
Write-Log "Log saved to: $script:LogFile" -Level INFO |
|
} |
|
|
|
exit 0 |
|
} |
|
|
|
# Execute uninstall |
|
Write-Host "" |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host " Starting Chrome Uninstall" -ForegroundColor White |
|
Write-Host "βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" -ForegroundColor Cyan |
|
Write-Host "" |
|
|
|
# Uninstall Chrome |
|
$uninstallResult = Invoke-ChromeUninstall -ChromeInfo $chromeInfo |
|
|
|
# Perform comprehensive cleanup |
|
Invoke-ComprehensiveCleanup |
|
|
|
# Verify removal |
|
$verificationIssues = Test-ChromeRemoval |
|
|
|
# Show final report |
|
Show-FinalReport -VerificationIssues $verificationIssues |
|
|
|
# Log completion |
|
if ($script:LogFile) { |
|
Write-Log "Uninstall process complete" -Level SUCCESS |
|
Write-Log "Log saved to: $script:LogFile" -Level INFO |
|
|
|
$endTime = Get-Date |
|
Write-Log "Script ended at: $endTime" -Level INFO |
|
} |
|
|
|
# Determine exit code |
|
if ($verificationIssues.Count -eq 0) { |
|
Write-Log "Exiting with code 0 (Success)" -Level SUCCESS |
|
exit 0 |
|
} else { |
|
Write-Log "Exiting with code 1 (Completed with warnings)" -Level WARNING |
|
exit 1 |
|
} |
|
|
|
#endregion |
|
|
|
|