Looks like my organization (accidentally) wants to manage my background picture. But I'm a local admin, so let's see if we can still change that background picture. When I go to Setting > Personalization > Background, I see the message: "Some of these settings are managed by your organization." If you are an admin, you might be able to (temporarily) override the wallpaper, by editing your registry and triggering a refresh of the wallpaper. If you can, you can automate it using PowerShell.
To have more control over the look and feel of Windows 11, install Dynamic Theme by Christophe Lavalle from the Windows Store. It will give you the pictures from the Bing and Windows Spotlight sources. You can enable them to save the picture in your profile. For Windows Spotlight they are located in $env:USERPROFILE\Pictures\Windows Spotlight Images.
The idea 🤓
Let's do the following:
- The script lets you set a static background image or set a background images path that will search for the latest JPG image.
- Setting the registry requires elevation, so we want to prevent to reset the registry if we don't need to. That's why we're copying the background image to
wallpaper.jpgand the path of the image towallpaper.jpg.txt. - If the registry points to
wallpaper.jpgwe only need to copy the new image to the file and restart the explorer. - If the registry is pointing towards something else, we need some elevation to set the correct properties.
"Native" reload of the wallpaper
You can restart the explorer.exe process to force the wallpaper to be updated, but that seems the equivalent of killing a fly with a cannon. The following snippet uses native interop to force a wallpaper change without us having to restart Explorer:
# Force explorer to reload the wallpaper
if (-not ([type]::GetType("Wallpaper"))) {
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class Wallpaper {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SystemParametersInfo(uint uiAction, uint uiParam, string pvParam, uint fWinIni);
}
"@
}
$SPI_SETDESKWALLPAPER = 0x0014
$SPIF_UPDATEINIFILE = 0x01
$SPIF_SENDCHANGE = 0x02
[Wallpaper]::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $null, $SPIF_UPDATEINIFILE -bor $SPIF_SENDCHANGE)The script
I've created a script that will set the latest image from a certain directory. Let's view the config options:
$StaticBackgroundImagePath- set this full path if you want a static background image.$BackgroundImagesPathis the name of the directory where your background images are located. If no static background image is set, it will try to find the latest JPG file.$EnablePauseBeforeAdminPrompt- my organization uses Make Me Admin. You need to become admin explicitly before executing administrative tasks. Having a pause helps to activate admin rights before the admin prompt appears. You might not need this.$AdminCountDownSeconds- when an elevated screen is launched, the script won't exit immediately, but wait a bit before it exits, so you can read the output.
Without further ado, here's the script:
# Config options:
$PicturesFolder = [Environment]::GetFolderPath('MyPictures')
$BackgroundImagesPath = "$PicturesFolder\Windows Spotlight Images"
$StaticBackgroundImagePath = $null
$EnablePauseBeforeAdminPrompt = $true
$CountDownBeforeContinueSeconds = 5
# Vars
$WallpaperPath = "$PicturesFolder\wallpaper.jpg"
$WallpaperTxtPath = "$PicturesFolder\wallpaper.txt"
$WallpaperStyle = 4
$TileWallpaper = 0
$Restart = $false
$ImagePath = ""
$RegistryRootKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\System"
$CurrentWallpaper = (Get-ItemProperty -Path $RegistryRootKey -Name Wallpaper).Wallpaper
$CurrentWallpaperStyle = (Get-ItemProperty -Path $RegistryRootKey -Name WallpaperStyle).WallpaperStyle
$CurrentTileWallpaper = (Get-ItemProperty -Path $RegistryRootKey -Name TileWallpaper).TileWallpaper
$ShouldChange = $CurrentWallpaper -ne $WallpaperPath -or `
$CurrentWallpaperStyle -ne $WallpaperStyle -or `
$CurrentTileWallpaper -ne $TileWallpaper
$IsAdmin = $null -ne ([Security.Principal.WindowsIdentity]::GetCurrent().Groups | Where-Object { $_.Value -eq 'S-1-5-32-544' })
$ErrorActionPreference = "Stop" # Fail on the first error
# Step 1: Determine the image path
if ($StaticBackgroundImagePath) {
$ImagePath = $StaticBackgroundImagePath
} else {
$LatestImage = Get-ChildItem -Path $BackgroundImagesPath -Filter *.jpg | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$ImagePath = $LatestImage.FullName
}
if (-not $ImagePath) {
Write-Output "No wallpaper found. Please check \$StaticBackgroundImagePath or \$BackgroundImagesPath."
timeout /t $CountDownBeforeContinueSeconds
return
}
# Step 1.1: Verify and copy the latest image to wallpaper.jpg if needed
if (-not (Test-Path $WallpaperTxtPath) -or (Get-Content $WallpaperTxtPath -ErrorAction SilentlyContinue) -ne $ImagePath) {
Copy-Item -Path $ImagePath -Destination $WallpaperPath -Force
Set-Content -Path $WallpaperTxtPath -Value $ImagePath
Write-Output "Latest image copied to $WallpaperPath"
$Restart = $true
}
# Step 1.2: Check if the registry is set to wallpaper.jpg (requires elevation if needed)
if (-not $IsAdmin -and $ShouldChange) {
Write-Output "Script requires elevation to update the registry. Restarting with elevated privileges..."
if ($EnablePauseBeforeAdminPrompt) { Pause }
Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"$PSCommandPath" -Verb RunAs
return
}
if ($ShouldChange) {
Set-ItemProperty -Path $RegistryRootKey -Name Wallpaper -Value $WallpaperPath -ErrorAction SilentlyContinue
Set-ItemProperty -Path $RegistryRootKey -Name WallpaperStyle -Value 4
Set-ItemProperty -Path $RegistryRootKey -Name TileWallpaper -Value 0
Write-Output "Registry updated to use $WallpaperPath as the wallpaper."
$Restart = $true
}
# Step 2: Reload the wallpaper
if (-not $Restart) {
Write-Output "No changes needed; wallpaper is already set correctly."
timeout /t $CountDownBeforeContinueSeconds
return
}
try {
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class Wallpaper {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SystemParametersInfo(uint uiAction, uint uiParam, string pvParam, uint fWinIni);
}
"@
}
catch {
# only on the first time the script runs
# the type can be added
}
$SPI_SETDESKWALLPAPER = 0x0014
$SPIF_UPDATEINIFILE = 0x01
$SPIF_SENDCHANGE = 0x02
$Result = [Wallpaper]::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $null, $SPIF_UPDATEINIFILE -bor $SPIF_SENDCHANGE)
Write-Output "Wallpaper changed."
timeout /t $CountDownBeforeContinueSeconds
Automate change on logon
I've saved the script to my Pictures folder and named it ChangeWallpaper.ps1. We can trigger the script when we logon so that our wallpaper is synced.
Paste this script in an elevated terminal, and it will create the task for you:
& {
$TaskName = "ChangeWallpaperOnLogin"
$PicturesFolder = [Environment]::GetFolderPath('MyPictures')
$ScriptPath = "$PicturesFolder\ChangeWallpaper.ps1" # Path to the wallpaper-changing script
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$ErrorActionPreference = "Stop" # Ensure the script stops on errors
# Ensure the script is running as administrator
$isAdmin = [Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544'
if (-not $isAdmin) {
Write-Output "This script must be run as an administrator. Please restart it with administrative privileges."
exit 1
}
# Check if the task already exists
if (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue) {
Write-Output "Task '$TaskName' already exists. Deleting it now..."
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
Write-Output "Task '$TaskName' has been deleted."
}
# Define the task action
$Action = New-ScheduledTaskAction `
-Execute 'PowerShell.exe' `
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$ScriptPath`""
# Create a logon trigger for the current user
$Trigger = New-ScheduledTaskTrigger -AtLogOn -User $CurrentUser
# allow the task to run on batteries
$Settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries
# Register the task without a delay
Register-ScheduledTask -TaskName $TaskName `
-Action $Action `
-Trigger $Trigger `
-Description "Runs the ChangeWallpaper script at user logon." `
-Settings $Settings
# Add a 30-second delay to the task using XML configuration
$Task = Get-ScheduledTask -TaskName $TaskName
$Task.Settings.StartWhenAvailable = $true
$Task.Triggers[0].Delay = 'PT30S' # 30-second delay in ISO 8601 duration format
$Task | Set-ScheduledTask
Write-Output ""
Write-Output "Scheduled task '$TaskName' has been created successfully with a 30-second delay and is allowed to run on battery power."
}
🍻 Enjoy your new wallpaper!
The code is on GitHub, so check it out: code gallery / 8. bypass company wallpaper.
Changelog
- Added tests for wallpaper style and tile. When those settings are different, the script will correct them.
- Wallpaper style is now set to
4. - Make sure the task can run when the laptop is on batteries.
- Added a delay to the task script. It will wait for 30 seconds after logon before execution.
- The wallpaper is now copied to a secondary file to prevent needless script elevation. I removed the explorer restart (with some Windows native magic). There is now a task available to automate it all. Also added a timeout.
- Initial article.