|
<#
|
|
.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
|
|
|
|
|