01 July 2016

PowerShell: Pin and Unpin Applications to the Start Menu and Taskbar

I am in the middle of building a new Windows 10 image and testing out all of the GPOs and applications. One of the settings we do is to add apps to the taskbar and start menu. I had written a script a couple of years ago to do this, but it was in a rush when I had just started this position. With the help of using Sapien's PowerShell studio, this script was a breeze to write. I didn't have time to really put much thought into the script. This time around, I have much more time to write the scripts.

The first thing is the old script does not work with Windows 10. I started to do a little research and found this person's script. I liked it and ended up taking some references from it, but as you have probably seen in some of my other scripts, I also like verification and adding more features. The first of the features I added is being able to generate an official list of all applications on the machine that are listed within the specified GUID. The reason for this feature is that you will need to add that application exactly like it appears in the generated list, otherwise it will not pin or unpin it. I also put a feature to allow the generated list to be exported to a csv file.

The next feature is being able to add all of the apps you want to be pinned or unpinned to a text file. The script will read the text file and process each app. One thing you will see I did was to put a unpin first within the pin functions. I did this so if there is an app already pinned and a possible change was made to the app affecting the pinned shortcut, an updated one will appear. I also put examples at the bottom on how to hardcode apps directly into the script if you do not want to use a text file.

The final feature was to use boolean variables to reflect on success or failure of each processed step. This allows the script to exit out with an error code 1, thereby flagging it as failed if implemented in SCCM/MDT.

Here is a screenshot on how to populate a the text file. As you can see in the script, I hardcoded 'Applications.txt' as the name of the file to contain the list of applications. It must be in the same directory as the powershell script. You can override the hardcoded filename by using the -AppsFile parameter. To unpin apps, change the add to remove in the list below.


NOTE: It has come to my attention that on newer versions of Windows 10, the pin to taskbar verb has been removed. The version of Windows 10 I am using, which still works even with all update patches applied, is 1511 (10586.420).

You can download the script from here.



 <#  
      .SYNOPSIS  
           A brief description of the ApplicationShortcutsWindows10.ps1 file.  
        
      .DESCRIPTION  
           This script will add shortcuts to the taskbar.  
        
      .PARAMETER AppsFile  
           Name of the text file that contains a list of the applications to be added or removed  
        
      .PARAMETER ConsoleTitle  
           ConsoleTitle assigned to the PowerShell console  
        
      .PARAMETER OutputToTextFile  
           Select if output needs to go to a text file  
        
      .PARAMETER GetApplicationList  
           Get a list of applications with the specific name needed to use or pinning and unpinning  
        
      .EXAMPLE  
           Read apps from within a text file that resides in the same directory as this script  
                powershell.exe -executionpolicy bypass -file ApplicationShortcutsWin10.ps1 -AppsFile 'Applications.txt'  
   
           Get an official list of applications with the exact names that need to be used for pinning/unpinning  
                powershell.exe -executionpolicy bypass -file ApplicationShortcutsWin10.ps1 -GetApplicationList  
   
           Get an official list of applications with the exact names that need to be used for pinning/unpinning and write to the text file ApplicationList.csv residing in the same directory as this script  
                powershell.exe -executionpolicy bypass -file ApplicationShortcutsWin10.ps1 -GetApplicationList -OutputToTextFile  
   
           Near the bottom of the script are commented out lines that give examples of how to hardcode apps inside this script  
   
      .NOTES  
           ===========================================================================  
           Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.123  
           Created on:       6/29/2016 10:33 AM  
           Created by:       Mick Pletcher  
           Organization:  
           Filename:         ApplicationShortcutsWindows10.ps1  
           ===========================================================================  
 #>  
   
 [CmdletBinding()]  
 param  
 (  
           [string]$AppsFile = 'Applications.txt',  
           [ValidateNotNullOrEmpty()][string]$ConsoleTitle = 'Application Shortcuts',  
           [switch]$OutputToTextFile,  
           [switch]$GetApplicationList  
 )  
   
 function Add-AppToStartMenu {  
 <#  
      .SYNOPSIS  
           Pins an application to the start menu  
        
      .DESCRIPTION  
           Add an application to the start menu  
        
      .PARAMETER Application  
           Name of the application. This can be left blank and the function will use the file description metadata instead.  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([boolean])]  
      param  
      (  
                [Parameter(Mandatory = $true)][string]$Application  
      )  
        
      $Success = $true  
      $Status = Remove-AppFromStartMenu -Application $Application  
      If ($Status -eq $false) {  
           $Success = $false  
      }  
      Write-Host 'Pinning'$Application' to start menu.....' -NoNewline  
      ((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | Where-Object{ $_.Name -eq $Application }).verbs() | Where-Object{ $_.Name.replace('&', '') -match 'Pin to Start' } | ForEach-Object{ $_.DoIt() }  
      If ($? -eq $true) {  
           Write-Host 'Success' -ForegroundColor Yellow  
      } else {  
           Write-Host 'Failed' -ForegroundColor Red  
           $Success = $false  
      }  
      Return $Success  
 }  
   
 function Add-AppToTaskbar {  
 <#  
      .SYNOPSIS  
           Pins an application to the taskbar  
        
      .DESCRIPTION  
           Add an application to the taskbar  
        
      .PARAMETER Application  
           Name of the application. This can be left blank and the function will use the file description metadata instead.  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([boolean])]  
      param  
      (  
                [Parameter(Mandatory = $true)][string]$Application  
      )  
        
      $Success = $true  
      $Status = Remove-AppFromTaskbar -Application $Application  
      If ($Status -eq $false) {  
           $Success = $false  
      }  
      Write-Host 'Pinning'$Application' to start menu.....' -NoNewline  
      ((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | Where-Object{ $_.Name -eq $Application }).verbs() | Where-Object{ $_.Name.replace('&', '') -match 'Pin to taskbar' } | ForEach-Object{ $_.DoIt() }  
      If ($? -eq $true) {  
           Write-Host 'Success' -ForegroundColor Yellow  
      } else {  
           Write-Host 'Failed' -ForegroundColor Red  
           $Success = $false  
      }  
      Return $Success  
 }  
   
 function Get-ApplicationList {  
 <#  
      .SYNOPSIS  
           Get list of Applications  
        
      .DESCRIPTION  
           Get a list of available applications with the precise name to use when pinning or unpinning to the taskbar and/or start menu  
        
      .PARAMETER SaveOutput  
           Save output to a text file  
        
      .EXAMPLE  
           PS C:\> Get-ApplicationList  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param  
      (  
                [switch]$SaveOutput  
      )  
        
      $RelativePath = Get-RelativePath  
      $OutputFile = $RelativePath + "ApplicationList.csv"  
      $Applications = (New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items()  
      $Applications = $Applications | Sort-Object -Property name -Unique  
      If ($SaveOutput.IsPresent) {  
           If ((Test-Path -Path $OutputFile) -eq $true) {  
                Remove-Item -Path $OutputFile -Force  
           }  
           "Applications" | Out-File -FilePath $OutputFile -Encoding UTF8 -Force  
           $Applications.Name | Out-File -FilePath $OutputFile -Encoding UTF8 -Append -Force  
      }  
      $Applications.Name  
 }  
   
 function Get-Applications {  
 <#  
      .SYNOPSIS  
           Get Application List  
        
      .DESCRIPTION  
           Get the list of applications to add or remove  
        
      .EXAMPLE  
           PS C:\> Get-Applications  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([object])]  
      param ()  
        
      $RelativePath = Get-RelativePath  
      $File = $RelativePath + $AppsFile  
      $Contents = Get-Content -Path $File -Force  
      Return $Contents  
 }  
   
 function Get-RelativePath {  
 <#  
      .SYNOPSIS  
           Get the relative path  
        
      .DESCRIPTION  
           Returns the location of the currently running PowerShell script  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([string])]  
      param ()  
        
      $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
      Return $Path  
 }  
   
 function Invoke-PinActions {  
 <#  
      .SYNOPSIS  
           Process the application list  
        
      .DESCRIPTION  
           Add or remove applications within the text file to/from the taskbar and start menu.  
        
      .PARAMETER AppList  
           List of applications  
        
      .EXAMPLE  
           PS C:\> Invoke-PinActions -AppList 'Value1'  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([boolean])]  
      param  
      (  
                [Parameter(Mandatory = $false)][ValidateNotNullOrEmpty()][object]$AppList  
      )  
        
      $Success = $true  
      foreach ($App in $AppList) {  
           $Entry = $App.Split(',')  
           If ($Entry[1] -eq 'startmenu') {  
                If ($Entry[2] -eq 'add') {  
                     $Status = Add-AppToStartMenu -Application $Entry[0]  
                     If ($Status -eq $false) {  
                          $Success = $false  
                     }  
                } elseif ($Entry[2] -eq 'remove') {  
                     $Status = Remove-AppFromStartMenu -Application $Entry[0]  
                     If ($Status -eq $false) {  
                          $Success = $false  
                     }  
                } else {  
                     Write-Host $Entry[0]" was entered incorrectly"  
                }  
           } elseif ($Entry[1] -eq 'taskbar') {  
                If ($Entry[2] -eq 'add') {  
                     $Status = Add-AppToTaskbar -Application $Entry[0]  
                     If ($Status -eq $false) {  
                          $Success = $false  
                     }  
                } elseif ($Entry[2] -eq 'remove') {  
                     $Status = Remove-AppFromTaskbar -Application $Entry[0]  
                     If ($Status -eq $false) {  
                          $Success = $false  
                     }  
                } else {  
                     Write-Host $Entry[0]" was entered incorrectly"  
                }  
           }  
      }  
      Return $Success  
 }  
   
 function Remove-AppFromStartMenu {  
 <#  
      .SYNOPSIS  
           Remove the pinned application from the start menu  
        
      .DESCRIPTION  
           A detailed description of the Remove-AppFromStartMenu function.  
        
      .PARAMETER Application  
           Name of the application. This can be left blank and the function will use the file description metadata instead.  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([boolean])]  
      param  
      (  
                [Parameter(Mandatory = $true)][string]$Application  
      )  
        
      $Success = $true  
      Write-Host 'Unpinning'$Application' from start menu.....' -NoNewline  
      ((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | Where-Object{ $_.Name -eq $Application }).verbs() | Where-Object{ $_.Name.replace('&', '') -match 'Unpin from Start' } | ForEach-Object{ $_.DoIt() }  
      If ($? -eq $true) {  
           Write-Host 'Success' -ForegroundColor Yellow  
      } else {  
           Write-Host 'Failed' -ForegroundColor Red  
           $Success = $false  
      }  
      Return $Success  
 }  
   
 function Remove-AppFromTaskbar {  
 <#  
      .SYNOPSIS  
           Unpins an application to the taskbar  
        
      .DESCRIPTION  
           Remove the pinned application from the taskbar  
        
      .PARAMETER Application  
           Name of the application. This can be left blank and the function will use the file description metadata instead.  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([boolean])]  
      param  
      (  
                [Parameter(Mandatory = $true)][string]$Application  
      )  
        
      $Success = $true  
      Write-Host 'Unpinning'$Application' from task bar.....' -NoNewline  
      ((New-Object -Com Shell.Application).NameSpace('shell:::{4234d49b-0245-4df3-b780-3893943456e1}').Items() | Where-Object{ $_.Name -eq $Application }).verbs() | Where-Object{ $_.Name.replace('&', '') -match 'Unpin from taskbar' } | ForEach-Object{ $_.DoIt() }  
      If ($? -eq $true) {  
           Write-Host 'Success' -ForegroundColor Yellow  
      } else {  
           Write-Host 'Failed' -ForegroundColor Red  
           $Success = $false  
      }  
      Return $Success  
 }  
   
 function Set-ConsoleTitle {  
 <#  
      .SYNOPSIS  
           Console Title  
        
      .DESCRIPTION  
           Sets the title of the PowerShell Console  
        
      .PARAMETER Title  
           Title of the PowerShell Console  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param  
      (  
                [Parameter(Mandatory = $true)][String]$Title  
      )  
        
      $host.ui.RawUI.WindowTitle = $Title  
 }  
   
 Clear-Host  
 $Success = $true  
 Set-ConsoleTitle -Title $ConsoleTitle  
 If ($GetApplicationList.IsPresent) {  
      If ($OutputToTextFile.IsPresent) {  
           Get-ApplicationList -SaveOutput  
      } else {  
           Get-ApplicationList  
      }  
 }  
 If (($AppsFile -ne $null) -or ($AppsFile -ne "")) {  
      $ApplicationList = Get-Applications  
      $Success = Invoke-PinActions -AppList $ApplicationList  
 }  
   
 #Hardcoded applications  
 <#  
 $Success = Add-AppToStartMenu -Application 'Microsoft Outlook 2010'  
 $Success = Add-AppToTaskbar -Application 'Microsoft Outlook 2010'  
 #>  
   
 If ($Success -eq $false) {  
      Exit 1  
 }  
   

7 comments:

  1. There seems to be an issue with this approach as Pin to Taskbar isn't available in the Verb List? The only way I've found that works reliably is 'http://ccmexec.com/2015/12/customizing-the-taskbar-in-windows-10-during-osd/'

    ReplyDelete
    Replies
    1. The function/verb I am using to pin to the task bar is add-apptotaskbar.

      Delete
    2. I believe he means that the 'Pin to taskbar' verb referenced in add-apptotaskbar does not exist and hence, the function does not work. This is my experience as well. The other operations, add/remove from start and unpin from taskbar work just fine.
      Judging from some of the responses in this thread, it seems like it might be hit-and-miss depending on which exact build/update you are working on: https://connect.microsoft.com/PowerShell/feedback/details/1609288/pin-to-taskbar-no-longer-working-in-windows-10
      Can i ask which version of Windows 10 you have had this working on?

      Delete
    3. Gotcha. I am using version 1511 (10586.420)

      Delete
  2. Mick, how do you normally run this script? Are you configuring a RunOnce setting to run the script in the user context? I'm excited to test this out, but I wanted to figure out the best way to approach it. I didn't know if it was possible to use in the system context. Thanks!

    ReplyDelete
    Replies
    1. I am using SCCM to deploy it as a package to a user group collection. It probably could also be setup to execute using the runonce.

      Delete
  3. Hi need some help. I need to Pin a folder Shortcut to Start Menu in Windows 10. And Script needs to work for all users.

    ReplyDelete