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
This commit is contained in:
1 parent
4fd95b76d6
commit
5ef82842f2
19 files changed
+1651
-1268
No files matched your search
@@ -0,0 +1,120 @@
|
||||
#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
|
||||
}
|
||||
Reference in new issue
Block a user