- teams: harden Classic Teams removal, protect New Teams, and clean HKU run keys - dotnet: add bootstrapper EXE uninstall support and remove orphaned 3.1.* folders - teams: add MSIX package targeting and version parsing for vulnerable New Teams builds - m365: improve Graph SDK export speed, reuse sessions, and add null-safe handling
120 lines
4.6 KiB
PowerShell
120 lines
4.6 KiB
PowerShell
#Requires -RunAsAdministrator
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Removes unsupported Microsoft .NET Core / ASP.NET Core / Windows Desktop Runtime / SDK 3.1.x components.
|
|
.DESCRIPTION
|
|
Queries HKLM uninstall registry locations (64-bit and 32-bit) for .NET 3.1.x components,
|
|
executes quiet uninstallation for both MSI and Executable/Bundle bootstrappers,
|
|
cleans orphaned runtime directories, and verifies removal.
|
|
#>
|
|
|
|
$ErrorActionPreference = "Continue"
|
|
|
|
Write-Host "Starting .NET Core 3.1 remediation..." -ForegroundColor Cyan
|
|
|
|
$registryPaths = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
|
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
|
)
|
|
|
|
# Search pattern targeting 3.1.x runtimes, SDKs, targeting packs, and hosting bundles
|
|
$targetApps = foreach ($path in$registryPaths) {
|
|
Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object {
|
|
(
|
|
$_.DisplayName -match "Microsoft .*\b3\.1\.\d+\b" -or
|
|
$_.DisplayName -match "Microsoft \.NET (Core|SDK|Runtime|Windows Desktop|ASP\.NET Core).*3\.1"
|
|
) -and ($_.UninstallString -or$_.QuietUninstallString)
|
|
}
|
|
}
|
|
|
|
if (-not $targetApps) {
|
|
Write-Host "No .NET Core 3.1 components found. System is clean." -ForegroundColor Green
|
|
exit 0
|
|
}
|
|
|
|
Write-Host "Found $($targetApps.Count) .NET Core 3.1 component(s) to remove:" -ForegroundColor Yellow
|
|
foreach ($app in$targetApps) {
|
|
Write-Host " - $($app.DisplayName) ($($app.DisplayVersion))" -ForegroundColor Yellow
|
|
}
|
|
|
|
foreach ($app in$targetApps) {
|
|
Write-Host "`nAttempting to remove: $($app.DisplayName)..." -ForegroundColor Cyan
|
|
|
|
$uninstallCmd = if ($app.QuietUninstallString) { $app.QuietUninstallString } else { $app.UninstallString }
|
|
|
|
if ($uninstallCmd -match "\{[A-Fa-f0-9\-]{36}\}") {
|
|
# Standard MSI Uninstall
|
|
$productCode = $Matches[0]
|
|
Write-Host "Executing MSI uninstall for GUID: $productCode" -ForegroundColor Gray
|
|
$proc = Start-Process -FilePath "msiexec.exe" -ArgumentList "/x $productCode /qn /norestart" -Wait -PassThru -NoNewWindow
|
|
}
|
|
else {
|
|
# Executable / WiX Burn Bootstrapper Uninstall
|
|
Write-Host "Executing Bootstrapper EXE uninstall..." -ForegroundColor Gray
|
|
|
|
# Parse EXE path and arguments safely
|
|
if ($uninstallCmd -match '^"([^"]+)"(.*)$') {
|
|
$exePath = $Matches[1]
|
|
$exeArgs = "$($Matches[2]) /uninstall /quiet /norestart".Trim()
|
|
}
|
|
elseif ($uninstallCmd -match '^(\S+)(.*)$') {
|
|
$exePath = $Matches[1]
|
|
$exeArgs = "$($Matches[2]) /uninstall /quiet /norestart".Trim()
|
|
}
|
|
else {
|
|
$exePath = $uninstallCmd
|
|
$exeArgs = "/uninstall /quiet /norestart"
|
|
}
|
|
|
|
$proc = Start-Process -FilePath $exePath -ArgumentList $exeArgs -Wait -PassThru -NoNewWindow -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# Evaluate return codes (0 = Success, 3010 = Success, reboot required)
|
|
if ($proc.ExitCode -in @(0, 3010)) {
|
|
Write-Host "Successfully processed uninstall for $($app.DisplayName)." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "Warning: Uninstall process returned exit code $($proc.ExitCode) for $($app.DisplayName)." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
|
|
# Clean residual 3.1.x folder remnants from dotnet installation directories
|
|
$dotnetPaths = @(
|
|
"$env:ProgramFiles\dotnet",
|
|
"${env:ProgramFiles(x86)}\dotnet"
|
|
)
|
|
|
|
foreach ($basePath in $dotnetPaths) {
|
|
if (Test-Path $basePath) {
|
|
Get-ChildItem -Path $basePath -Recurse -Directory -ErrorAction SilentlyContinue | Where-Object {
|
|
$_.Name -like "3.1.*"
|
|
} | ForEach-Object {
|
|
Write-Host "Removing leftover directory: $($_.FullName)" -ForegroundColor Yellow
|
|
Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
|
|
# Final Verification
|
|
Write-Host "`nVerifying remaining .NET Core 3.1 components..." -ForegroundColor Cyan
|
|
|
|
$remainingApps = foreach ($path in$registryPaths) {
|
|
Get-ItemProperty $path -ErrorAction SilentlyContinue | Where-Object {
|
|
(
|
|
$_.DisplayName -match "Microsoft .*\b3\.1\.\d+\b" -or
|
|
$_.DisplayName -match "Microsoft \.NET (Core|SDK|Runtime|Windows Desktop|ASP\.NET Core).*3\.1"
|
|
) -and ($_.UninstallString -or$_.QuietUninstallString)
|
|
}
|
|
}
|
|
|
|
if ($remainingApps) {
|
|
Write-Host "ERROR: Some .NET Core 3.1 components remain installed:" -ForegroundColor Red
|
|
$remainingApps | ForEach-Object {
|
|
Write-Host " - $($_.DisplayName) ($($_.DisplayVersion))" -ForegroundColor Red
|
|
}
|
|
exit 1
|
|
}
|
|
else {
|
|
Write-Host ".NET Core 3.1 remediation completed successfully." -ForegroundColor Green
|
|
exit 0
|
|
} |