win: fix system app removal affecting updates #287

This commit fixes an issue where removing systems apps could disrupt
Windows Cumulative updates as reported in #287.

The fix involves removing the `EndOfLife` registry key after the app is
removed. Keeping the key at
`HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\EndOfLife`
was identified as the cause for update failures in #287.

This commit also refactors the registry key creation/removal logic to be
owned by separate functions for easier readability and reusability.
This commit is contained in:
undergroundwires
2023-11-24 14:52:52 +01:00
parent 1442f62633
commit 7c632f7388

View File

@@ -12003,50 +12003,29 @@ functions:
recurse: 'true' recurse: 'true'
- -
# -- ❗️ This script must be executed before `UninstallStoreApp` as it enables it for system app removal --- # -- ❗️ This script must be executed before `UninstallStoreApp` as it enables it for system app removal ---
function: RunPowerShell function: CreateRegistryKey
parameters: parameters:
codeComment: Enable removal of system app '{{ $packageName }}' by marking it as "EndOfLife"
# This script modifies the system registry to enable the uninstallation of a specified app. # This script modifies the system registry to enable the uninstallation of a specified app.
# Some apps (including system apps) are marked as non-removable, which prevents uninstallation and results in error 0x80070032 if an uninstall is attempted. # Some apps (including system apps) are marked as non-removable, which prevents uninstallation and results in error 0x80070032 if an uninstall is attempted.
# To bypass this, the script marks the app as 'EndOfLife' in the registry, tricking the system into allowing the uninstallation. # To bypass this, the script marks the app as 'EndOfLife' in the registry, tricking the system into allowing the uninstallation
codeComment: Enable removal of system app '{{ $packageName }}' by marking it as "EndOfLife" in the system registry keyName: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\EndOfLife\$CURRENT_USER_SID\{{ $packageName }}_{{ $publisherId }}
code: |- replaceSid: 'true'
$packageName='{{ $packageName }}'
$publisherId='{{ $publisherId }}'
$packageFamilyName = "$($packageName)_$($publisherId)"
$sid = (New-Object System.Security.Principal.NTAccount($env:USERNAME)).Translate([Security.Principal.SecurityIdentifier]).Value
$path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\EndOfLife\$($sid)\$($packageFamilyName)"
if (Test-Path $path) {
Write-Host "Skipping, no action needed, path `"$path`" already exists."
exit 0
}
try {
New-Item -Path $path -Force -ErrorAction Stop | Out-Null
Write-Host "Successfully created the registry key at path `"$path`"."
} catch {
Write-Error "Failed to create the registry key at path `"$path`": $($_.Exception.Message)"
}
revertCodeComment: Disable removal of system app '{{ $packageName }}' by removing the "EndOfLife" mark from the registry.
revertCode: |-
$packageName='{{ $packageName }}'
$publisherId='{{ $publisherId }}'
$packageFamilyName = "$($packageName)_$($publisherId)"
$sid = (New-Object System.Security.Principal.NTAccount($env:USERNAME)).Translate([Security.Principal.SecurityIdentifier]).Value
$path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\EndOfLife\$($sid)\$($packageFamilyName)"
if (-not (Test-Path $path)) {
Write-Host "Skipping, no action needed, path `"$path`" does not exist."
exit 0
}
try {
Remove-Item -Path $path -Force -ErrorAction Stop | Out-Null
Write-Host "Successfully removed the registry key at path `"$path`"."
} catch {
Write-Error "Failed to remove the registry key at path `"$path`": $($_.Exception.Message)"
}
- -
function: UninstallStoreApp function: UninstallStoreApp
parameters: parameters:
packageName: '{{ $packageName }}' packageName: '{{ $packageName }}'
publisherId: '{{ $publisherId }}' publisherId: '{{ $publisherId }}'
-
# -- ❗️ This script must be executed after `UninstallStoreApp` as it disables it for system app removal ---
function: DeleteRegistryKey
parameters:
codeComment: Revert '{{ $packageName }}' to its default, non-removable state.
# This script reverses the previous modification made to the Windows registry to enable its uninstallation.
# By removing the 'EndOfLife' status from the registry entry, the app is restored to its default, non-removable state.
# Restoring (removing) this key is important for maintaining the stability of Windows Updates (for details: https://github.com/undergroundwires/privacy.sexy/issues/287).
keyName: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\EndOfLife\$CURRENT_USER_SID\{{ $packageName }}_{{ $publisherId }}
replaceSid: 'true'
- -
# Clear: User-specific data # Clear: User-specific data
# - Folder : %LOCALAPPDATA%\Packages\{PackageFamilyName} # - Folder : %LOCALAPPDATA%\Packages\{PackageFamilyName}
@@ -12404,11 +12383,15 @@ functions:
name: RunPowerShellWithSameCodeAndRevertCode name: RunPowerShellWithSameCodeAndRevertCode
parameters: parameters:
- name: code - name: code
- name: codeComment
optional: true
call: call:
function: RunPowerShell function: RunPowerShell
parameters: parameters:
code: '{{ $code }}' code: '{{ $code }}'
revertCode: '{{ $code }}' revertCode: '{{ $code }}'
codeComment: '{{ with $codeComment }}{{ . }}{{ end }}'
revertCodeComment: '{{ with $codeComment }}{{ . }}{{ end }}'
- -
name: RunInlineCodeAsTrustedInstaller name: RunInlineCodeAsTrustedInstaller
parameters: parameters:
@@ -13648,3 +13631,75 @@ functions:
Write-Output 'Failed to restore some tasks. Check error messages above.' Write-Output 'Failed to restore some tasks. Check error messages above.'
exit 1 exit 1
} }
-
name: CreateRegistryKey
parameters:
- name: keyName # Full path of the subkey or entry to be added.
- name: replaceSid # Replaces "$CURRENT_USER_SID" string in registry key with user SID.
optional: true
- name: codeComment
optional: true
- name: revertCodeComment
optional: true
call:
# Marked: refactor-with-variables
# Replacing SID is same as `DeleteRegistryKey`
function: RunPowerShell
parameters:
code: |-
$keyName='{{ $keyName }}'
$replaceSid={{ with $replaceSid }} $true # {{ end }} $false
$registryHive = $keyName.Split('\')[0]
$registryPath = "$($registryHive):$($keyName.Substring($registryHive.Length))"
{{ with $replaceSid }}
$userSid = (New-Object System.Security.Principal.NTAccount($env:USERNAME)).Translate([Security.Principal.SecurityIdentifier]).Value
$registryPath = $registryPath.Replace('$CURRENT_USER_SID', $userSid)
{{ end }}
if (Test-Path $registryPath) {
Write-Host "Skipping, no action needed, registry path `"$registryPath`" already exists."
exit 0
}
try {
New-Item -Path $registryPath -Force -ErrorAction Stop | Out-Null
Write-Host "Successfully created the registry key at path `"$registryPath`"."
} catch {
Write-Error "Failed to create the registry key at path `"$registryPath`": $($_.Exception.Message)"
}
codeComment: '{{ with $codeComment }}{{ . }}{{ end }}'
revertCodeComment: '{{ with $revertCodeComment }}{{ . }}{{ end }}'
-
name: DeleteRegistryKey
parameters:
- name: keyName # Full path of the subkey or entry to be added.
- name: replaceSid # Replaces "$CURRENT_USER_SID" string in registry key with user SID.
optional: true
- name: codeComment
optional: true
- name: revertCodeComment
optional: true
call:
# Marked: refactor-with-variables
# Replacing SID is same as `CreateRegistryKey`
function: RunPowerShell
parameters:
code: |-
$keyName='{{ $keyName }}'
$replaceSid={{ with $replaceSid }} $true # {{ end }} $false
$registryHive = $keyName.Split('\')[0]
$registryPath = "$($registryHive):$($keyName.Substring($registryHive.Length))"
{{ with $replaceSid }}
$userSid = (New-Object System.Security.Principal.NTAccount($env:USERNAME)).Translate([Security.Principal.SecurityIdentifier]).Value
$registryPath = $registryPath.Replace('$CURRENT_USER_SID', $userSid)
{{ end }}
if (-not (Test-Path $registryPath)) {
Write-Host "Skipping, no action needed, registry path `"$registryPath`" does not exist."
exit 0
}
try {
Remove-Item -Path $registryPath -Force -ErrorAction Stop | Out-Null
Write-Host "Successfully removed the registry key at path `"$registryPath`"."
} catch {
Write-Error "Failed to remove the registry key at path `"$registryPath`": $($_.Exception.Message)"
}
codeComment: '{{ with $codeComment }}{{ . }}{{ end }}'
revertCodeComment: '{{ with $revertCodeComment }}{{ . }}{{ end }}'