Removing Registry Permissions

I am working with a customer that has encountered some issues with a server template they are using in their VMware environment. Through some troubleshooting, I found that the template, when attempting to add server roles, is looking for a package that is not there. This template has been distributed to a number of locations worldwide, so pulling it back and redoing it is not the first option. I found several registry values that can be deleted which fixes the issue, but they are under keys in a protected hive (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Services\PackageDetect).

It took some doing on a key to which no one but Trusted Installer has access, but I was able to assign permissions temporarily to a local local account, at the PackageDetect level, with propagation, so I could then delete the needed items. This works well:

$sKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackageDetect", [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::ChangePermissions)
$inheritFlag = @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit,[System.Security.AccessControl.InheritanceFlags]::ObjectInherit)
$propagationFlag = [System.Security.AccessControl.PropagationFlags]::InheritOnly
$acl = $sKey.GetAccessControl()
$rule = New-Object System.Security.AccessControl.RegistryAccessRule ("TempAdmin", "FullControl", $inheritFlag, $propagationFlag, "Allow")
$acl.SetAccessRule($rule)
$sKey.SetAccessControl($acl)

The problem comes in trying to delete the acl. If I manually delete it at the PackageDetect key it removes from all children as expected. If I manually delete the user account, it also deletes the permission as I would expect. But neither option works for me through a script. If I try this:

$acl = Get-Acl 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackageDetect"
$inheritFlag = @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit,[System.Security.AccessControl.InheritanceFlags]::ObjectInherit)
$propagationFlag = [System.Security.AccessControl.PropagationFlags]::InheritOnly
$rule = New-Object System.Security.AccessControl.RegistryAccessRule ("TempAdmin", "FullControl", $inheritFlag, $propagationFlag, "Allow")
$acl.RemoveAccess($rule)

This returns True, but the permission is not deleted. I am not sure what I am missing.

I also tried deleting the user account altogether (it is just a temp account used for this process) with a Remove-LocalUser, but this does not produce the desired result either. The user account is deleted, but the permission changes to the user’s GUID and is never cleaned up from the acl. When I manually go into Control Panel and delete the user, however, it immediately removes this permission. I am unsure if there is another way of deleting the user via script that would give me this same functionality.

I would appreciate any suggestions, whether through removing the acl (preferred) or deleting the user account so that all permissions are removed.

So… will try and help here, but understand that you’re unfortunately not really “doing PowerShell” as much as “doing .NET Framework,” so we’ll run to the end of my knowledge pretty quickly. StackOverflow might be a better venue since you get more .NET devs there.

Linguistically, you’re not removing an ACL, you’re removing an ACE. You can’t delete the ACL itself, only its entries (rules).

First, ensure that Get-ACL is returning something of type System.Security.AccessControl.RegistrySecurity.

Next, retrieve the ACEs (ACL rules) and ensure you’re specifying exactly the same thing to remove. The matching on those rules is notoriously picky - just because you set Rule A doesn’t necessarily mean the registry stored the exact same thing - but to remove it, you need to specify precisely what the registry stored. I’ve had experiences where the True return value was spurious.

Control Panel does a bit more than just delete the user; it has some under-the-hood magic that’s also cleaning up ACLs. That’s why you’re not seeing the same thing when just deleting the user via code. Code is always harder ;).

Finally, you might want to consider a command-line tool not based on .NET. Something speaking more pure C++ might have better results than the 92 layers of translation that .NET winds up involving.