Skip to main content

This has been an interesting one I thought I’d share. I’m honestly still very hands on with this one and only run it manually. Meaning I fully expect this to be a journey, not a perfect solution.

Also please note, I’m using the Get-Win32 and Remove-Win32App cmdlets available by using Automox’s amazing WDK https://developer.automox.com/developer-portal/wdk/overview/ (saves me from writing so much code)

 

Another call-out. These worklets will install dotnet-uninstall-tool version 1.6.0 explicitly (update code to use newer versions). Learn more about that at Emanuele Bartolesi blog here https://dev.to/kasuken/removing-old-version-of-net-4052 or just go checkout the latest releases of the Microsoft built tool here https://github.com/dotnet/cli-lab/releases

 

My workflow consists of using two worklets as such:

  1. Update variable versions in BOTH worklets manually based on my own internal needs of what versions need removed (typically get this list from a vulnerability scanner’s output)
  2. Run the NET Core SDK What-If that accomplishes the following:
    • Uses Get-Win32App to find ‘Microsoft .NET Core SDK’​​​​​​​
    • Reports EOL Win32 Apps based on versions variable
    • Looks for dotnet-core-uninstall tool, if not installs version 1.6.0
    • Runs dotnet-core-uninstall list
    • Runs dotnet-core-uninstall whatif -all-but-latest --core (cycles through aspnet-runtime, hosting-bundle, runtime, sdk with output. Marvel at this because figuring out how to pass to the command and still get output was not easy!)
    • Cycle through four directories for EOL versions (even after removal, these directories seem to get left behind. hence the focus)
    • Run dotnet --info
  3. Analyze the output as that helps me understand two very important points:
    • NET Core SDK dependency on Visual Studio (fix that manually before running actual cleanup worklet)
    • What the Cleanup worklet will do
  4. Run the NET Core SDK Removal that accomplishes the following:
    • ​​​​​​​Looks for dotnet-core-uninstall tool, if not installs version 1.6.0
    • Removes EOL Win32 Apps based on versions variable (not always smooth)
    • Runs dotnet-core-uninstall list
    • Runs dotnet-core-uninstall remove -all-but-latest --core (cycles through aspnet-runtime, hosting-bundle, runtime, sdk with output)
    • Cycle through four directories for EOL versions, first archiving each one to a zip file, then if the zip exists, removing the directory.

 

Remediation Code “NET Core SDK What-If”

# Approved Removal
$versions = '1.0.4','2.0.7','2.1.12','2.1.14.28209','2.2.8','3.1.32','3.1.32.31915','5.0.17','5.0.17.31213','5.0.17.31219','5.0.4','5.0.9'

# Shows Win32 SDK Core Apps that are EOL per $versions variable
$apps = Try { Get-Win32App | Where-Object { $_.Name -match 'Microsoft .NET Core SDK' -and $_.Name -notmatch 'Microsoft .NET Core SDK Uninstall Tool'} }catch{exit 0}
foreach ($app in $apps){
$versions | % {
IF($app.name -match $_){$eol = "End of Life -- "}
}
Write-Output "$eol$($app.name) installed $($app.InstallDate)"
IF($eol){remove-variable eol}
}
IF(!$apps){Write-Output "Did not detect any Win32 Apps with Microsoft .NET Core SDK as installed."; Write-Output ""}

# Validate Microsoft .NET Core SDK Uninstall Tool as installed
$app = Try { Get-Win32App | Where-Object { $_.Name -match 'Microsoft .NET Core SDK Uninstall Tool' } }catch{exit 0}
IF(!$app){
Write-Output "Attempting to install Microsoft .NET Core SDK Uninstall Tool (x86)"
$uri = 'https://github.com/dotnet/cli-lab/releases/download/1.6.0/dotnet-core-uninstall-1.6.0.msi'
$msi = 'C:\windows\temp\dotnet-core-uninstall-1.6.0.msi'
IF((Test-Path $msi) -eq $false){
Write-Output "Downloading $uri"
Invoke-WebRequest -uri $uri -out $msi
}else{
Write-Output "Found $msi. Starting Install."
}

# Install
Start-Process MsiExec -ArgumentList "/I $msi /qn" -wait

# Validate Install
$app = Try { Get-Win32App | Where-Object { $_.Name -match 'Microsoft .NET Core SDK Uninstall Tool' } }catch{exit 0}
IF(!$app){
Write-Output "Failed to Install Microsoft .NET Core SDK Uninstall Tool (x86). Giving up."
exit 0
}else{
Write-Output "Installed Microsoft .NET Core SDK Uninstall Tool (x86)"
}
}

# List the dotnet-core-uninstall list to see what versions can be removed
Write-Output "dotnet-core-uninstall list"
$exe = "C:\Program Files (x86)\dotnet-core-uninstall\dotnet-core-uninstall.exe"
cmd /c $exe list

Write-Output ""
Write-Output "Running what-if mode on dotnet-core-uninstall.exe cleanup tool:"
Function Start-DotnetCoreUninstall ($core){
$exe = "C:\Program Files (x86)\dotnet-core-uninstall\dotnet-core-uninstall.exe"
$obj = New-Object System.Diagnostics.ProcessStartInfo
$obj.FileName = "cmd.exe"
$obj.RedirectStandardError = $true
$obj.RedirectStandardOutput = $true
$obj.UseShellExecute = $false
$obj.Arguments = "/c echo y | `"$exe`" whatif --all-but-latest --$core"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $obj
$p.Start() | Out-Null
$p.WaitForExit()
$p.StandardOutput.ReadToEnd()
$p.StandardError.ReadToEnd()
}
Write-Output "Cleanup aspnet-runtime:"
Start-DotnetCoreUninstall aspnet-runtime
Write-Output "Cleanup hosting-bundle:"
Start-DotnetCoreUninstall hosting-bundle
Write-Output "Cleanup runtime:"
Start-DotnetCoreUninstall runtime
Write-Output "Cleanup sdk:"
Start-DotnetCoreUninstall sdk


Write-Output ""
Write-Output "Manual Directory Cleanup"

$dirs = 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\',
'C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\',
'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\',
'C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App\'
$paths = Get-ChildItem $dirs -Directory -ErrorAction SilentlyContinue
foreach ($path in $paths){
$versions | % {
IF($path.FullName -match $_){
$i++
}
}
IF($i -gt 0){
Write-Output "EOL Path: $($path.FullName)"
}else{
Write-Output "Path: $($path.FullName)"
}
IF($i){Remove-Variable i}
}

Write-Output ""
Write-Output "dotnet --info"
dotnet --info
Write-Output ""

Remediation Code “NET Core SDK Removal”

# Approved Removal
$versions = '1.0.4','2.0.7','2.1.12','2.1.14.28209','2.2.8','3.1.32','3.1.32.31915','3.1.426','5.0.17','5.0.17.31213','5.0.17.31219','5.0.4','5.0.9'

# Validate Microsoft .NET Core SDK Uninstall Tool as installed
$app = Try { Get-Win32App | Where-Object { $_.Name -match 'Microsoft .NET Core SDK Uninstall Tool' } }catch{exit 0}
IF(!$app){
Write-Output "Attempting to install Microsoft .NET Core SDK Uninstall Tool (x86)"
$uri = 'https://github.com/dotnet/cli-lab/releases/download/1.6.0/dotnet-core-uninstall-1.6.0.msi'
$msi = 'C:\windows\temp\dotnet-core-uninstall-1.6.0.msi'
IF((Test-Path $msi) -eq $false){
Write-Output "Downloading $uri"
Invoke-WebRequest -uri $uri -out $msi
}else{
Write-Output "Found $msi. Starting Install."
}

# Install
Start-Process MsiExec -ArgumentList "/I $msi /qn" -wait

# Validate Install
$app = Try { Get-Win32App | Where-Object { $_.Name -match 'Microsoft .NET Core SDK Uninstall Tool' } }catch{exit 0}
IF(!$app){
Write-Output "Failed to Install Microsoft .NET Core SDK Uninstall Tool (x86). Giving up."
exit 0
}else{
Write-Output "Installed Microsoft .NET Core SDK Uninstall Tool (x86)"
}
}

# Remove Win32 SDK Core Apps
$apps = Try { Get-Win32App | Where-Object { $_.Name -match 'Microsoft .NET Core SDK' -and $_.Name -notmatch 'Microsoft .NET Core SDK Uninstall Tool'} }catch{exit 0}
foreach ($app in $apps){
$versions | % {
IF($app.name -match $_){$i++}
}
IF($i -gt 0){
Write-Output "Removing $($app.name) installed $($app.InstallDate)"
Get-Win32App | Where-Object { $_.Name -eq "$($app.name)" } | Remove-Win32App -AdditionalArgs '/qn'
}
IF($i){remove-variable i}
}

# Remove all but latest ASP.NET versions
Write-Output "dotnet-core-uninstall list"
$exe = "C:\Program Files (x86)\dotnet-core-uninstall\dotnet-core-uninstall.exe"
cmd /c $exe list
Write-Output ""

Function Start-DotnetCoreUninstall ($core){
$exe = "C:\Program Files (x86)\dotnet-core-uninstall\dotnet-core-uninstall.exe"
$obj = New-Object System.Diagnostics.ProcessStartInfo
$obj.FileName = "cmd.exe"
$obj.RedirectStandardError = $true
$obj.RedirectStandardOutput = $true
$obj.UseShellExecute = $false
$obj.Arguments = "/c echo y | `"$exe`" remove --all-but-latest --$core --force"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $obj
$p.Start() | Out-Null
$p.WaitForExit()
$p.StandardOutput.ReadToEnd()
$p.StandardError.ReadToEnd()
}
Write-Output "Cleanup aspnet-runtime:"
Start-DotnetCoreUninstall aspnet-runtime
Write-Output "Cleanup hosting-bundle:"
Start-DotnetCoreUninstall hosting-bundle
Write-Output "Cleanup runtime:"
Start-DotnetCoreUninstall runtime
Write-Output "Cleanup sdk:"
Start-DotnetCoreUninstall sdk

write-output ""
write-output "List dotnet-core-uninstall"
cmd /c $exe list

Write-Output ""
Write-Output "Manual Directory Cleanup:"

$dirs = 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\',
'C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\',
'C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\',
'C:\Program Files (x86)\dotnet\shared\Microsoft.AspNetCore.App\'
$paths = Get-ChildItem $dirs -Directory -ErrorAction SilentlyContinue
foreach ($path in $paths){
$versions | % {
IF($path.FullName -match $_){
$i++
}
}
IF($i -gt 0){
Write-Output "Cleanup $($path.FullName)"
# Backup
Compress-Archive -Path $path.FullName -DestinationPath "$($path.FullName).zip"

# Remove
IF(Test-Path "$($path.FullName).zip") {
Remove-Item $path.FullName -Recurse -force
}else{
Write-Output "Cleanup Stopped. Failed to archive $($path.FullName)"
}
}else{
Write-Output "No action on path: $($path.FullName)"
}
IF($i){Remove-Variable i}
}

 

Be the first to reply!

Reply