Pinning apps to the taskbar in Windows 11
Using registry-based local group policy and an XML file to maintain pinned apps.
Published in Articles on by Michiel van Oosterhout ~ 5 min read
The Windows 11 taskbar, like the Start menu, may have apps pinned to it. The pinned apps apear to the right of the taskbar items (the icons that launch the Start menu, search, task view, widgets, and chat). A custom set of pinned apps on the taskbar can be maintained via a registry-based group policy for Windows Explorer.

Registry-based group policy
Group Policy for Windows is a way to manage Windows clients that are joined to an Active Directory domain, by distributing configuration from central location. But a stand-alone Windows computer can also be configured using local group policies.
Registry-based group policies are policies that, when applied, will result in changes to the Windows Registry database (see Implementing Registry-based Policy). Local registry-based group policies for the computer and for the local users are stored in 2 Registry.pol
files in the $Env:SystemRoot\System32\GroupPolicy\Machine
and User
directories respectively. These are binary files (see Registry Policy File Format), and Microsoft provides a tool (LGPO.exe) that can "import and apply policy settings". This tool can be downloaded as part of the Microsoft Security Compliance Toolkit 1.0 (includes documentation).
Even if we were to use this tool to set a local registry-based group policy and then apply it, it would require us to know the mapping of the registry-based group policy to the actual registry keys and values. We might as well use Windows PowerShell to create or update the registry. The only downside is that changing the registry keys used by registry-based policies (e.g. HKCU:\Software\Policies
) requires an elevated Windows PowerShell session.
Start Layout policy
The Start Layout policy can be used to modify the taskbar's pinned apps. The policy has 2 parts, Start Layout File (a text box to specify the path to an XML file) and Reapply layout at every logon (a check box). The policy itself and the parts map to the following registry values under HCKU:Software\Policies
:
Microsoft\Windows\Explorer,LockedStartLayout,REG_DWORD
Microsoft\Windows\Explorer,StartLayoutFile,REG_EXPAND_SZ
Microsoft\Windows\Explorer,ReapplyStartLayoutEveryLogon,REG_DWORD
The schema of the XML file is described in Customize the Taskbar on Windows 11 (for group policy) and Customize the Taskbar (for OEMs).
Script
The Windows PowerShell script below will create values in the registry in a way that is similar to applying the Start Layout policy. This script must be run from an elevated Windows PowerShell session. After running this script you have to sign out and sign back in for Windows Explorer to check the registry.
# Ensure the directory for to store the layout file exists
$path = "$Env:LOCALAPPDATA\Microsoft\Windows\Shell"
$directory = New-Item -Force -ItemType Directory -Path $path
# XML namespace URIs
$rootNamespaceUri = "http://schemas.microsoft.com/Start/2014/LayoutModification"
$defaultlayoutNamespaceUri = "http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
$taskbarNamespaceUri = "http://schemas.microsoft.com/Start/2014/TaskbarLayout"
# The path to the Start Layout File
$fileName = "LayoutModification.xml"
$filePath = Join-Path $path $fileName
# The XML document
$document = [xml]::new()
if (-not (Test-Path $filePath))
{
$declaration = $document.CreateXmlDeclaration("1.0", "utf-8", $null)
$document.AppendChild($declaration) | Out-Null
$root = $document.CreateElement("LayoutModificationTemplate", $rootNamespaceUri)
$root.SetAttribute("xmlns:defaultlayout", $defaultlayoutNamespaceUri)
$root.SetAttribute("xmlns:taskbar", $taskbarNamespaceUri)
$root.SetAttribute("Version", "1")
$document.AppendChild($root) | Out-Null
$customTaskbarLayoutCollectionn = $document.CreateElement("CustomTaskbarLayoutCollection", $rootNamespaceUri)
$root.AppendChild($customTaskbarLayoutCollectionn) | Out-Null
$taskbarLayout = $document.CreateElement("defaultlayout", "TaskbarLayout", $defaultlayoutNamespaceUri)
$customTaskbarLayoutCollectionn.AppendChild($taskbarLayout) | Out-Null
$taskbarPinList = $document.CreateElement("taskbar", "TaskbarPinList", $taskbarNamespaceUri)
$taskbarLayout.AppendChild($taskbarPinList) | Out-Null
Write-Host "Creating initial taskbar layout modification file..." -NoNewline
$document.Save($filePath) | Out-Null
Write-Host " OK"
}
else
{
Write-Host "Loading initial taskbar layout modification file..." -NoNewline
$document.Load($filePath)
Write-Host " OK"
$taskbarPinList = $document.GetElementsByTagName("TaskbarPinList", $taskbarNamespaceUri)[0]
}
# List of apps to pin
$pinnedApps = @(
@{ Type = "DesktopApp"; Id = "Microsoft.Windows.Explorer"; DisplayName = "File Explorer" }
@{ Type = "UniversalWindowsApp"; Id = "Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge"; DisplayName = "Edge" }
@{ Type = "UniversalWindowsApp"; Id = "Microsoft.WindowsTerminal_8wekyb3d8bbwe!App"; DisplayName = "Windows Terminal" }
)
# Pin each app by adding the pinned app's type-specific XML element to the taskbar pin list element
$pinnedApps | ForEach-Object {
$displayName = $_.DisplayName
$id = $_.Id
switch ($_.Type)
{
"DesktopApp" {
$taskbarPin = $document.CreateElement("taskbar", "DesktopApp", $taskbarNamespaceUri)
$taskbarPin.SetAttribute("DesktopApplicationID", $id)
}
"Link" {
$taskbarPin = $document.CreateElement("taskbar", "DesktopApp", $taskbarNamespaceUri)
$taskbarPin.SetAttribute("DesktopApplicationLinkPath", $id)
}
"UniversalWindowsApp" {
$taskbarPin = $document.CreateElement("taskbar", "UWA", $taskbarNamespaceUri)
$taskbarPin.SetAttribute("AppUserModelID", $id)
}
}
Write-Host "Pinning [$displayName]..." -NoNewline
$taskbarPinList.AppendChild($taskbarPin) | Out-Null
Write-Host " OK"
}
# Save the Start Layout File
$document.Save($filePath)
# Create the registry-based policy's registry values
$policyPath = "HKCU:\Software\Policies\Microsoft\Windows\Explorer"
if (-not (Test-Path -Path $policyPath))
{
# Ensuring the registry key exists
$key = New-Item -Force -ItemType Directory -Path $policyPath
}
# Force the (re)creation of the registry values
$value = New-ItemProperty -Force -Name "LockedStartLayout" -Path $policyPath -PropertyType "DWord" -Value 1
$value = New-ItemProperty -Force -Name "StartLayoutFile" -Path $policyPath -PropertyType "ExpandString" -Value $filePath
$value = New-ItemProperty -Force -Name "ReapplyStartLayoutEveryLogon" -Path $policyPath -PropertyType "DWord" -Value 1
The script uses the types in the System.Xml
namespace to create the Start Layout File if the file does not exist yet. Apps are then pinned by adding <taskbar:DesktopApp>
and <taskbar:UWA>
child nodes to the <taskbar:TaskbarPinList>
element. This technique lends itself to pinning newly installed apps in the future.
Summary
Apps can be pinned to the taskbar via a registry-based local group policy that references an XML file. By adding the registry keys directly we can get the same result we'd get from manually applying the local group policy. The XML file can be updated later when you want to pin newly installed apps.