Tired of struggling how to figure out how to set registry keys and values for all users on Windows? Look no further! Active Setup is here to save the day!

I'm constantly writing PowerShell scripts to deploy my applications. At least 1/4 of these applications need to have some kind of registry value added or modified in not only HKEY_LOCAL_MACHINE but also HKEY_CURRENT_USER. As soon as I get some kind of batch file from an application owner referencing HKEY_CURRENT_USER I cringe. I cringe because not only do I need to modify a registry value for the currently logged on user I need to do it for all users on the desktop to ensure all users that use that machine are able to use the software.

It's not just as simple as modifying a value in something like HKEY_CURRENT_USER\Software\MyApplication for two reasons.

  1. I deploy software to systems and the deployment always runs as the local SYSTEM account. The HKEY_CURRENT_USER "hive" is only available when a user is logged on. Since I'm "logged on" as SYSTEM I have no access to the "real" logged on user hive.
  2. #2 Changing a registry value in HKEY_CURRENT_USER (even if I could get to it) isn't enough. To ensure a common experience across any user that logs onto that computer it's necessary to not only modify a value for the currently logged on user but for all users on that machine.

Don't worry though there are solutions but first a little background on HKEY_CURRENT_USER. HKEY_CURRENT_USER is simply a pointer to HKEY_USERS\%USERSID% and HKEY_USERS\%USERSID% is accessible outside of the currently logged on user. This means when running a script as SYSTEM on a computer with an interactive logon I can easily modify a registry value inside of the user SID registry key in HKEY_USERS which is the same thing as HKEY_CURRENT_USER. Problem solved for the currently logged on user. Yay!

The next task is changing the value for across all user profiles. This is the tricky part. For years I used a VBscript that searched through every user profile on the file system, loaded the user's NTUser.dat file, modified what I needed to and unloaded it. It worked...sometimes. I found that every now and then there'd be permission problems of some sort and the hive would be stuck in a state of limbo. The next time the affected user logged on their profile got corrupted and anger would ensue. I had to come up with a better solution.

This is why I started using a feature called Active Setup. Click through the link to learn the specific of the feature but in a nutshell it's a simple method to run a command once for every user (new or existing) on a computer. When a user logs on, Active Setup kicks in and checks a particular registry key in HKEY_LOCAL_MACHINE to see if a matching key exists in the current HKEY_CURRENT_USER. If not, it executes a command specified in a registry value called StubPath. This is a perfect way to implement the RunOnce method for the entire system on a per-user basis.

In my instance, I'm using this technique to place an old school reg add command in there to create or modify a registry value inside HKEY_CURRENT_USER. When Active Setup runs, it always runs in the current logged on user context so I now have the opportunity to do whatever I need at that point.

You know I wouldn't just get you all excited about this cool technique without sharing an automated PowerShell way to implement it, right? :) Here's what I've come up with to make this happen; a PowerShell function that you can call in your script.

I made it as simple as possible. With a single line you don't have to worry about all the key paths. It just works across the board. Simply pass a hashtable containing the registry key path, name, value and the type of registry value you'd like to create/modify and you're done! If you need to modify more than one value just pass in an array to it and it will change them all.

function Set-RegistryValueForAllUsers {
	<#
    .SYNOPSIS
        This function uses Active Setup to create a "seeder" key which creates or modifies a user-based registry value
        for all users on a computer. If the key path doesn't exist to the value, it will automatically create the key and add the value.
    .EXAMPLE
        PS&gt; Set-RegistryValueForAllUsers -RegistryInstance @{'Name' = 'Setting'; 'Type' = 'String'; 'Value' = 'someval'; 'Path' = 'SOFTWARE\Microsoft\Windows\Something'}
    
        This example would modify the string registry value 'Type' in the path 'SOFTWARE\Microsoft\Windows\Something' to 'someval'
        for every user registry hive.
    .PARAMETER RegistryInstance
        A hash table containing key names of 'Name' designating the registry value name, 'Type' to designate the type
        of registry value which can be 'String,Binary,Dword,ExpandString or MultiString', 'Value' which is the value itself of the
        registry value and 'Path' designating the parent registry key the registry value is in.
    #>
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [hashtable[]]$RegistryInstance
    )
    try {
        New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null
        
        ## Change the registry values for the currently logged on user. Each logged on user SID is under HKEY_USERS
        $LoggedOnSids = (Get-ChildItem HKU: | where { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+

<a href="https://gallery.technet.microsoft.com/scriptcenter/Easily-set-a-registry-b3449784">Download this script on the Technet Script Repository</a>
 }).PSChildName
        Write-Verbose "Found $($LoggedOnSids.Count) logged on user SIDs"
        foreach ($sid in $LoggedOnSids) {
            Write-Verbose -Message "Loading the user registry hive for the logged on SID $sid"
            foreach ($instance in $RegistryInstance) {
                ## Create the key path if it doesn't exist
                New-Item -Path "HKU:\$sid\$($instance.Path | Split-Path -Parent)" -Name ($instance.Path | Split-Path -Leaf) -Force | Out-Null
                ## Create (or modify) the value specified in the param
                Set-ItemProperty -Path "HKU:\$sid\$($instance.Path)" -Name $instance.Name -Value $instance.Value -Type $instance.Type -Force
            }
        }
        
        ## Create the Active Setup registry key so that the reg add cmd will get ran for each user
        ## logging into the machine.
        ## http://www.itninja.com/blog/view/an-active-setup-primer
        Write-Verbose "Setting Active Setup registry value to apply to all other users"
        foreach ($instance in $RegistryInstance) {
            ## Generate a unique value (usually a GUID) to use for Active Setup
            $Guid = [guid]::NewGuid().Guid
            $ActiveSetupRegParentPath = 'HKLM:\Software\Microsoft\Active Setup\Installed Components'
            ## Create the GUID registry key under the Active Setup key
            New-Item -Path $ActiveSetupRegParentPath -Name $Guid -Force | Out-Null
            $ActiveSetupRegPath = "HKLM:\Software\Microsoft\Active Setup\Installed Components\$Guid"
            Write-Verbose "Using registry path '$ActiveSetupRegPath'"
            
            ## Convert the registry value type to one that reg.exe can understand.  This will be the
            ## type of value that's created for the value we want to set for all users
            switch ($instance.Type) {
                'String' {
                    $RegValueType = 'REG_SZ'
                }
                'Dword' {
                    $RegValueType = 'REG_DWORD'
                }
                'Binary' {
                    $RegValueType = 'REG_BINARY'
                }
                'ExpandString' {
                    $RegValueType = 'REG_EXPAND_SZ'
                }
                'MultiString' {
                    $RegValueType = 'REG_MULTI_SZ'
                }
                default {
                    throw "Registry type '$($instance.Type)' not recognized"
                }
            }
            
            ## Build the registry value to use for Active Setup which is the command to create the registry value in all user hives
            $ActiveSetupValue = "reg add <code>"{0}</code>" /v {1} /t {2} /d {3} /f" -f "HKCU\$($instance.Path)", $instance.Name, $RegValueType, $instance.Value
            Write-Verbose -Message "Active setup value is '$ActiveSetupValue'"
            ## Create the necessary Active Setup registry values
            Set-ItemProperty -Path $ActiveSetupRegPath -Name '(Default)' -Value 'Active Setup Test' -Force
            Set-ItemProperty -Path $ActiveSetupRegPath -Name 'Version' -Value '1' -Force
            Set-ItemProperty -Path $ActiveSetupRegPath -Name 'StubPath' -Value $ActiveSetupValue -Force
        }
    } catch {
        Write-Warning -Message $_.Exception.Message
    }
}
Set-RegistryValueForAllUsers

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!