# ScoutIT Remote Support (RustDesk) - Uninstall Script # Removes ALL installed versions: ScoutIT-RemoteSupport (hyphen), ScoutIT Remote Support # (spaces), plain RustDesk — side-by-side installs included. $ErrorActionPreference = 'SilentlyContinue' # ── Config ──────────────────────────────────────────────────────────────────── # Pattern is case-insensitive via -match. [-\s]? tolerates hyphen OR space OR # nothing between "ScoutIT", "Remote", and "Support" — covers every branding # we've shipped. $namePattern = 'ScoutIT[-\s]?Remote[-\s]?Support|ScoutIT|RustDesk' $servicePattern = 'ScoutIT[-\s]?Remote[-\s]?Support|RustDesk' $processPattern = 'ScoutIT[-\s]?Remote[-\s]?Support|scoutit|rustdesk' $fallbackInstallDirs = @( 'C:\Program Files\ScoutIT-RemoteSupport', 'C:\Program Files (x86)\ScoutIT-RemoteSupport', 'C:\Program Files\ScoutIT Remote Support', 'C:\Program Files (x86)\ScoutIT Remote Support', 'C:\Program Files\RustDesk', 'C:\Program Files (x86)\RustDesk' ) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Write-Host "Starting ScoutIT Remote Support / RustDesk uninstall (all versions)..." # ── Stop every matching service ────────────────────────────────────────────── $services = Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $servicePattern -or $_.DisplayName -match $servicePattern } if ($services) { foreach ($s in $services) { Write-Host "Stopping service: $($s.Name) ($($s.DisplayName))..." Stop-Service -Name $s.Name -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 3 } else { Write-Host "No matching services found." } # ── Enumerate every matching registry uninstall entry ──────────────────────── Write-Host "Scanning registry for all matching uninstall entries..." $uninstallKeys = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' ) $entries = foreach ($key in $uninstallKeys) { Get-ItemProperty $key -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -match $namePattern -or $_.Publisher -match $namePattern } } if ($entries) { Write-Host "Found $($entries.Count) matching entry/entries:" $entries | ForEach-Object { Write-Host " - $($_.DisplayName) [$($_.PSChildName)]" } } else { Write-Host "No matching uninstall entries in registry." } # Collect InstallLocation values so we can both run exe --uninstall-service # and clean the folders after MSI removal. $installLocations = @() foreach ($e in $entries) { if ($e.InstallLocation -and (Test-Path $e.InstallLocation)) { $installLocations += $e.InstallLocation.TrimEnd('\') } } $installLocations += $fallbackInstallDirs | Where-Object { Test-Path $_ } $installLocations = $installLocations | Select-Object -Unique # ── Graceful service unregister via every exe we can find ──────────────────── foreach ($dir in $installLocations) { Get-ChildItem $dir -Filter '*.exe' -ErrorAction SilentlyContinue | ForEach-Object { Write-Host "Unregistering service via: $($_.FullName)" Start-Process -FilePath $_.FullName -ArgumentList '--uninstall-service' -Wait -ErrorAction SilentlyContinue } } if ($installLocations) { Start-Sleep -Seconds 5 } # ── Uninstall every matching MSI / entry ───────────────────────────────────── foreach ($e in $entries) { $name = $e.DisplayName $code = $e.PSChildName if ($code -match '^\{') { Write-Host "Running MSI uninstall for $name [$code]..." Start-Process -FilePath "msiexec.exe" ` -ArgumentList "/x `"$code`" /qn /norestart" ` -Wait Start-Sleep -Seconds 10 } elseif ($e.UninstallString) { Write-Host "Running UninstallString for $name..." $cmd = $e.UninstallString -replace '"', '' $exe = ($cmd -split ' ')[0] $rest = ($cmd -split ' ', 2)[1] $args = "$rest /S /qn /norestart".Trim() Start-Process -FilePath $exe -ArgumentList $args -Wait -ErrorAction SilentlyContinue Start-Sleep -Seconds 10 } else { Write-Warning "No ProductCode or UninstallString for $name - skipping MSI step." } } # ── Kill any remaining processes ───────────────────────────────────────────── Write-Host "Killing any remaining processes..." Get-Process | Where-Object { $_.Path -match $processPattern } | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 # ── Remove install directories (registry-reported + fallbacks) ─────────────── foreach ($dir in $installLocations) { if (Test-Path $dir) { Write-Host "Removing install directory: $dir" Remove-Item -Path $dir -Recurse -Force -ErrorAction SilentlyContinue if (Test-Path $dir) { Write-Warning "Directory still exists: $dir (locked files - reboot may be required)" } } } # ── Remove leftover AppData / config ───────────────────────────────────────── $appDataPaths = @( "$env:APPDATA\ScoutIT-RemoteSupport", "$env:APPDATA\ScoutIT Remote Support", "$env:APPDATA\RustDesk", "$env:ProgramData\ScoutIT-RemoteSupport", "$env:ProgramData\ScoutIT Remote Support", "$env:ProgramData\RustDesk" ) foreach ($path in $appDataPaths) { if (Test-Path $path) { Write-Host "Removing: $path" Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue } } # ── Confirm ─────────────────────────────────────────────────────────────────── $svcRemaining = Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -match $servicePattern -or $_.DisplayName -match $servicePattern } $regRemaining = foreach ($key in $uninstallKeys) { Get-ItemProperty $key -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -match $namePattern -or $_.Publisher -match $namePattern } } $dirRemaining = $installLocations | Where-Object { Test-Path $_ } Write-Output "................................................." if (-not $svcRemaining -and -not $regRemaining -and -not $dirRemaining) { Write-Output "ScoutIT Remote Support / RustDesk fully uninstalled (all versions)." } else { if ($svcRemaining) { Write-Warning "Services still present: $(($svcRemaining | Select-Object -Expand Name) -join ', ')" } if ($regRemaining) { Write-Warning "Registry entries still present: $(($regRemaining | Select-Object -Expand DisplayName) -join ', ')" } if ($dirRemaining) { Write-Warning "Directories still present: $($dirRemaining -join ', ') - reboot may be required." } } Write-Output "................................................." exit 0