Last updated on December 21st, 2022 at 04:55 pm
A question was recently asked about how to set the wallpaper fit with Intune (fill, stretch, tile, etc.). This reminded me of a previous blog post about setting the background and lock screen on Windows 10 Pro devices with Intune (you can see that post here). I figured I’d update that post but found myself making a separate post. This turned into a deep rabbit hole, but I went down it anyway trying to figure this out, which spawned this blog post. The registry key containing the wallpaperstyle property, which sets the image fit, is located in HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies. Also, setting the value in this key disables a standard user from changing the wallpaper fit. You’d think running a script to add the values as the logged-in user would suffice. However, standard users only have read access to that key (the system account has full control). So, running a simple PowerShell script as the logged in user to add the necessary values fails. If we run it as system, it won’t add the keys to the proper HKCU hive, since they will be added to the HKCU hive for the system user (for more on this see this post).
This got me thinking about how to easily make changes to HKCU keys when standard users don’t have permissions. Figuring out how to do this became the primary goal. First, I thought perhaps we could run a script as system to modify the permissions on the necessary Key, then run our script as the user to add the values, and then change the permissions back. That would probably have worked, but it’s a lot of moving pieces and a lot more work. I scrapped that idea shortly after going down that path. After some more thinking, we do have access as system to the HKEY_Users hive. This allows us access the HKCU hives for each user, so I began exploring how we can properly edit these as system. These are displayed with the SID of users who have signed into the workstation. Determining the SID of the signed in user became the next obstacle.
The challenge was running as system, but identifying the SID of the signed in standard user. With Azure AD users, they don’t have a local user account presence. So, regular commands like wmic useraccount get name,sid don’t return Azure AD users. Although there are other methods to get their SID of a signed in user, most of them involve some sort of manual intervention. In this case we need the script to execute on its own with no user or admin interaction. You can see below how some user SIDs in the registry begin with S-1-12, which means they are Azure AD user, and our local user SIDs don’t show Azure AD users:
After some research and a bunch of testing, I came up with a PowerShell script. The first portion of the script does some discovery and identifies the SID of the currently logged on user by searching the registry. The second part of the script makes the registry edits you need to the HKU\$SID of the logged in user. In this case, setting the wallpaper fit. The script is broken into two parts, with the first part below:
- Identifies the currently logged on user. By default, it pulls the value as domain\username
- We need this shortened to just the username, so we can accurately search the registry for the user profile. So, the value is shortened and stored as the $userwithoutdomain variable
- We search the HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList key for the $userwithoutdomain variable value, which will provide the full path to the Key containing the user profile (the parent key is the logged-in user’s SID).
- Next, we remove all the contents before the first character of the SID and store it as the $SID variable. This gives us the plain SID of the logged-on user, which we use in the second part of the script.
$profilelist = "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
$loggedonuser = Get-Ciminstance -ClassName Win32_ComputerSystem | Select-Object UserName
$loggedonusername = $loggedonuser.username
$userwithoutdomain = $loggedonusername -replace "^.*?\\"
$GetSID = Get-ChildItem -Path $profilelist -rec -ea SilentlyContinue | % { if((get-itemproperty -Path $_.PsPath) -match "$userwithoutdomain") { $_.PsPath} }
$SID = $GetSID -replace "^.*?list\\"
#====================================================#
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS
$RegPath = "HKU:\$SID\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$WallPaperFit = "0"
# 0 - center
# 1 - tile
# 2 - stretch
# 3 - fit
# 4 - fill
# 5 - span
New-Item $regpath -Force
New-ItemProperty -Path $RegPath -Name WallPaperStyle -Value $WallPaperFit -PropertyType String -Force | Out-Null
A breakdown of the top half of the script is shown below with each individual line, and how we end up retrieving the SID. Where you can see PowerShell is running as system, but we are able to pull the SID for the logged-on user MFoley:
The second half of the script is fairly simple. Now that we have the SID, we can use that variable and make edits to the HKU hive as the system account for the logged-in user. In this example, we want to edit HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System and add the value WallPaperStyle. For this example, I’ll use the WallPaperStyle as center, which uses the value of 0. The other values to choose from are below:
- 0 – center
- 1 – tile
- 2 – stretch
- 3 – fit
- 4 – fill
- 5 – span
Adding values directly to the HKU hive under the key for the user will reflect in their loaded HKCU hive. Now that we have a working script. A GIF is below showing the script being manually executed. The two sections of the script are executed separately to show how the SID is retrieved and then how the values are written for the logged in user by the system account. The wallpaper fit doesn’t take effect until the user signs out and back in:
Now that we have a working script, we can create an uninstall script to delete the added registry entries, and push the reg edits through Intune as a Win32 app. There was one other problem, though, which was the detection rule. How do we use registry detection since this app is going to run as system? We need to use a custom detection script. If you’re not familiar with custom detection scripts, Andrew Taylor has a great write-up here which helped me create a quick detection script in short order. Our detection script is below. It uses the same method our install script uses to pull the SID of the user, so we can identify if the registry keys exist for the logged in user:
$profilelist = "HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
$loggedonuser = Get-Ciminstance -ClassName Win32_ComputerSystem | Select-Object UserName
$loggedonusername = $loggedonuser.username
$userwithoutdomain = $loggedonusername -replace "^.*?\\"
#CD $ProfileList
$GetSID = Get-ChildItem -Path $profilelist -rec -ea SilentlyContinue | % { if((get-itemproperty -Path $_.PsPath) -match "$userwithoutdomain") { $_.PsPath} }
$SID = $GetSID -replace "^.*?list\\"
#====================================================#
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS
$RegPath = "HKU:\$SID\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$WallPaperStyleValue = Get-ItemPropertyValue $RegPath -Name WallPaperStyle
If ($WallPaperStyleValue -eq 0){
Write-Output "Detected"
Exit 0
}
Else {
exit 1
}
We create the Win32 app the same way we create any other Win32 App. The exact scripts I used in this blog are available on my Github here. First, package your install and uninstall scripts as an .intunewin file. My install script is named wallpaperfitsystem.ps1. Create a new Win32 app and upload the .intunewin file:
Provide a Name and Description:
Install behavior is system and the install and uninstall commands are below. If you are not familiar with sysnative and running PowerShell in 64-bit context, see this blog post.
- Install: %windir%\sysnative\WindowsPowerShell\v1.0\powershell.exe -Executionpolicy Bypass .\WallPaperFit.ps1
- uninstall: %windir%\sysnative\WindowsPowerShell\v1.0\powershell.exe -Executionpolicy Bypass .\uninstall.ps1
Add your requirement rules, and use a custom detection script. Edit the custom script if needed, and upload it as the detection method.
Chose any dependencies if you have any. I have a separate Win32 app that deploys wallpaper and lockscreen images. So, I’ll make that a dependency for this app. Lastly, assign your app to your target groups. Once machines start showing successfully installed, the new wallpaper fit won’t take effect until they sign out and back in (like in the GIF posted above).
That’s all! This definitely isn’t a common request to edit registry keys in HKCU where the user doesn’t have permission, but it was fun figuring this out. Hope this helps.