How to bypass company wallpaper as local admin on Windows 11

How to bypass company wallpaper as local admin on Windows 11

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:

  1. The script lets you set a static background image or set a background images path that will search for the latest JPG image.
  2. 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.jpg and the path of the image to wallpaper.jpg.txt.
  3. If the registry points to wallpaper.jpg we only need to copy the new image to the file and restart the explorer.
  4. 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:

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

Source: keestalkstech-code-gallery/​08.bypass-company-wallpaper/​ChangeWallpaper.ps1

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."
}

Source: keestalkstech-code-gallery/​08.bypass-company-wallpaper/​SetupTask.ps1

🍻 Enjoy your new wallpaper!

The code is on GitHub, so check it out: code gallery / 8. bypass company wallpaper.

Changelog