Pinning Apps to the Windows 11 Taskbar with PowerShell

Marc-André Moreau published on
3 min, 503 words

Pinning apps to the taskbar in Windows 11 is surprisingly tricky to automate. Microsoft's official solution is to provision a LayoutModification.xml file, which is only applied during user profile initialization. If your user profile is already initialized, or if you just want to test layout modifications for your current user: well, tough luck.

In a first step, we'll generate a working LayoutModification.xml file using PowerShell and a list of application shortcuts. In a second step, we'll see how to force Windows to re-initialize the layout modifications for an existing user. It's not elegant, but it makes repeated testing much easier, and it's good enough for automation.

Generating LayoutModification.xml with PowerShell

The default layout modification file is located in C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml and automatically copied to %AppData%\Microsoft\Windows\Shell\LayoutModification.xml upon user profile initialization.

Here's a sample layout modification file using only a list of shortcuts to existing applications:

<LayoutModificationTemplate
    xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
    xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
    xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
    Version="1">
  <CustomTaskbarLayoutCollection PinListPlacement="Replace">
    <defaultlayout:TaskbarLayout>
      <taskbar:TaskbarPinList>
        <taskbar:DesktopApp DesktopApplicationLinkPath="%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk" />
        <taskbar:DesktopApp DesktopApplicationLinkPath="%APPDATA%\Microsoft\Windows\Start Menu\Programs\File Explorer.lnk" />
        <taskbar:DesktopApp DesktopApplicationLinkPath="%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Notepad.lnk" />
        <taskbar:DesktopApp DesktopApplicationLinkPath="%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Calculator.lnk" />
      </taskbar:TaskbarPinList>
    </defaultlayout:TaskbarLayout>
  </CustomTaskbarLayoutCollection>
</LayoutModificationTemplate>

The full XML schema supports more than shortcut files (.lnk), but I won't be covering them to keep things simple. Instead of manually editing the XML, let's use a PowerShell cmdlet to generate it from a list of shortcut files:

function New-LayoutModificationXml {
    [CmdletBinding()]
    param (
        [string[]] $LnkPaths,
        [string] $OutputPath = "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml",
        [ValidateSet("Append", "Replace")]
        [string] $PinListPlacement = "Replace"
    )

    $xml = New-Object System.Xml.XmlDocument
    $root = $xml.CreateElement("LayoutModificationTemplate", "http://schemas.microsoft.com/Start/2014/LayoutModification")
    $xml.AppendChild($root) | Out-Null
    $root.SetAttribute("xmlns:defaultlayout", "http://schemas.microsoft.com/Start/2014/FullDefaultLayout")
    $root.SetAttribute("xmlns:taskbar", "http://schemas.microsoft.com/Start/2014/TaskbarLayout")
    $root.SetAttribute("Version", "1")
    $collection = $xml.CreateElement("CustomTaskbarLayoutCollection", $root.NamespaceURI)
    $collection.SetAttribute("PinListPlacement", $PinListPlacement)
    $root.AppendChild($collection) | Out-Null
    $layout = $xml.CreateElement("defaultlayout:TaskbarLayout", $root.GetAttribute("xmlns:defaultlayout"))
    $collection.AppendChild($layout) | Out-Null
    $pinList = $xml.CreateElement("taskbar:TaskbarPinList", $root.GetAttribute("xmlns:taskbar"))
    $layout.AppendChild($pinList) | Out-Null
    foreach ($lnk in $LnkPaths) {
        $desktopApp = $xml.CreateElement("taskbar:DesktopApp", $root.GetAttribute("xmlns:taskbar"))
        $desktopApp.SetAttribute("DesktopApplicationLinkPath", $lnk)
        $pinList.AppendChild($desktopApp) | Out-Null
    }
    $xml.Save($OutputPath)
}

Paste the New-LayoutModificationXml cmdlet into a PowerShell terminal, and then paste the following code snippet to generate a sample LayoutModification.xml file in the default location:

$LnkPaths = @(
    "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk"
    "%APPDATA%\Microsoft\Windows\Start Menu\Programs\File Explorer.lnk"
    "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Notepad.lnk"
    "%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Calculator.lnk"
)
New-LayoutModificationXml -LnkPaths $LnkPaths -PinListPlacement "Replace"

Inspect the resulting LayoutModification.xml file in Notepad:

notepad "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml"

Feel free to edit the XML manually, but I would recommend updating the original cmdlet to accommodate your needs. ChatGPT or Claude can handle this kind of task quite well.

Testing Taskbar Layout Modifications

So, you now have a LayoutModification.xml file which looks good in theory, but you want to see the end result. Instead of using RDP to login interactively with newly created users, I offer you the much simpler Reset-TaskbarLayout PowerShell cmdlet instead:

function Reset-TaskbarLayout {
    Get-Process explorer | Stop-Process -Force
    Remove-Item "$Env:AppData\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\*" -Force -ErrorAction SilentlyContinue
    Remove-Item "$Env:AppData\Microsoft\Windows\Shell\*.dat" -Force -ErrorAction SilentlyContinue
    Remove-Item "$Env:AppData\Microsoft\Windows\Shell\LayoutModification.xml" -Force -ErrorAction SilentlyContinue
    Remove-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband" -Recurse -ErrorAction SilentlyContinue
    Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\TrayNotify" -Name "IconStreams" -ErrorAction SilentlyContinue
    Remove-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\TrayNotify" -Name "PastIconsStream" -ErrorAction SilentlyContinue
}

All you have to do is open a terminal, paste the cmdlet definition, then invoke Reset-TaskbarLayout after updating the LayoutModification.xml file. Here's what a successfully customized taskbar looks like:

customized pinned apps in taskbar

If you delete the files without deleting the registry keys, you can end up with blank icons:

blank pinned app icons in taskbar

If this happens, just right-click and unpin all the blank icons manually, then reset the taskbar layout again. While this approach has worked reliably in my testing, keep in mind that it's an unsupported workaround that could break in the future.

Conclusion

While taskbar pinning in Windows 11 was never meant to be scripted in this way, using PowerShell to generate LayoutModification.xml and forcing a taskbar layout reset gets the job done.

Is this officially supported? No.
Is it reliable for automation? Mostly.
Is it good enough? For many scenarios — yes.

If you've found other reliable tricks to simplify taskbar app pinning, feel free to share them with me!