Chapter 16. Managing Windows Registry

by Mar 24, 2012

Thanks to PowerShells universal "Provider" concept, you can navigate the Windows Registry just as you would the file system. In this chapter, you will learn how to read and write Registry keys and Registry values.

The Registry stores many crucial Windows settings. That's why it's so cool to read and sometimes change information in the Windows Registry: you can manage a lot of configuration settings and sometimes tweak Windows in ways that are not available via the user interface.

However, if you mess things up – change the wrong values or deleting important settings – you may well permanently damage your installation. So, be very careful, and don't change anything that you do not know well.

Using Providers

To access the Windows Registry, there are no special cmdlets. Instead, PowerShell ships with a so-called provider named "Registry". A provider enables a special set of cmdlets to access data stores. You probably know these cmdlets already: they are used to manage content on drives and all have the keyword "item" in their noun part:

PS> Get-Command -Noun Item*

CommandType     Name                  ModuleName           Definition
-----------     ----                  ----------           ----------
Cmdlet          Clear-Item            Microsoft.PowerSh... ...
Cmdlet          Clear-ItemProperty    Microsoft.PowerSh... ...
Cmdlet          Copy-Item             Microsoft.PowerSh... ...
Cmdlet          Copy-ItemProperty     Microsoft.PowerSh... ...
Cmdlet          Get-Item              Microsoft.PowerSh... ...
Cmdlet          Get-ItemProperty      Microsoft.PowerSh... ...
Cmdlet          Invoke-Item           Microsoft.PowerSh... ...
Cmdlet          Move-Item             Microsoft.PowerSh... ...
Cmdlet          Move-ItemProperty     Microsoft.PowerSh... ...
Cmdlet          New-Item              Microsoft.PowerSh... ...
Cmdlet          New-ItemProperty      Microsoft.PowerSh... ...
Cmdlet          Remove-Item           Microsoft.PowerSh... ...
Cmdlet          Remove-ItemProperty   Microsoft.PowerSh... ...
Cmdlet          Rename-Item           Microsoft.PowerSh... ...
Cmdlet          Rename-ItemProperty   Microsoft.PowerSh... ...
Cmdlet          Set-Item              Microsoft.PowerSh... ...
Cmdlet          Set-ItemProperty      Microsoft.PowerSh... ...

Many of these cmdlets have historic aliases, and when you look at those, the cmdlets probably become a lot more familiar:

PS> Get-Alias -Definition *-Item*

CommandType     Name                  ModuleName           Definition
-----------     ----                  ----------           ----------
Alias           cli                                        Clear-Item
Alias           clp                                        Clear-ItemProperty
Alias           copy                                       Copy-Item
Alias           cp                                         Copy-Item
Alias           cpi                                        Copy-Item
Alias           cpp                                        Copy-ItemProperty
Alias           del                                        Remove-Item
Alias           erase                                      Remove-Item
Alias           gi                                         Get-Item
Alias           gp                                         Get-ItemProperty
Alias           ii                                         Invoke-Item
Alias           mi                                         Move-Item
Alias           move                                       Move-Item
Alias           mp                                         Move-ItemProperty
Alias           mv                                         Move-Item
Alias           ni                                         New-Item
Alias           rd                                         Remove-Item
Alias           ren                                        Rename-Item
Alias           ri                                         Remove-Item
Alias           rm                                         Remove-Item
Alias           rmdir                                      Remove-Item
Alias           rni                                        Rename-Item
Alias           rnp                                        Rename-ItemProperty
Alias           rp                                         Remove-ItemProperty
Alias           si                                         Set-Item
Alias           sp                                         Set-ItemProperty

Thanks to the "Registry" provider, all of these cmdlets (and their aliases) can also work with the Registry. So if you wanted to list the keys of HKEY_LOCAL_MACHINESoftware, this is how you'd do it:

Dir HKLM:Software

Available Providers

Get-PSProvider gets a list of all available providers. Your list can easily be longer than in the following example. Many PowerShell extensions add additional providers. For example, the ActiveDirectory module that ships with Windows Server 2008 R2 (and the RSAT tools for Windows 7) adds a provider for the Active Directory. Microsoft SQL Server (starting with 2007) comes with an SQLServer provider.

Get-PSProvider
Name                 Capabilities                 Drives
----                 ------------                 ------
Alias                ShouldProcess                {Alias}
Environment          ShouldProcess                {Env}
FileSystem           filter, ShouldProcess        {C, E, S, D}
function             ShouldProcess                {function}
Registry             ShouldProcess                {HKLM, HKCU}
Variable             ShouldProcess                {Variable}
Certificate          ShouldProcess                {cert}

What's interesting here is the “Drives” column, which lists the drives that are managed by a respective provider. As you see, the registry provider manages the drives HKLM: (for the registry root HKEY_LOCAL_MACHINE) and HKCU: (for the registry root HKEY_CURRENT_USER). These drives work just like traditional file system drives. Check this out:

Cd HKCU: 
Dir 
   Hive: Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USER
SKC  VC Name                           Property
---  -- ----                           --------
  2   0 AppEvents                      {}
  7   1 Console                        {CurrentPage}
 15   0 Control Panel                  {}
  0   2 Environment                    {TEMP, TMP}
  4   0 EUDC                           {}
  1   6 Identities                     {Identity Ordinal, Migrated7, Last ...
  3   0 Keyboard Layout                {}
  0   0 Network                        {}
  4   0 Printers                       {}
 38   1 Software                       {(default)}
  2   0 System                         {}
  0   1 SessionInformation             {ProgramCount}
  1   8 Volatile Environment           {LOGONSERVER, USERDOMAIN, USERNAME,...

You can navigate like in the file system and dive deeper into subfolders (which here really are registry keys).

Provider Description Example
Alias Manages aliases, which enable you to address a command under another name. You'll learn more about aliases in Chapter 2. Dir Alias:
$alias:Dir
Environment Provides access to the environment variables of the system. More in Chapter 3. Dir env:
$env:windir
Function Lists all defined functions. Functions operate much like macros and can combine several commands under one name. Functions can also be an alternative to aliases and will be described in detail in Chapter 9. Dir function:
$function:tabexpansion
FileSystem Provides access to drives, directories and files. Dir c:
$(c:autoexec.bat)
Registry Provides access to branches of the Windows registry. Dir HKCU:
Dir HKLM:
Variable Manages all the variables that are defined in the PowerShell console. Variables are covered in Chapter 3. Dir variable:
$variable:pshome
Certificate Provides access to the certificate store with all its digital certificates. These are examined in detail in Chapter 10. Dir cert:
Dir cert: -recurse

Table 16.2: Default providers

Creating Drives

PowerShell comes with two drives built-in that point to locations in the Windows Registry: HKLM: and HKCU:.

Get-PSDrive -PSProvider Registry
Name       Provider      Root                                                      CurrentLocation
----       --------      ----                                                      ---------------
HKCU       Registry      HKEY_CURRENT_USER
HKLM       Registry      HKEY_LOCAL_MACHINE

That's a bit strange because when you open the Registry Editor regedit.exe, you'll see that there are more than just two root hives. If you wanted to access another hive, let's say HKEY_USERS, you'd have to add a new drive like this:

New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS
Dir HKU:

You may not have access to all keys due to security settings, but your new drive HKU: works fine. Using New-PSDrive, you now can access all parts of the Windows Registry. To remove the drive, use Remove-PSDrive (which only works if HKU: is not the current drive in your PowerShell console):

Remove-PSDrive HKU

You can of course create additional drives that point to specific registry keys that you may need to access often.

New-PSDrive InstalledSoftware registry 'HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall'
Dir InstalledSoftware:

Note that PowerShell drives are only visible inside the session you defined them. Once you close PowerShell, they will automatically get removed again. To keep additional drives permanently, add the New-PSDrive statements to your profile script so they get automatically created once you launch PowerShell.

Using Provider Names Directly

Actually, you do not need PowerShell drives at all to access the Registry. In many scenarios, it can be much easier to work with original Registry paths. To make this work, prepend the paths with the provider names like in the example below:

Dir HKLM:Software
Dir Registry::HKEY_LOCAL_MACHINESoftware
Dir Registry::HKEY_USERS
Dir Registry::HKEY_CLASSES_ROOT.ps1

With this technique, you can even list all the Registry hives:

Dir Registry::

Searching for Keys

Get-ChildItem can list all subkeys of a key, and it can of course use recursion to search the entire Registry for keys with specific keywords.

The registry provider doesn't support filters, though, so you cannot use the parameter -Filter when you search the registry. Instead, use -Include and -Exclude. For example, if you wanted to find all Registry keys that include the word “PowerShell”, you could search using:

PS> Get-ChildItem HKCU:, HKLM: -Recurse -Include *PowerShell* -ErrorAction SilentlyContinue |
>>
Select-Object -ExpandProperty Name
>>
HKEY_CURRENT_USERConsole%SystemRoot%_System32_WindowsPowerShell_v1.0_powershell.exe HKEY_CURRENT_USERSoftwareMicrosoftPowerShell HKEY_CURRENT_USERSoftwareMicrosoftPowerShell1ShellIdsMicrosoft.PowerShell

Note that this example searches both HKCU: and HKLM:. The error action is set to SilentlyContinue because in the Registry, you will run into keys that are access-protected and would raise ugly "Access Denied" errors. All errors are suppressed that way.

Searching for Values

Since Registry values are not interpreted as separate items but rather are added to keys as so-called ItemProperties, you cannot use Get-ChildItem to search for Registry values. You can search for values indirectly, though. Here is some code that finds all Registry keys that have at least one value with the keyword "PowerShell":

PS> Get-ChildItem HKCU:, HKLM: -Recurse -ea 0 | Where-Object { $_.GetValueNames() |
>>
Where-Object { $_ -like '*PowerShell*' } }

If you want to find all keys that have a value with the keyword in its data, try this:

PS> Get-ChildItem HKCU:, HKLM: -Recurse -ea 0 | Where-Object { $key = $_ $_.GetValueNames() |
>>
ForEach-Object { $key.GetValue($_) } | Where-Object { $_ -like '*PowerShell*' } }

Reading One Registry Value

If you need to read a specific Registry value in a Registry key, use Get-ItemProperty. This example reads the registered owner:

PS> Get-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftWindows NTCurrentVersion' -Name RegisteredOwner

RegisteredOwner : Tim Telbert
PSPath          : Registry::HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows
                  NTCurrentVersion
PSParentPath    : Registry::HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NT
PSChildName     : CurrentVersion
PSDrive         : HKLM
PSProvider      : Registry

Unfortunately, the Registry provider adds a number of additional properties so you don't get back the value alone. Add another Select-Object to really get back only the content of the value you are after:

PS> Get-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftWindows NTCurrentVersion' -Name RegisteredOwner | 
>>
Select-Object -ExpandProperty RegisteredOwner Tim Telbert

Reading Multiple Registry Values

Maybe you'd like to read more than one Registry value. Registry keys can hold an unlimited number of values. The code is not much different from before. Simply replace the single Registry value name with a comma-separated list, and again use Select-Object to focus only on those. Since this time you are reading multiple properties, use -Property instead of –ExpandProperty parameter.

PS> Get-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftWindows NTCurrentVersion' `
>>
-Name ProductName, EditionID, CSDVersion, RegisteredOwner |
>>
Select-Object -Property ProductName, EditionID, CSDVersion, RegisteredOwner
>>
ProductName EditionID CSDVersion RegisteredOwner ----------- --------- ---------- --------------- Windows 7 Ultimate Ultimate Service Pack 1 Tim Telbert

Or, a little simpler:

PS> Get-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftWindows NTCurrentVersion' |
>>
Select-Object -Property ProductName, EditionID, CSDVersion, RegisteredOwner
>>
ProductName EditionID CSDVersion RegisteredOwner ----------- --------- ---------- --------------- Windows 7 Ultimate Ultimate Service Pack 1 Tim Telbert

Reading Multiple Keys and Values

Yet maybe you want to read values not just from one Registry key but rather a whole bunch of them. In HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall, you find a lot of keys, one for each installed software product. If you wanted to get a list of all software installed on your machine, you could read all of these keys and display some values from them.

That again is just a minor adjustment to the previous code because Get-ItemProperty supports wildcards. Have a look:

PS> Get-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftWindowsCurrentVersionUninstall*' |
>>
Select-Object -Property DisplayName, DisplayVersion, UninstallString DisplayName DisplayVersion UninstallString ----------- -------------- --------------- 0.8.2.232 Microsoft IntelliPoint 8.1 8.15.406.0 msiexec.exe /I {3ED4AD... Microsoft Security Esse... 2.1.1116.0 C:Program FilesMicro... NVIDIA Drivers 1.9 C:Windowssystem32nv... WinImage "C:Program FilesWinI... Microsoft Antimalware 3.0.8402.2 MsiExec.exe /X{05BFB06... Windows XP Mode 1.3.7600.16422 MsiExec.exe /X{1374CC6... Windows Home Server-Con... 6.0.3436.0 MsiExec.exe /I{21E4979... Idera PowerShellPlus Pr... 4.0.2703.2 MsiExec.exe /I{7a71c8a... Intel(R) PROSet/Wireles... 13.01.1000 (...)

Voilá, you get a list of installed software. Some of the lines are empty, though. This occurs when a key does not have the value you are looking for.

To remove empty entries, simply add Where-Object like this:

PS> Get-ItemProperty -Path 'HKLM:SOFTWAREMicrosoftWindowsCurrentVersionUninstall*' |
>>
Select-Object -Property DisplayName, DisplayVersion, UninstallString |
>>
Where-Object { $_.DisplayName -ne $null }

Creating Registry Keys

Since Registry keys are treated like files or folders in the file system, you can create and delete them accordingly. To create new keys, either use historic aliases like md or mkdir, or use the underlying cmdlet directly:

PS> New-Item HKCU:SoftwareNewKey1


    Hive: Registry::HKEY_CURRENT_USERSoftware


Name                           Property
----                           --------
NewKey1
PS> md HKCU:SoftwareNewKey2


    Hive: Registry::HKEY_CURRENT_USERSoftware


Name                           Property
----                           --------
NewKey2

If a key name includes blank characters, enclose the path in quotation marks. The parent key has to exist.

To create a new key with a default value, use New-Item and specify the value and its data type:

PS> New-Item HKCU:SoftwareNewKey3 -Value 'Default Value Text' -Type String

    Hive: Registry::HKEY_CURRENT_USERSoftware


Name                           Property
----                           --------
NewKey3                        (default) : Default Value Text

Deleting Registry Keys

To delete a key, use the historic aliases from the file system that you would use to delete a folder, or use the underlying cmdlet Remove-Item directly:

PS> Remove-Item HKCU:SoftwareTest1
Del HKCU:SoftwareTest2
Del HKCU:SoftwareTest3

This process needs to be manually confirmed if the key you are about to remove contains other keys:

Del HKCU:SoftwareKeyWithSubKeys
Confirm
The item at "HKCU:SoftwareKeyWithSubKeys" has children and the Recurse parameter was not specified. if you continue, all children will be removed with the item. Are you sure you want to continue? 
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Use the –Recurse parameter to delete such keys without manual confirmation:

Del "HKCU:SoftwareFirst key" -Recurse

Creating Values

Each Registry key can have an unlimited number of values. Earlier in this chapter, you learned how to read these values. Values are called "ItemProperties", so they belong to an "Item", the Registry key.

To add new values to a Registry key, either use New-ItemProperty or Set-ItemProperty. New-ItemProperty cannot overwrite an existing value and returns the newly created value in its object form. Set-ItemProperty is more easy going. If the value does not yet exist, it will be created, else changed. Set-ItemProperty does not return any object.

Here are some lines of code that first create a Registry key and then add a number of values with different data types:

PS> New-Item HKCU:SoftwareTestKey4
PS> Set-ItemProperty HKCU:SoftwareTestKey4 -Name Name -Value 'Smith'
PS> Set-ItemProperty HKCU:SoftwareTestKey4 -Name ID -Value 12 -Type DWORD
PS> Set-ItemProperty HKCU:SoftwareTestKey4 -Name Path -Value '%WINDIR%' -Type ExpandString
PS> Set-ItemProperty HKCU:SoftwareTestKey4 -Name Notes -Value 'First Note','Second Note' `
>>
-Type MultiString >>
PS
> Set-ItemProperty HKCU:SoftwareTestKey4 -Name DigitalInfo -Value 4,8,12,200,90 -Type Binary PS> Get-ItemProperty HKCU:SoftwareTestKey4 Name : Smith ID : 12 Path : C:Windows Notes : {First Note, Second Note} DigitalInfo : {4, 8, 12, 200...} PSPath : Registry::HKEY_CURRENT_USERSoftwareTestKey4 PSParentPath : Registry::HKEY_CURRENT_USERSoftware PSChildName : TestKey4 PSDrive : HKCU PSProvider : Registry

If you wanted to set the keys' default value, use '(default)' as value name.

ItemType Description DataType
String A string REG_SZ
ExpandString A string with environment variables that are resolved when invoked REG_EXPAND_SZ
Binary Binary values REG_BINARY
DWord Numeric values REG_DWORD
MultiString Text of several lines REG_MULTI_SZ
QWord 64-bit numeric values REG_QWORD

Table 16.4: Permitted ItemTypes in the registry

Use Remove-ItemProperty to remove a value. This line deletes the value Name value that you created in the previous example:

Remove-ItemProperty HKCU:SoftwareTestkey4 Name

Clear-ItemProperty clears the content of a value, but not the value itself.

Be sure to delete your test key once you are done playing:

Remove-Item HKCU:SoftwareTestkey4 -Recurse

Securing Registry Keys

Registry keys (and its values) can be secured with Access Control Lists (ACLs) in pretty much the same way the NTFS file system manages access permissions to files and folders. Likewise, you can use Get-Acl to show current permissions of a key:

md HKCU:SoftwareTestkey4
Get-Acl HKCU:SoftwareTestkey
Path                                 Owner                           Access
----                                 -----                           ------
Microsoft.PowerShell.CoreRegistr... TobiasWeltne-PCTobias Weltner  TobiasWeltne-PCTobias Weltner A...

To apply new security settings to a key, you need to know the different access rights that can be assigned to a key. Here is how you get a list of these rights:

PS> [System.Enum]::GetNames([System.Security.AccessControl.RegistryRights])
QueryValues
SetValue
CreateSubKey
EnumerateSubKeys
Notify
CreateLink
Delete
ReadPermissions
WriteKey
ExecuteKey
ReadKey
ChangePermissions
TakeOwnership
FullControl

Taking Ownership

Always make sure that you are the “owner” of the key before modifying Registry key access permissions. Only owners can recover from lock-out situations, so if you set permissions wrong, you may not be able to undo the changes unless you are the owner of the key.

This is how to take ownership of a Registry key (provided your current access permissions allow you to take ownership. You may want to run these examples in a PowerShell console with full privileges):

$acl = Get-Acl HKCU:SoftwareTestkey
$acl.Owner
scriptinternalsTobiasWeltner
$me = [System.Security.Principal.NTAccount]"$env:userdomain$env:username"
$acl.SetOwner($me)

Setting New Access Permissions

The next step is to assign new permissions to the key. Let's exclude the group “Everyone” from making changes to this key:

$acl = Get-Acl HKCU:SoftwareTestkey
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.RegistryRights]"WriteKey"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Deny"

$rule = New-Object System.Security.AccessControl.RegistryAccessRule(`
$person,$access,$inheritance,$propagation,$type) $acl.AddAccessRule($rule) Set-Acl HKCU:SoftwareTestkey $acl

The modifications immediately take effect.Try creating new subkeys in the Registry editor or from within PowerShell, and you’ll get an error message:

md HKCU:SoftwareTestkeysubkey
New-Item : Requested Registry access is not allowed.
At line:1 char:34
+ param([string[]]$paths) New-Item  <<<< -type directory -path $paths

Why does the restriction applies to you as an administrator? Aren't you supposed to have full access? No, restrictions always have priority over permissions, and because everyone is a member of the Everyone group, the restriction applies to you as well. This illustrates that you should be extremely careful applying restrictions. A better approach is to assign permissions only.

Removing an Access Rule

The new rule for Everyone was a complete waste of time after all because it applied to everyone, effectively excluding everyone from the key. So, how do you go about removing a rule? You can use RemoveAccessRule() to remove a particular rule, and RemoveAccessRuleAll() to remove all rules of the same type (permission or restriction) for the user named in the specified rule. ModifyAccessRule() changes an existing rule, and PurgeAccessRules() removes all rules for a certain user.

To remove the rule that was just inserted, proceed as follows:

$acl = Get-Acl HKCU:SoftwareTestkey
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.RegistryRights]"WriteKey"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Deny"

$rule = New-Object System.Security.AccessControl.RegistryAccessRule(`
$person,$access,$inheritance,$propagation,$type) $acl.RemoveAccessRule($rule) Set-Acl HKCU:SoftwareTestkey $acl -Force

However, removing your access rule may not be as straightforward because you have effectively locked yourself out. Since you no longer have modification rights to the key, you are no longer allowed to modify the keys' security settings as well.

You can overrule this only if you take ownership of the key: Open the Registry editor, navigate to the key, and by right-clicking and then selecting Permissions open the security dialog box and manually remove the entry for Everyone.

You’ve just seen how relatively easy it is to lock yourself out. Be careful with restriction rules.

Controlling Access to Sub-Keys

In the next example, you use permission rules rather than restriction rules. The task: create a key where only administrators can make changes. Everyone else should just be allowed to read the key.

md HKCU:SoftwareTestkey2
$acl = Get-Acl HKCU:SoftwareTestkey2

# Admins may do everything:
$person = [System.Security.Principal.NTAccount]Administrators
$access = [System.Security.AccessControl.RegistryRights]"FullControl"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(`
$person,$access,$inheritance,$propagation,$type) $acl.ResetAccessRule($rule) # Everyone may only read and create subkeys: $person = [System.Security.Principal.NTAccount]"Everyone" $access = [System.Security.AccessControl.RegistryRights]"ReadKey" $inheritance = [System.Security.AccessControl.InheritanceFlags]"None" $propagation = [System.Security.AccessControl.PropagationFlags]"None" $type = [System.Security.AccessControl.AccessControlType]"Allow" $rule = New-Object System.Security.AccessControl.RegistryAccessRule(`
$person,$access,$inheritance,$propagation,$type) $acl.ResetAccessRule($rule) Set-Acl HKCU:SoftwareTestkey2 $acl

Note that in this case the new rules were not entered by using AddAccessRule() but by ResetAccessRule(). This results in removal of all existing permissions for respective users. Still, the result isn’t right because regular users could still create subkeys and write values:

md hkcu:softwareTestkey2Subkey

   Hive: Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERsoftwareTestkey2

SKC  VC Name                           Property
---  -- ----                           --------
  0   0 Subkey                 {}

Set-ItemProperty HKCU:SoftwareTestkey2 Value1 "Here is text" 

Revealing Inheritance

Look at the current permissions of the key to figure out why your permissions did not work the way you planned:

(Get-Acl HKCU:SoftwareTestkey2).Access | Format-Table -Wrap

    RegistryRights  AccessControlType IdentityReference IsInherited  InheritanceFlags  PropagationFlags
    --------------  ----------------- ----------------- -----------  ----------------  ----------------
         ReadKey              Allow Everyone                   False              None              None
       FullControl              Allow BUILT-inAdmi       False              None              None
                                      nistrators
       FullControl              Allow TobiasWeltne-PCT        True ContainerInherit,              None
                                      obias Weltner                           ObjectInherit
       FullControl              Allow NT AUTHORITYSYST        True ContainerInherit,              None
                                      EM                                      ObjectInherit
       FullControl              Allow BUILT-inAdmi        True ContainerInherit,              None
                                      nistrators                              ObjectInherit
           ReadKey              Allow NT AUTHORITYREST        True ContainerInherit,              None
                                      RICTED ACCESS                           ObjectInherit

The key includes more permissions than what you assigned to it. It gets these additional permissions by inheritance from parent keys. If you want to turn off inheritance, use SetAccessRuleProtection():

$acl = Get-Acl HKCU:SoftwareTestkey2
$acl.SetAccessRuleProtection($true, $false)
Set-Acl HKCU:SoftwareTestkey2 $acl

Now, when you look at the permissions again, the key now contains only the permissions you explicitly set. It no longer inherits any permissions from parent keys:

RegistryRights  AccessControlType IdentityReference IsInherited  InheritanceFlags  PropagationFlags
--------------  ----------------- ----------------- -----------  ----------------  ----------------
       ReadKey              Allow Everyone                False              None              None
    FullControl             Allow BUILT-inAdmistrators   False              None              None

Controlling Your Own Inheritance

Inheritance is a sword that cuts both ways. You have just turned off the inheritance of permissions from parent keys, but will your own newly set permissions be propagated to subkeys? Not by default. If you want to pass on your permissions to subdirectories, change the setting for propagation, too. Here are all steps required to secure the key:

del HKCU:SoftwareTestkey2 
md HKCU:SoftwareTestkey2

$acl = Get-Acl HKCU:SoftwareTestkey2

# Admins may do anything:
$person = [System.Security.Principal.NTAccount]Administrators
$access = [System.Security.AccessControl.RegistryRights]"FullControl"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"ObjectInherit,ContainerInherit"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(`
$person,$access,$inheritance,$propagation,$type) $acl.ResetAccessRule($rule) # Everyone may only read and create subkeys: $person = [System.Security.Principal.NTAccount]"Everyone" $access = [System.Security.AccessControl.RegistryRights]"ReadKey" $inheritance = [System.Security.AccessControl.InheritanceFlags]"ObjectInherit,ContainerInherit" $propagation = [System.Security.AccessControl.PropagationFlags]"None" $type = [System.Security.AccessControl.AccessControlType]"Allow" $rule = New-Object System.Security.AccessControl.RegistryAccessRule(`
$person,$access,$inheritance,$propagation,$type) $acl.ResetAccessRule($rule) Set-Acl HKCU:SoftwareTestkey2 $acl