Files
asg/scripts/Remove-DotNetCore31.ps1
T
mattspeer 5ef82842f2 refactor(scripts): optimize system remediation and M365 export scripts
- 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
2026-10-04 10:58:05 -05:00

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
}