Skip to content

Deployment At Scale

Rod Christiansen edited this page Sep 3, 2026 · 1 revision

Deployment At Scale

Everything TaskbarUtil does is per-user and local. There is no server, no agent, no scheduled task and no inventory. Deploying a taskbar layout across a fleet therefore means deciding two things: which file becomes the layout, and in whose session the tool runs. This page covers what an administrator actually runs, and when, grounded in what the tool supports.

The three delivery points

Point Reaches Runs as Shell restart
Image time / provisioning Every profile created afterwards SYSTEM or admin none needed
Login script The user logging in that user one, at logon
Ad-hoc / remediation The users currently signed in admin or SYSTEM one per session

A complete rollout usually needs the first two. The third is how you fix a machine that missed both.

Image time: seed the Default profile

The layout that new users get comes from C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml. Windows copies the Default profile when it creates a new one, so a layout staged there is inherited by every profile created from that moment on, with no login script, no policy key and no shell restart.

apply --allhomes writes that file as part of its work. On a reference image with a single build account, that is the whole job:

taskbarutil add "File Explorer"
taskbarutil add "Microsoft Edge"
taskbarutil add "Outlook"
taskbarutil add "Microsoft Teams"
taskbarutil apply --allhomes

Run elevated. --allhomes also copies the layout to C:\ProgramData\TaskbarUtil\LayoutModification.xml and points the build account's own policy at it, which is harmless on an image.

If you would rather not modify the build account at all, build the config, then place the XML yourself. taskbarutil show prints exactly what to copy:

taskbarutil show
New-Item -ItemType Directory -Force -Path "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell"
Copy-Item "$env:LOCALAPPDATA\TaskbarUtil\LayoutModification.xml" "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml"

The Default profile route reaches only profiles created after it. Existing profiles need one of the routes below.

Login: apply in the user's own session

For existing users, the layout has to be installed into their own HKCU or their own Shell directory, which means running as that user. A login script or a per-user scheduled task is the delivery vehicle.

The safest login-time shape reuses the machine-wide layout rather than rebuilding it in each session, and does the least possible work when nothing has changed. TaskbarUtil has no built-in idempotency check, so write one:

$shared  = 'C:\ProgramData\TaskbarUtil\LayoutModification.xml'
$config  = Join-Path $env:LOCALAPPDATA 'TaskbarUtil\LayoutModification.xml'
$applied = (Get-ItemProperty 'HKCU:\Software\Policies\Microsoft\Windows\Explorer' -Name LockedStartLayout -ErrorAction SilentlyContinue).LockedStartLayout

if ((Test-Path $shared) -and (-not (Test-Path $config) -or (Get-FileHash $shared).Hash -ne (Get-FileHash $config).Hash -or $applied -ne 1)) {
    New-Item -ItemType Directory -Force -Path (Split-Path $config) | Out-Null
    Copy-Item $shared $config -Force
    & taskbarutil apply
}

Without a guard like that, every logon tears down and rebuilds the shell — which is visible, slow, and takes the user's own pins with it each time.

The PinGeneration note matters here: the layout the tool writes by default does not let users keep their own pins, and re-applying at every logon means anything they pin is gone by the next one. If that is not the intent, either apply once and record it, or hand-edit PinGeneration="1" into the XML — see Layout XML Reference.

Building the layout in the login script instead

The examples/ scripts in the repository take the simpler approach of constructing the layout from scratch in each session:

taskbarutil reset --no-restart
taskbarutil add "File Explorer"
taskbarutil add "Google Chrome"
taskbarutil add "Windows Terminal"
taskbarutil add "Visual Studio Code"
taskbarutil apply

This is readable and self-contained, and it is fine for a lab or a small fleet. Two costs to know about. Each add that is not a known-app entry scans both Start Menu trees and opens shortcuts through COM, so a layout of a dozen third-party apps is not instant. And reset --no-restart plus apply means the shell is rebuilt on every logon.

Running at logon without a visible console

A login script that pops a console window on a shared or lab machine is its own problem. Deliver the script through whatever mechanism you already use for per-user logon work (Group Policy logon scripts, a per-user scheduled task with a hidden window, or your management agent's user-context runner) rather than a Run key. TaskbarUtil itself does nothing to hide a console — it is an ordinary console application.

Ad-hoc: remediate signed-in sessions

apply --allhomes run elevated on a live machine writes the policy into every profile whose hive is currently loaded in HKEY_USERS. In practice that is the signed-in users and no one else. It reports the count:

taskbarutil apply --allhomes

Read that count. "Policy set for 1 user profile(s)" on a machine with 40 profiles is not a failure — it is --allhomes telling you the other 39 hives were not loaded.

Note also what --allhomes does per profile: it writes StartLayoutFile and LockedStartLayout into HKEY_USERS\<SID>, deletes that profile's start2.bin, and deletes that profile's Taskband subtree. It does not restart the shell for other users' sessions, so their taskbars converge on the layout when their shell next starts. The Explorer restart that apply performs happens only in the session running the command.

Choosing a pattern

Situation Pattern
New image, one standard layout Seed the Default profile at image time
Existing fleet, one standard layout Seed the Default profile and run a guarded login script for existing users
Different layouts per machine role Stage a different C:\ProgramData\TaskbarUtil\LayoutModification.xml per role, with the same login script everywhere
Kiosk or lab where the layout must not drift Re-apply on every logon and accept the shell restart; do not set PinGeneration
Users may customise, you set the starting point Seed the Default profile only; never apply at logon

The last two rows are the real decision. Once you understand that Replace plus a Taskband wipe means an apply is destructive to user customisation, the choice of when to apply becomes the whole deployment design.

Packaging the tool itself

Install taskbarutil.exe machine-wide before anything needs it. The MSI puts it in C:\Program Files\sbin and adds that to the machine PATH; that directory is where the login script should expect to find it. Because PATH is set at machine scope in postinstall, a session that started before installation will not see it — call the full path in scripts that might run early:

& 'C:\Program Files\sbin\taskbarutil.exe' apply

Released binaries are unsigned. Sign them with your own certificate before fleet distribution — see Installation.

Verifying a rollout

There is no reporting built in, so verification is reading state. On a single machine:

taskbarutil list

Across a fleet, the two facts worth collecting are whether the policy is set and whether the layout matches what you shipped:

Get-ItemProperty 'HKCU:\Software\Policies\Microsoft\Windows\Explorer' -Name StartLayoutFile,LockedStartLayout -ErrorAction SilentlyContinue
(Get-FileHash 'C:\ProgramData\TaskbarUtil\LayoutModification.xml').Hash

The file log is the per-machine audit trail. Every apply, reset, pin and unpin lands in C:\ProgramData\ManagedUtilities\logs\taskbarutil.log with its outcome, which is the right thing to collect when a rollout looks partial:

Get-Content 'C:\ProgramData\ManagedUtilities\logs\taskbarutil.log' -Tail 40

Note that an unelevated user's run may have logged to %LOCALAPPDATA%\TaskbarUtil\logs\taskbarutil.log instead. Check both.

Rollback

reset is per-user and has no --allhomes. Rolling back a fleet means either running reset in each user's session — the same delivery problem as applying — or removing the policy values directly and letting Explorer rebuild.

taskbarutil reset

To stop new profiles inheriting the layout, delete the Default-profile copy:

Remove-Item 'C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml' -Force

Removing C:\ProgramData\TaskbarUtil\LayoutModification.xml while profiles still point their StartLayoutFile at it leaves those users with a policy pointing at a missing file. Clear the policy values first, then the shared file.

Clone this wiki locally