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:
mattspeer committed 2026-10-04 10:58:05 -05:00
1 parent 4fd95b76d6
commit 5ef82842f2
19 files changed
+1651 -1268

No files matched your search

+135
View File
@@ -0,0 +1,135 @@
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Removes Classic Microsoft Teams and Teams Machine-Wide Installer.
.DESCRIPTION
Stops Classic Teams processes, uninstalls the Machine-Wide Installer via MSI,
removes per-user Classic Teams installations, cleans residual files/registry run keys,
and verifies that New Teams (MSTeams) remains intact.
#>
$ErrorActionPreference = "Continue"
Write-Host "Starting Classic Microsoft Teams cleanup..." -ForegroundColor Cyan
# 1. Stop ONLY Classic Teams processes (preserve New Teams)
Get-Process -Name "Teams" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
# Stop Update.exe processes originating specifically from Classic Teams directories
Get-Process -Name "Update" -ErrorAction SilentlyContinue | Where-Object {
$_.Path -like "*\Microsoft\Teams\Update.exe"
} | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
# 2. Remove Teams Machine-Wide Installer from HKLM registry
$uninstallPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
$machineWideInstallers = foreach ($path in$uninstallPaths) {
Get-ItemProperty $path -ErrorAction SilentlyContinue \vert{} Where-Object {$_.DisplayName -eq "Teams Machine-Wide Installer"
}
}
foreach ($app in$machineWideInstallers) {
Write-Host "Found Teams Machine-Wide Installer: $($app.DisplayName) $($app.DisplayVersion)" -ForegroundColor Yellow
if ($app.UninstallString -match "\{[A-Fa-f0-9\-]{36}\}") {
$productCode =$Matches[0]
Write-Host "Uninstalling Machine-Wide Installer via MSI product code: $productCode" -ForegroundColor Cyan
Start-Process -FilePath "msiexec.exe" -ArgumentList "/x $productCode /qn /norestart" -Wait -NoNewWindow
}
elseif ($app.UninstallString) {
Write-Host "Uninstalling via UninstallString: $($app.UninstallString)" -ForegroundColor Cyan
if ($app.UninstallString -match "msiexec") {
$args =$app.UninstallString -replace "msiexec.exe", "" -replace "/I", "/x"
Start-Process -FilePath "msiexec.exe" -ArgumentList "$args /qn /norestart" -Wait -NoNewWindow
} else {
Start-Process -FilePath "cmd.exe" -ArgumentList "/c `"$($app.UninstallString)`" /quiet /norestart" -Wait -NoNewWindow
}
}
}
# 3. Clean per-user Classic Teams installations
$usersRoot = Join-Path$env:SystemDrive "Users"
$userProfiles = Get-ChildItem -Path $usersRoot -Directory -ErrorAction SilentlyContinue \vert{} Where-Object {$_.Name -notin @("Public", "Default", "Default User", "All Users", "defaultuser0")
}
foreach ($profile in$userProfiles) {
$localTeamsFolder = Join-Path$profile.FullName "AppData\Local\Microsoft\Teams"
$roamingTeamsFolder = Join-Path$profile.FullName "AppData\Roaming\Microsoft\Teams"
$updateExe = Join-Path$localTeamsFolder "Update.exe"
if (Test-Path $updateExe) {
Write-Host "Uninstalling Classic Teams for profile: $($profile.Name)" -ForegroundColor Cyan
Start-Process -FilePath $updateExe -ArgumentList "--uninstall -s" -Wait -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
}
# Remove residual AppData folders (both Local and Roaming)
foreach ($folder in @($localTeamsFolder,$roamingTeamsFolder)) {
if (Test-Path $folder) {
Write-Host "Removing directory: $folder" -ForegroundColor Yellow
Remove-Item -Path $folder -Recurse -Force -ErrorAction SilentlyContinue
}
}
}
# 4. Remove System-Wide ProgramData Leftovers
$leftoverPaths = @(
"$env:ProgramData\Microsoft\Teams",
"$env:ProgramData\Microsoft\TeamsMachineInstaller",
"$env:ProgramData\Microsoft\TeamsCustomData"
)
foreach ($path in$leftoverPaths) {
if (Test-Path $path) {
Write-Host "Removing leftover ProgramData path: $path" -ForegroundColor Yellow
Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue
}
}
# 5. Clean Startup Registry Run Keys across loaded user hives
$hkuPath = "Registry::HKEY_USERS"
Get-ChildItem -Path $hkuPath -ErrorAction SilentlyContinue | ForEach-Object {
$runKey = Join-Path$_.PSPath "Software\Microsoft\Windows\CurrentVersion\Run"
if (Test-Path $runKey) {
$val = Get-ItemProperty -Path$runKey -Name "com.squirrel.teams.teams" -ErrorAction SilentlyContinue
if ($val) {
Write-Host "Removing Classic Teams startup reg key from hive: $($_.PSChildName)" -ForegroundColor Yellow
Remove-ItemProperty -Path $runKey -Name "com.squirrel.teams.teams" -ErrorAction SilentlyContinue
}
}
}
# 6. Confirm New Teams (MSTeams) status
$newTeams = Get-AppxPackage -Name "MSTeams" -AllUsers -ErrorAction SilentlyContinue
if ($newTeams) {
Write-Host "New Teams (MSTeams) is installed:" -ForegroundColor Green
$newTeams | Select-Object Name, Version, PackageFullName | Format-Table -AutoSize | Out-String | Write-Host
} else {
Write-Host "WARNING: New Teams (MSTeams) was not detected on this device." -ForegroundColor Red
}
# 7. Final Verification
$remainingMachineWide = foreach ($path in$uninstallPaths) {
Get-ItemProperty $path -ErrorAction SilentlyContinue \vert{} Where-Object {$_.DisplayName -eq "Teams Machine-Wide Installer"
}
}
$remainingClassicFolders = foreach ($profile in$userProfiles) {
$localTeamsFolder = Join-Path$profile.FullName "AppData\Local\Microsoft\Teams"
if (Test-Path $localTeamsFolder) {$localTeamsFolder }
}
if ($remainingMachineWide -or$remainingClassicFolders) {
Write-Host "Cleanup completed, but some Classic Teams remnants remain." -ForegroundColor Red
exit 1
} else {
Write-Host "Classic Teams and Teams Machine-Wide Installer cleanup completed successfully." -ForegroundColor Green
exit 0
}