Must Run Script 2x to Take Effect?

This script is credit to @tonyd. Why do I have to run the script TWICE for the RegistryAccessRule to take effect ?

$regPath = 'HKLM:\SOFTWARE\MyKey'
$objReg = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')

$allSubKeys = Get-ChildItem -Path $regPath -Recurse
$allSubKeys += Get-Item -Path $regPath

$allSubKeys | foreach-Object {
	if($_.PSIsContainer) {
		$var = $($($_.Name.Replace('HKEY_LOCAL_MACHINE\', '')).Replace('\', '\\'))
		Write-Output "Changing key: $var"
		$subKeyFound = $objReg.OpenSubKey($var, $true)
		$regACl = $subKeyFound.GetAccessControl()
        $regACL.SetOwner([System.Security.Principal.NTAccount]"Administrators")
		$rule = New-Object System.Security.AccessControl.RegistryAccessRule ("Administrators","ReadKey","ContainerInherit,ObjectInherit","None","Allow")
        $regACL.SetAccessRule($rule)
        $regACL.SetAccessRuleProtection($true,$true)
        $subKeyFound.SetAccessControl($regACL)
        }
}

Ironically your code doesn’t work me at all. Like it errors before it even gets to the owner change part.

$allSubKeys += Get-Item -Path $regPath

errors out stating

InvalidOperation: Method invocation failed because [Microsoft.Win32.RegistryKey] does not contain a method named ‘op_Addition’.

I’m not sure why your code is working and not running into this error. I’ve replicated the error on a couple systems now. I might suggest just doing this one liner instead:

$allSubKeys = (Get-ChildItem -Path $regPath -Recurse) + (Get-Item -Path $regPath)

IMO more clean.

Next, regardless of the first error, I can’ t replicate what you are reporting, that it needs to be ran twice. It does not take two runs for me. Other than the one code change I made to get you code to properly run, that code works with a single pass. I created the same key in my own registry with some fake sub keys (named subkey1, subkey2, etc.). Then I switched owners to one of my accounts, and ensured all subkeys inherite that permission by checking ‘replace owner on subcontainers and objects’ box. I then switched it back using the code you provided and checked again, and each key reports the local administrators group, as expected.

  1. Are you not sharing some of your code?
  2. Are you sure you’re viewing the updated owner after it runs?

Im using PowerShell ISE (RunAsAdmin) to run scripts…Im not sure if that makes a difference. Im using this to test

$regPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\XboxGipSvc'

The ReadKey permission takes effect only after script is ran a 2nd time. Take ownership and Disable-Inheritance works on 1st run. I would like this script to change owner to Administrator, Disable-Inheritance, and change FullControl to Read for main key and all its subkeys. I appreciate you helping me with this.

Gotcha, Thanks for clarifying.

Yeah I don’t use the ISE, i was just using powershell proper, but definitely recommend taking a look at VS code.

I think you’re running into a race condition. Also not sure why you aren’t using Get-ACL and Set-ACL here. I just decided to rewrite this to make it cleaner. However, if you want to use your other way you can, i think my fix would still work.

Based on what you stated, I theorized there was a race condition happening where perhaps you needed to update the owner before it would let you set that key, I did replicate after you further explained. Typically what I do just split it out into steps, and added a small sleep in between. You can probably even reduce that sleep to like 200 MS, i just did 1 second to start. Yeah it’s a little redundant, but it gets the job done. This is also much easier to read/follow IMO:

$regPath = 'HKLM:\SOFTWARE\MyKey'

$allSubKeys = (Get-ChildItem -Path $regPath -Recurse) + (Get-Item -Path $regPath)


$allSubKeys | foreach-Object {
	if($_.PSIsContainer) {
        Write-Output "Changing key: $($_.PSPath)"
        $regACL = Get-ACL -path $_.PSPath
        $regACL.SetOwner([System.Security.Principal.NTAccount]"Administrators")
        $regACL.SetAccessRuleProtection($true,$true)
        Set-ACL -Path $_.pspath -AclObject $regacl
        # Splitting in two steps because of potential race condition
        Start-Sleep -Seconds 1
        $regACl = Get-ACL -path $_.PSPath
        $rule = New-Object System.Security.AccessControl.RegistryAccessRule ("Administrators","ReadKey","ContainerInherit,ObjectInherit","None","Allow")
        $regACL.SetAccessRule($rule)
        Set-ACL -Path $_.pspath -AclObject $regacl
        }
}

I’ve confirmed this does everything you ask in a single run.

Thankyou very much for your script. That 1 second delay was the kicker ! But the test path that I used, one of the subkeys HAS a subkey. The Permissions is changed to READ, but the owner is still ‘SYSTEM’ and inheritance was not disabled. I was wondering for future, is it possible the script recurse to subkeys OF subkeys ? Thankyou