Skip to content

How to modify the registry for all users with PowerShell

Brock Bingham candid headshot
Brock Bingham|Updated June 20, 2024
Illustration of block with Powershell logo
Illustration of block with Powershell logo

Modifying the Windows registry for all users can be — challenging — to say the least. But, as always, we like a good a good challenge around here. Our mission today, if we choose to accept it, is to learn how to modify the registry for all users with PowerShell.

If you missed it, we already covered how to modify the registry for another user, which serves as a precursor to this article. Definitely check it out to learn more about this topic. Spoiler alert: It was pretty dang exciting.

The problem with logged-on users

Before we can modify the registry for all users, we need to grab all the ntuser.dat files so that we can load them into the registry. As a reminder, the ntuser.dat files are located in the root of each user profile directory (i.e., C:\Users\<username>\ntuser.dat).

If you read through our previous article (link above), then you’re probably thinking no big deal. However, this only works if no other accounts are logged on to the machine. The reason is that any currently logged-on users already have their ntuser.dat files loaded into the registry. This includes users who forget to log off. Even though their session is disconnected and somebody else has logged on, their registry is still loaded in the registry.

Here’s an example of this conflict.

PS-Blog-Registry-HKU-example

If I try loading this disconnected user’s ntuser.dat, I encounter an error telling me that it’s already being used by another process.

PS-Blog-Registry-Cannot-load-ntuser.dat

So, what do we do? Well, first we need to find all users on a machine and compare it with all currently logged-on user security identifiers (SIDs).

How to identify all users and their SIDs in Windows

Identifying all users and their SIDs on a Windows computer is pretty easy because all the information is stored in a registry key. Here’s how to find the information we’re looking for.

  1. Launch Registry Editor (regedit.exe).

  2. Navigate to this registry key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\

  3. Identify the user security identifiers (SIDs) starting with S-1-5-21.

This location has a list of all the SIDs for the machine as well as some other properties. We’re interested in the SIDs that start with S-1-5-21. Notice that you see the two SIDs from an earlier screenshot:

PS-Blog-Registry-ProfileList-example

With this information, we can use regular expressions and calculated properties to select some great information with PowerShell. We’ll use the Get-ItemProperty cmdlet to get that information from the registry.

$PatternSID = 'S-1-5-21-\d+-\d+\-\d+\-\d+$' Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} | select @{name="SID";expression={$_.PSChildName}}, @{name="UserHive";expression={"$($_.ProfileImagePath)\ntuser.dat"}}, @{name="Username";expression={$_.ProfileImagePath -replace '^(.*[\\\/])', ''}}

Now we have a list of the usernames and their associated SIDs.

Comparing SIDs to usernames in PowerShell.

Comparing profile SIDs to logged-on user SIDs

It’s time to compare the SIDs found in HKEY_LOCAL_MACHINE to the SIDs loaded in HKEY_USERS. We’ll use a PowerShell command to match the objects.

Get-ChildItem Registry::HKEY_USERS | Where-Object {$_.PSChildName -match $PatternSID} | select PSChildName

PowerShell script to modify the registry for all users

Now, we just need to compare the two lists of SIDs and we’ll be able to modify the registry at will. I’ve compiled it all into a template that you can use to read or modify the registry of each user on a machine. In my example, I load each registry (if not loaded) and attempt to read the Uninstall key at HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*.

This script shows me which users have per-user installs of software as well as the software name.

# Regex pattern for SIDs $PatternSID = 'S-1-5-21-\d+-\d+\-\d+\-\d+$' # Get Username, SID, and location of ntuser.dat for all users $ProfileList = gp 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} | Select @{name="SID";expression={$_.PSChildName}}, @{name="UserHive";expression={"$($_.ProfileImagePath)\ntuser.dat"}}, @{name="Username";expression={$_.ProfileImagePath -replace '^(.*[\\\/])', ''}} # Get all user SIDs found in HKEY_USERS (ntuder.dat files that are loaded) $LoadedHives = gci Registry::HKEY_USERS | ? {$_.PSChildname -match $PatternSID} | Select @{name="SID";expression={$_.PSChildName}} # Get all users that are not currently logged $UnloadedHives = Compare-Object $ProfileList.SID $LoadedHives.SID | Select @{name="SID";expression={$_.InputObject}}, UserHive, Username # Loop through each profile on the machine Foreach ($item in $ProfileList) { # Load User ntuser.dat if it's not already loaded IF ($item.SID -in $UnloadedHives.SID) { reg load HKU\$($Item.SID) $($Item.UserHive) | Out-Null } ##################################################################### # This is where you can read/modify a users portion of the registry # This example lists the Uninstall keys for each user registry hive "{0}" -f $($item.Username) | Write-Output Get-ItemProperty registry::HKEY_USERS\$($Item.SID)\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Foreach {"{0} {1}" -f " Program:", $($_.DisplayName) | Write-Output} Get-ItemProperty registry::HKEY_USERS\$($Item.SID)\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Foreach {"{0} {1}" -f " Program:", $($_.DisplayName) | Write-Output} ##################################################################### # Unload ntuser.dat IF ($item.SID -in $UnloadedHives.SID) { ### Garbage collection and closing of ntuser.dat ### [gc]::Collect() reg unload HKU\$($Item.SID) | Out-Null } }
Modifying the registry for all users full script results.

See more about Out-Null, Compare-Object, and Write-Output.

Modify the registry at your own risk

Use this information with a healthy dose of caution. Modifying the registry is risky and can quickly turn a good day into a bad one. Even good reasons to modify the registry aren’t good enough. Be responsible and thoroughly test your scripts in a safe environment before running them anywhere near your production environment. We cannot be held responsible for any issues that you may encounter.

Brock Bingham candid headshot
Brock Bingham

Born in the '80s and raised by his NES, Brock quickly fell in love with everything tech. With over 15 years of IT experience, Brock now enjoys the life of luxury as a renowned tech blogger and receiver of many Dundie Awards. In his free time, Brock enjoys adventuring with his wife, kids, and dogs, while dreaming of retirement.

Related articles