The obvious thing about this article is that it seems oddly specific. It absolutely is! Awhile back, I wrote a nice series about Kiosks. A major part of things like Assigned Access in more advanced use cases is customizing the start menu/taskbar. I received a ton of feedback/questions on getting the taskbar to work as expected. Today, we’re going to discuss what happens when you need to make changes/undo things in the taskbar. What we will cover is:
Deploying the Taskbar Layout with Microsoft Intune
This section is a bit of a refresher from my previous article, but it’s useful to cover it. When we want to deploy a taskbar layout, we create a “Custom” policy with the OMA-URI of: ./Device/Vendor/MSFT/Policy/Config/Start/StartLayout and deploy the XML file.
Building that XML is pretty easy, but let’s cover it modularly. The base, which is always the same is:
<?xml version="1.0" encoding="utf-8"?>
<LayoutModificationTemplate
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
Version="1">
<CustomTaskbarLayoutCollection>
<defaultlayout:TaskbarLayout>
<taskbar:TaskbarPinList>
We have basically two ways of working with pinned apps: “Add” or “Replace” That means, it will either “add” additional apps to the taskbar or “remove” all existing apps and only add the ones you have deployed to the policy.
Before we get started, let’s see how we find the AppIDs we need. You can run “Get-StartApps” as seen below:

The syntax of adding a specific app looks like this below:
##A full blow application##
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer"/>
##A UWA application##
<taskbar:UWA AppUserModelID="windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel" />
An example of “Add” can be seen below in its full form. You will note that there’s two different contexts for either Store Apps aka UWA or a regular desktop application. Interestingly enough, you don’t need to add anything as its the default behavior.
<?xml version="1.0" encoding="utf-8"?>
<LayoutModificationTemplate
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
Version="1">
<CustomTaskbarLayoutCollection>
<defaultlayout:TaskbarLayout>
<taskbar:TaskbarPinList>
<taskbar:UWA AppUserModelID="windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel" />
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer"/>
<taskbar:UWA AppUserModelID="Microsoft.MicrosoftLoop_8wekyb3d8bbwe!App" />
<taskbar:UWA AppUserModelID="MicrosoftCorporationII.QuickAssist_8wekyb3d8bbwe!App" />
</taskbar:TaskbarPinList>
</defaultlayout:TaskbarLayout>
</CustomTaskbarLayoutCollection>
</LayoutModificationTemplate>
Your replace would look like this below (you literally add PinListPlacement=”Replace”)
<?xml version="1.0" encoding="utf-8"?>
<LayoutModificationTemplate
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
Version="1">
<CustomTaskbarLayoutCollection PinListPlacement="Replace">
<defaultlayout:TaskbarLayout>
<taskbar:TaskbarPinList>
<taskbar:UWA AppUserModelID="windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel" />
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer"/>
<taskbar:UWA AppUserModelID="Microsoft.MicrosoftLoop_8wekyb3d8bbwe!App" />
<taskbar:UWA AppUserModelID="MicrosoftCorporationII.QuickAssist_8wekyb3d8bbwe!App" />
</taskbar:TaskbarPinList>
</defaultlayout:TaskbarLayout>
</CustomTaskbarLayoutCollection>
</LayoutModificationTemplate>
Overall, as long as it’s in Get-StartApps it should work with the XML. I’ve found through testing/validation that some custom apps may not work. My overall success has been about 90-95% with that being said.
Using Intune Remediations to Properly Undo Taskbar Layout
A common issue in the Intune world is called “tattooing” where configuration settings/registry keys don’t get removed when a device is removed from a policy.
Let’s consider this real world example:
“Consultant deploys a Taskbar Layout at the customers request that pins all of the office apps for all devices”
Users start complaining that when they unpin apps that they don’t stay unpinned for long. Logic dictates that you build an Entra group to exclude users out of the Taskbar Layout policy. So you do that, and people are still having issues.
The reason that happens is from tattooing. You deployed that exclusion but the registry key was never cleaned up thus the layout is still being enforced.
If you go to this location: Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PolicyManager\current\device\Start you will find some items in there, specifically a REG_SZ with the XML that you deployed originally:
If you fully unassign the entire policy, it should look like this ideally:
Often, when you exclude out a group of users in a policy it doesn’t cleanup like it’s supposed to. In these scenarios, you can also leverage an Intune remediation like this below:
The Detect Script:
# Define the registry path and value name
$regPath = "HKLM:SOFTWAREMicrosoftPolicyManagercurrentdeviceStart"
$valueName = "StartLayout_ProviderSet"
# Check if the registry key and value exist
if (Test-Path $regPath) {
$value = Get-ItemProperty -Path $regPath -Name $valueName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $valueName -ErrorAction SilentlyContinue
if ($value -eq 1) {
Exit 1
} else {
Exit 0
}
} else {
Exit 0
}
The Remediation Script:
# Define the registry path
$regKey = "HKLM:SOFTWAREMicrosoftPolicyManagercurrentdeviceStart"
# Check if the key exists
if (Test-Path $regKey) {
try {
Remove-Item -Path $regKey -Recurse -Force
Write-Output "Registry key deleted successfully: $regKey"
}
catch {
Write-Error "Failed to delete registry key: $_"
}
} else {
Write-Output "Registry key does not exist: $regKey"
}
This remediation script will cleanup the registry keys that should have been deleted from the exclusion, but you want to make sure you use the same group you used to exclude the policy with the remediation so it works accordingly.
New Capabilities to Let Users Unpin Apps
The last part of this article is dedicated to a fun new feature. As a disclaimer, it is only “officially” supported in Insider Builds right now from Windows Insider 22635.5305 (Beta Channel)+, but it works in a “limited fashion today”
The new feature called “PinGeneration” will let administrators to allow certain apps to be unpinned by users. Today, when you unpin an application, it will get repinned at the next policy update cycle. People have been complaining about it for a long time so it makes sense that they are finally going to address that issue. Let’s look at an example of how it works:
<?xml version="1.0" encoding="utf-8"?>
<LayoutModificationTemplate
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
Version="1">
<CustomTaskbarLayoutCollection PinListPlacement="Replace">
<defaultlayout:TaskbarLayout>
<taskbar:TaskbarPinList>
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Windows.Explorer"/>
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Office.WINWORD.EXE.15" PinGeneration="1" />
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Office.EXCEL.EXE.15" PinGeneration="2" />
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Office.POWERPNT.EXE.15" PinGeneration="3" />
<taskbar:DesktopApp DesktopApplicationID="Microsoft.Office.OUTLOOK.EXE.15" />
<taskbar:UWA AppUserModelID="MSTeams_8wekyb3d8bbwe!MSTeams" />
</taskbar:TaskbarPinList>
</defaultlayout:TaskbarLayout>
</CustomTaskbarLayoutCollection>
</LayoutModificationTemplate>
The logic of PinGeneration is weird, so this is how it works:
-
- Without PinGeneration, apps get repinned during the next update cycle like I said previously.
-
- When you specify it for an application, you can unpin it.
-
- Every number is specific to an app itself and the number doesn’t actually matter.
-
- When you change the number, it gets repinned.
-
- When you remove the attribute, it will repin.
Now, we fast forward to today. From testing in Windows 24H2, it appears to work in a limited fashion. When you set PinGeneration on ANY app, it basically enforces it for ALL apps. So, in “theory” you “can” use it if the goal is to allow apps to be unpinned, but it’s all or nothing today.
Once it goes live, it should give you more granular control over pinning, but today it’s limited.
