There are times that you may need to push out a change to all existing user profiles and to new profiles that are created on a system. I’ve seen a few PowerShell scripts floating around out there, but they didn’t seem to work for Windows 7 SP1. You may or may not be surprised, but there are many organizations that still run Windows 7. The script is actually pretty simple.
Here is the breakdown of the script:
- Enumerate all the existing user profiles
- Add the .DEFAULT user profile to the list of existing user profiles
-
Iterate through all the profiles
- If the profile hive is not loaded, load it
- Manipulate the users’ registry
- If the profile hive was loaded by the script, unload it
- Finished
Enumerate all the existing user profiles
Using the registry path below, we can find a list of all the user profiles on the system and where the profile path exists. Every user profile has the file NTuser.dat which contains the registry hive that is loaded into the HKEY_USERS and HKCU when a user logs on to the system. This NTuser.dat can for example also be loaded when using RunAs.exe. It will then only show up in HKEY_USERS\<users’ SID>
# Get each user profile SID and Path to the profile $UserProfiles = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" | Where {$_.PSChildName -match "S-1-5-21-(\d+-?){4}$" } | Select-Object @{Name="SID"; Expression={$_.PSChildName}}, @{Name="UserHive";Expression={"$($_.ProfileImagePath)\NTuser.dat"}}
Add the .DEFAULT user profile to the list of existing user profiles
If you need to manipulate the registry of all new profiles, then you’ll need to add the following code. The .DEFAULT user information does not exist in the registry key information above.
# Add in the .DEFAULT User Profile $DefaultProfile = "" | Select-Object SID, UserHive $DefaultProfile.SID = ".DEFAULT" $DefaultProfile.Userhive = "C:\Users\Public\NTuser.dat" $UserProfiles += $DefaultProfile
Iterate through all the profiles
This is the main code where we will determine if we need to load or unload any user registry hives. It is also where the registry changes will be made.
# Loop through each profile on the machine</p> Foreach ($UserProfile in $UserProfiles) { # Load User ntuser.dat if it's not already loaded If (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) { Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE LOAD HKU\$($UserProfile.SID) $($UserProfile.UserHive)" -Wait -WindowStyle Hidden }
Manipulate the users’ registry
This is the area where you can create, delete or modify the registry. After the changes are made, the profile will be unloaded. Upon the next logon, the changes will come into effect.
# Manipulate the registry $key = "Registry::HKEY_USERS\$($UserProfile.SID)\Software\SomeArchaicSoftware\Configuration" New-Item -Path $key -Force | Out-Null New-ItemProperty -Path $key -Name "LoginURL" -Value "https://www.myCompany.local" -PropertyType STRING -Force | Out-Null New-ItemProperty -Path $key -Name "DisplayWelcome" -Value 0x00000001 -PropertyType DWORD -Force | Out-Null $key = "$key\UserInfo" New-Item -Path $key -Force | Out-Null New-ItemProperty -Path $key -Name "LoginName" -Value "$($ENV:USERDOMAIN)\$($ENV:USERNAME)" -PropertyType STRING -Force | Out-Null
If the profile hive was loaded by the script, unload it
This is another area that is easier to just call out to REG.EXE again to unload the registry. One issue to keep in mind is that if any handles are open to the registry, they need to be closed. If they’re not closed, you’ll get “Access Denied” when trying to unload the registry hive. This is why I’ve added the Garbage Collector. This cleans up all open handles [gc]::Collector. I also noticed that if I was opening and closing registry hives too fast, they all weren’t being closed. I’m guessing this is due to a race condition. I added a Start-Sleep 1 and this fixed the problem for me.
# Unload NTuser.dat If ($ProfileWasLoaded -eq $false) { [gc]::Collect() Start-Sleep 1 Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE UNLOAD HKU\$($UserProfile.SID)" -Wait -WindowStyle Hidden| Out-Null }
Happy coding and I hope this helps you solve whatever your problem was!
POWERSHELL – UPDATING THE .DEFAULT AND ALL USER PROFILES REGISTRY
This is some great code you’ve posted. Great step-by-step, made it really easy to follow then modify to fit my needs.
Thank you a whole heaven of a lot for share this code.
Have an awesome day.
I’m having a problem with this line:
$UserProfiles += $DefaultProfile
It generates the following error: “Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named ‘op_Addition’.”
I’m running Powershell v5 under Windows 10 Build 1703
Any ideas?
Thanks!
Having the same issue as Bill Westrup.
Win10 Build 1709
$UserProfiles = @($DefaultProfile,$UserProfiles)
To add userprofiles for Azure AD users, replace the first line with this to show both regular and Azure AD usersprofiles:
$UserProfiles = Get-ItemProperty “HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*” | Where {$_.PSChildName -match “S-1-5-21-(\d+-?){4}$” -or $_.PSChildName -match “S-1-12-1-(\d+-?){4}$” } | Select-Object @{Name=”SID”; Expression={$_.PSChildName}}, @{Name=”UserHive”;Expression={“$($_.ProfileImagePath)\NTuser.dat”}}
Thank you, helps with the AAD-only journey 🙂
I am getting an Error saying that this is missing a closing ‘}’ in statement block or type Definition in the code below
# Loop through each profile on the machine
Foreach ($UserProfile in $UserProfiles) { < this is were the error is
# Load User ntuser.dat if it's not already loaded
If (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE LOAD HKU\$($UserProfile.SID) $($UserProfile.UserHive)" -Wait -WindowStyle Hidden
}
I am getting an Error in windows 10 1803 ISE Missing closing ‘}’ in statement block or type definition in the below code.
# Loop through each profile on the machine
Foreach ($UserProfile in $UserProfiles) { <this is were I am getting the error
# Load User ntuser.dat if it's not already loaded
If (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE LOAD HKU\$($UserProfile.SID) $($UserProfile.UserHive)" -Wait -WindowStyle Hidden
}
You need to add a curly brace right at the end of the script . Worked for me. OP is a hero ! thank you!
Big warning. .Default is not the same as users\default\ntuser.dat. .Default is system account (like logon screen).
I love the code and it works just fine for me. However I would like to Remove a registry entry and all its child properties as oppose to adding an entry.
Any advise would be appreciated as I am fairly new to Powershell.
I did have a few tries but failed miserably.
I want to delete a registry set instead of adding.
I tried Remove-Item -Path Registry::HKEY_USERS\$($UserProfile.SID)\Software\SomeArchaicSoftware -Recurse
but it did not work. — because it does not exist.
Where am I going wrong
Dinked around with this for a while before figuring out that it has a big problem:
.DEFAULT is NOT the default user profile, it is the profile Windows is using before anyone logs in.
So In addition to not being effective in changing default settings for new users, this could cause a bunch of extra problems by loading the default user’s NTUSER.DAT on top of .DEFAULT’s hive.
Fix: Change this line:
$DefaultProfile.SID = “.DEFAULT”
to anything other than .DEFAULT. I used “defaultuser”.
Now it’ll load the hive into its own space and work as expected.
Also there’s a missing closing } at the very end.
I tried to perform this command running in powershell with SYSTEM permission (because SCCM uses this permission) and it didn’t work. Any suggestions to have changes running in SYSTEM context?
Great script. Saved me a bunch of work. Thanks for sharing.