2017-05-12

Cleanup All users desktop and Start menu

Problem to solve

Clean all shortcuts on the all users desktop (because users cannot delete them) and also remove unwanted shortcuts from the Start menu such as "Uninstall"-shortcuts.

I run this on our student computers in computer rooms where the Onedrive client cannot be used, instead we mount Onedrive as a mapped folder instead using OneDriveMapper.
We also want to remove unnecessary shortcuts for Java & Silverlight.

Description of the script

The script is based on several functions that could be used to remove files and folers other than the desktop and start menu shortcuts.

Remove-UnwantedFolders - Remove folders in a path
Remove-UnwantedFiles - Remove files in a path
Remove-UninstallLinks - Searches for and removes any shortcuts that begins with "Uninstall" in a path
Remove-EmptyFolders - Removes any empty folders in a path
Remove-FiletypeFromFolder - Removes all files of defined type from a path

Read the script comments for more details.
Change the files/folders/file types in the end of the script to adapt to your needs.

The script

=== SCRIPT Cleanup-Desktop_and_StartMenu.ps1 BEGINS ===
# David Djerf 2017-05-11

Function Remove-UnwantedFolders {
    <#
    .SYNOPSIS
        Removes unwanted folders in a path
    .DESCRIPTION
        Will remove one or more folders from a path, for example use this to remove unwanted folders in the start menu.
    .NOTES
   davpe67 2017-02-18
    .PARAMETER WorkFolder
   Define a path where script should seach recursive for the folders to remove
   For example: "C:\" or "$ENV:Temp"
    .PARAMETER SearchStartMenu
        Will Set Workfolder to "$ENV:Programdata\Microsoft\Windows\Start Menu"
    .PARAMETER UnwantedFolders
        Specify one or more folders you want to remove.
        For example: "Java","Silverlight"
    .EXAMPLE
   Remove-UnwantedFolders -SearchStartMenu -UnwantedFolders "Java","Unique Admin Generator" -WhatIf
    #>

    [cmdletbinding(SupportsShouldProcess=$True,DefaultParameterSetName="WorkFolder")]
    Param(
        [Parameter(Mandatory=$True, ParameterSetName="WorkFolder", Position=0, HelpMessage='Specify folder(s) serch in: ["C:\path\to\folders"]')]
        [string]$WorkFolder,
        [Parameter(Mandatory=$True, ParameterSetName="SearchStartMenu", Position=0)]
        [switch]$SearchStartMenu,
        [Parameter(Mandatory=$True, HelpMessage='Specify folder(s) to remove: ["Folder1","Folder"]')]
        [string[]]$UnwantedFolders
    )
    BEGIN { 
        Write-Verbose "Begin..."       
        IF ($SearchStartMenu) {
$IsAdmin=Test-Admin
Write-Verbose "`$IsAdmin is $IsAdmin"
IF ($IsAdmin -like $false) { Write-Error "Script need to run as administrator" ; Break }
            $WorkFolder = Resolve-Path $("$ENV:ProgramData\Microsoft\Windows\Start Menu") 
        } ELSE {
            $WorkFolder = Resolve-Path "$WorkFolder"
        } # End of IF ELSE
        Write-Verbose "`$WorkFolder=`"$WorkFolder`""
        Write-Verbose "`$UnwantedFolders=`"$UnwantedFolders`""
    } # End of Begin
    PROCESS {
        # Remove Unwanted Folders in Start Menu
        IF ($UnwantedFolders -and $WorkFolder) { # Only run if not empty
            Write-Verbose "Processing..."
            IF ($UnwantedFolders) {
                ForEach ($UnwantedFolder in $UnwantedFolders) {
                     Get-ChildItem -Recurse -Path $WorkFolder -Directory | Where-Object {$_.Name -like "$UnwantedFolder" } | Resolve-Path | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
                } # End of ForEach
            } # End of IF
        } # End of IF
    } # End of Process

} # End of function Remove-UnwantedFolders

Function Remove-UnwantedFiles {
    <#
    .SYNOPSIS
        Removes unwanted files in a path
    .DESCRIPTION
        Will remove one or more files from a path, for example use this to remove unwanted files in the start menu.
    .NOTES
   davpe67 2017-03-03
    .PARAMETER WorkFolder
   Define a path where script should seach recursive for the folders to remove
   For example: "C:\" or "$ENV:Temp"
    .PARAMETER SearchStartMenu
        Will Set Workfolder to "$ENV:Programdata\Microsoft\Windows\Start Menu"
    .PARAMETER UnwantedFiles
        Specify one or more folders you want to remove.
        For example: "Onedrive","Onedrive for Business"
    .EXAMPLE
   Remove-UnwantedFiles -SearchStartMenu -UnwantedFiles "Onedrive","Onedrive for Business" -WhatIf
    #>

    [cmdletbinding(SupportsShouldProcess=$True,DefaultParameterSetName="WorkFolder")]
    Param(
        [Parameter(Mandatory=$True, ParameterSetName="WorkFolder", Position=0, HelpMessage='Specify folder(s) serch in: ["C:\path\to\folders"]')]
        [string]$WorkFolder,
        [Parameter(Mandatory=$True, ParameterSetName="SearchStartMenu", Position=0)]
        [switch]$SearchStartMenu,
        [Parameter(Mandatory=$True, HelpMessage='Specify files(s) to remove: ["File1","File"]')]
        [string[]]$UnwantedFiles
    )
    BEGIN { 
        Write-Verbose "Begin..."       
        IF ($SearchStartMenu) {
$IsAdmin=Test-Admin
Write-Verbose "`$IsAdmin is $IsAdmin"
IF ($IsAdmin -like $false) { Write-Error "Script need to run as administrator" ; Break }
            $WorkFolder = Resolve-Path $("$ENV:ProgramData\Microsoft\Windows\Start Menu") 
        } ELSE {
            $WorkFolder = Resolve-Path "$WorkFolder"
        } # End of IF ELSE
        Write-Verbose "`$WorkFolder=`"$WorkFolder`""
        Write-Verbose "`$UnwantedFiles=`"$UnwantedFiles`""
    } # End of Begin
    PROCESS {
        # Remove Unwanted Folders in Start Menu
        IF ($UnwantedFiles -and $WorkFolder) { # Only run if not empty
            Write-Verbose "Processing..."
            IF ($UnwantedFiles) {
                ForEach ($UnwantedFile in $UnwantedFiles) {
                     Get-ChildItem -Recurse -Path $WorkFolder -File | Where-Object {$_.Name -like "$UnwantedFile" } | Resolve-Path | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
                } # End of ForEach
            } # End of IF
        } # End of IF
    } # End of Process

} # End of function Remove-UnwantedFiles


function Test-Admin {
<#
.SYNOPSIS
Test if script is run as administrator
.DESCRIPTION
Use to check if script is run with administrator privilegues or not.
Will return true or false.
.NOTES
Downloaded from: http://www.powertheshell.com/testadmin/
.EXAMPLE
Test-Admin
#>
  $wid = [System.Security.Principal.WindowsIdentity]::GetCurrent()
  $prp = New-Object System.Security.Principal.WindowsPrincipal($wid)
  $adm = [System.Security.Principal.WindowsBuiltInRole]::Administrator
  $prp.IsInRole($adm)  
}

Function Remove-UninstallLinks {
    <#
    .SYNOPSIS
        Removes unwanted Uninstall links in a path
    .DESCRIPTION
        Will remove links that begin with "uninstall" from a path, 
        for example use this to remove unwanted uninstall-links in the start menu.
    .NOTES
   davpe67 2017-02-18
    .PARAMETER WorkFolder
   Define a path where script should seach recursive for the folders to remove
   For example: "C:\" or "$ENV:Temp"
    .PARAMETER SearchStartMenu
        Will Set Workfolder to "$ENV:Programdata\Microsoft\Windows\Start Menu"
    .EXAMPLE
   Remove-UnwantedFolders -SearchStartMenu -WhatIf
    #>

    [cmdletbinding(SupportsShouldProcess=$True,DefaultParameterSetName="WorkFolder")]
    Param(
        [Parameter(Mandatory=$True, ParameterSetName="WorkFolder", Position=0, HelpMessage='Specify folder(s) serch in: ["C:\path\to\folders"]')]
        [string]$WorkFolder,
        [Parameter(Mandatory=$True, ParameterSetName="SearchStartMenu", Position=0)]
        [switch]$SearchStartMenu
    )
    BEGIN {
        Write-Verbose "Begin..."       
        IF ($SearchStartMenu) {
$IsAdmin=Test-Admin
Write-Verbose "`$IsAdmin is $IsAdmin"
IF ($IsAdmin -like $false) { Write-Error "Script need to run as administrator" ; Break }
            $WorkFolder = Resolve-Path $("$ENV:ProgramData\Microsoft\Windows\Start Menu") 
        } ELSE {
            $WorkFolder = Resolve-Path "$WorkFolder"
        } # End of IF ELSE
        Write-Verbose "`$WorkFolder=`"$WorkFolder`""
    } # End of Begin
    PROCESS {
        Write-Verbose "Processing..."
        # Remove all Uninstall-links
        $UninstallLinks=Get-ChildItem -Recurse -Path $WorkFolder -File "*.lnk" | Where-Object {$_.Name -like "Uninstall*" } | Resolve-Path
        $UninstallLinks | ForEach-Object { 
            Write-Verbose "Removing `"$_`""
            Remove-Item "$_" -Recurse -Force -ErrorAction SilentlyContinue
            }
    } # End of Process
} # End of Remove-UninstallLinks

Function Remove-EmptyFolders {
     <#
    .SYNOPSIS
        Removes empty folders in a path.
    .DESCRIPTION
        Will remove empty folders in a path.
        Script will loop x times to ensure folders that became empty, default number is 10.
    .NOTES
   davpe67 2017-02-18
    .PARAMETER WorkFolder
   Define a path where script should seach recursive for the folders to remove
   For example: "C:\" or "$ENV:Temp"
    .PARAMETER SearchStartMenu
        Will Set Workfolder to "$ENV:Programdata\Microsoft\Windows\Start Menu"
    .PARAMETER KeepFolders
        Will not remove folders with these names.
        For example: "StartUp","Maintenance"
    .PARAMETER Looptimes
        Script will loop this many times to remove new empty folders from previous runs.
    .EXAMPLE
   Remove-EmptyFolders -SearchStartMenu -Looptimes 10 -WhatIf
    #>
    [cmdletbinding(SupportsShouldProcess=$True,DefaultParameterSetName="WorkFolder")]
    Param(
        [Parameter(Mandatory=$True, ParameterSetName="WorkFolder", Position=0, HelpMessage='Specify folder(s) serch in: ["C:\path\to\folders"]')]
        [string]$WorkFolder,
        [Parameter(Mandatory=$True, ParameterSetName="SearchStartMenu", Position=0)]
        [switch]$SearchStartMenu,
        [int]$Looptimes,
        [string[]]$KeepFolders
    )
    BEGIN {
        Write-Verbose "Begin..."
        IF (!$Looptimes) { $Looptimes=10 }
        IF ($SearchStartMenu) {
$IsAdmin=Test-Admin
Write-Verbose "`$IsAdmin is $IsAdmin"
IF ($IsAdmin -like $false) { Write-Error "Script need to run as administrator" ; Break }
            $WorkFolder = Resolve-Path $("$ENV:ProgramData\Microsoft\Windows\Start Menu") 
        } ELSE {
            $WorkFolder = Resolve-Path "$WorkFolder"
        } # End of IF ELSE
        Write-Verbose "`$WorkFolder=`"$WorkFolder`""
    } # End of Begin
    PROCESS {
        Write-Verbose "Processing..."
        # Remove all folders with no links
        $LoopCounter=0
        do {
            Write-Verbose "Loop number $LoopCounter of $Looptimes"
            $EmptyFolders = Get-ChildItem $WorkFolder -Directory -Recurse -Force -Exclude $KeepFolders | Where-Object { (Get-ChildItem $_.fullName).count -eq 0 } | Select-Object -ExpandProperty FullName
            $EmptyFolders | ForEach-Object { Write-Verbose "Removing `"$_`"" }
            $EmptyFolders | ForEach-Object { Remove-Item "$_" -Recurse -Force -ErrorAction Continue }
            $LoopCounter=$LoopCounter+1
        } while ($EmptyFolders.count -gt 0 -and $LoopCounter -le $Looptimes)
    } # End of Process
} # End of Remove-EmptyFolders

Function Remove-FiletypeFromFolder {
    <#
    .SYNOPSIS
        Removes files of defined type in a path
    .DESCRIPTION
        Will remove all files of defined file type in a path and subfolders, 
        could be used to remove all links from the common desktop.
        Use the -whatif to see what files that will be removed.
    .NOTES
   davpe67 2017-02-25
davpe67 2017-05-11 Added support for exclution of files
    .PARAMETER WorkFolder
   Define a path where script should seach recursive for the folders to remove
   For example: "C:\" or "$ENV:Temp"
    .PARAMETER SearchStartMenu
        Will Set Workfolder to "$ENV:Programdata\Microsoft\Windows\Start Menu"
.PARAMETER SearchPublicDesktop
        Will Set Workfolder to "$ENV:PUBLIC\Desktop"
    .PARAMETER KeepFiles
        Define files that should not be deleted
    .EXAMPLE
   Remove-FiletypeFromFolder -SearchPublicDesktop -FileTypes ".lnk" -WhatIf

        Remove-FiletypeFromFolder -SearchPublicDesktop -FileTypes ".lnk","*.tmp" -Verbose
    #>

    [cmdletbinding(SupportsShouldProcess=$True,DefaultParameterSetName="WorkFolder")]
    Param(
        [Parameter(Mandatory=$True, ParameterSetName="WorkFolder", Position=0, HelpMessage='Specify folder(s) serch in: ["C:\path\to\folders"]')]
        [string]$WorkFolder,
        [Parameter(Mandatory=$True, ParameterSetName="SearchStartMenu", Position=0)]
        [switch]$SearchStartMenu,
        [Parameter(Mandatory=$True, ParameterSetName="SearchPublicDesktop", Position=0)]
        [switch]$SearchPublicDesktop,
        [Parameter(Mandatory=$True, HelpMessage='Specify filetypes to search for: [".lnk",".tmp"]')]
        [string[]]$FileTypes,
        [string[]]$KeepFiles
    )
    BEGIN {
        Write-Verbose "Begin..."
        $IsAdmin=Test-Admin
Write-Verbose "`$IsAdmin is $IsAdmin"
        IF ($SearchStartMenu) {
IF ($IsAdmin -like $false) { Write-Error "Script need to run as administrator" ; Break }
            $WorkFolder = Resolve-Path $("$ENV:ProgramData\Microsoft\Windows\Start Menu") 
        } ELSEIF ($SearchPublicDesktop) {
            IF ($IsAdmin -like $false) { Write-Error "Script need to run as administrator" ; Break }
            $WorkFolder = Resolve-Path $("$ENV:PUBLIC\Desktop") 
        } ELSE {
            $WorkFolder = Resolve-Path "$WorkFolder"
        } # End of IF ELSE
        Write-Verbose "`$WorkFolder=`"$WorkFolder`""
    } # End of Begin
    PROCESS {
        Write-Verbose "Processing..."
        # Search and remove
        foreach ($FileType in $FileTypes) { 
            $FilesToRemove=Get-ChildItem $WorkFolder -Force -Exclude $KeepFiles | Resolve-Path
            $FilesToRemove | ForEach-Object { 
                Write-Verbose "Removing `"$_`""
                Remove-Item "$_" -Recurse -Force
                }
            } # End of foreach
    } # End of Process
} # End of Remove-FiletypeFromFolder

Remove-FiletypeFromFolder -SearchPublicDesktop -FileTypes "*.lnk" -KeepFiles "Mount Onedrive for Business.lnk"
Remove-UnwantedFolders -SearchStartMenu -UnwantedFolders "Java","Microsoft Silverlight"
Remove-UnwantedFiles -SearchStartMenu -UnwantedFiles "Onedrive*.lnk"
Remove-UninstallLinks -SearchStartMenu
Remove-EmptyFolders -SearchStartMenu -KeepFolders "StartUp","Maintenance"
=== SCRIPT Cleanup-Desktop_and_StartMenu.ps1 ENDS ===

How it runs

The script is run by a scheduled task that triggers on computer startup, user logon, and every hour and on the creation of the scheduled job. This is to make sure that any shortcuts that may have been added by software installation have been removed.