startmenu.ps1

function Get-StartMenuProgramsLocation {
    <#
    .SYNOPSIS
        Returns the path to Start Menu > Programs.

    .DESCRIPTION
        Returns the path to the Start Menu Programs folder, for the current user (the default) or for all users.

    .PARAMETER AllUsers
        Return the All Users (machine) Start Menu Programs folder. Aliases: Machine, All.

    .PARAMETER User
        Return the current user's Start Menu Programs folder. (Default.)

    .OUTPUTS
        string - Path to the Start Menu Programs folder.

    .EXAMPLE
        Get-StartMenuProgramsLocation

    .EXAMPLE
        Get-StartMenuProgramsLocation -AllUsers

    .NOTES
        Default scope is User (current user).
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ParameterSetName = "AllUsers")]
        [Alias("Machine", "All")]
        [switch] $AllUsers,
        [Parameter(ParameterSetName = "User")]
        [switch] $User
    )

    # resolved per call, so a folder relocated during the session is picked up
    return $wshShell.SpecialFolders($AllUsers ? "AllUsersPrograms" : "Programs")
}

function New-StartMenuProgramsFolder {
    <#
    .SYNOPSIS
        Creates a new folder in Start Menu > Programs.

    .DESCRIPTION
        Creates a new folder in the Start Menu Programs folder, for the current user (the default) or for all users.

    .PARAMETER Name
        The name of the folder in the Start Menu > Programs folder.

    .PARAMETER AllUsers
        Create the folder in the All Users (machine) Start Menu Programs folder. Aliases: Machine, All.

    .PARAMETER User
        Create the folder in the current user's Start Menu Programs folder. (Default.)

    .OUTPUTS
        string - Path to the newly created folder in the Start Menu Programs folder.

    .EXAMPLE
        New-StartMenuProgramsFolder -Name MyCompany

    .EXAMPLE
        New-StartMenuProgramsFolder -Name MyCompany -AllUsers

    .NOTES
        Default scope is User (current user).
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory, Position = 0)]
        [string] $Name,
        [Parameter(Mandatory, ParameterSetName = "AllUsers")]
        [Alias("Machine", "All")]
        [switch] $AllUsers,
        [Parameter(ParameterSetName = "User")]
        [switch] $User
    )

    $programsLocation = Get-StartMenuProgramsLocation -AllUsers:$AllUsers
    $shortcutFolderName = "$programsLocation\$Name"
    if ($PSCmdlet.ShouldProcess($shortcutFolderName, "Create folder")) {
        New-Item -ItemType Directory $shortcutFolderName -Force | Out-Null
    }
    return $shortcutFolderName
}

function New-StartMenuShortcut {
    <#
    .SYNOPSIS
        Creates a new shortcut in Start Menu > Programs.

    .DESCRIPTION
        Creates a new shortcut in the Start Menu Programs folder, for the current user (the default) or for all users.

    .PARAMETER Name
        The name of the shortcut in the Start Menu > Programs folder.

    .PARAMETER Target
        The location of the file or folder the shortcut opens.

    .PARAMETER Folder
        The name of the folder in Start Menu > Programs to create the shortcut in. If not specified, the shortcut is created directly in Start Menu > Programs.

    .PARAMETER Arguments
        The command-line arguments to pass to the target.

    .PARAMETER RunLocation
        The location the target runs in, shown as "Start in" in the shortcut properties.
        Default: the folder of the target.

    .PARAMETER Description
        The free-text description of the shortcut, shown as its comment / tooltip.

    .PARAMETER Icon
        The icon of the shortcut, as a ShortcutIcon record built by New-ShortcutIcon or read by Get-Shortcut.
        Default: no icon.

    .PARAMETER Hotkey
        The keyboard shortcut that opens the shortcut, e.g. "Ctrl+Alt+N".

    .PARAMETER WindowStyle
        The window state the target launches in: Normal, Maximized or Minimized. Default: Normal.

    .PARAMETER Elevated
        Launch the target elevated, ticked as "Run as administrator" in the advanced properties.
        Alias: Administrator.

    .PARAMETER Force
        Overwrite the shortcut if it already exists. Without -Force, a terminating error is reported when the shortcut exists.

    .PARAMETER AllUsers
        Create the shortcut in the All Users (machine) Start Menu Programs folder. Aliases: Machine, All.

    .PARAMETER User
        Create the shortcut in the current user's Start Menu Programs folder. (Default.)

    .OUTPUTS
        Shortcut record describing the created shortcut, as read by Get-Shortcut. Nothing under -WhatIf.

    .EXAMPLE
        New-StartMenuShortcut -Name MyApp -Target "C:\Program Files\MyApp\MyApp.exe"

    .EXAMPLE
        New-StartMenuShortcut -Name MyApp -Folder MyCompany -Target "C:\Program Files\MyApp\MyApp.exe" `
            -Arguments "--profile Default" `
            -Icon (New-ShortcutIcon -Location "C:\Program Files\MyApp\MyApp.exe" -Index 3) `
            -WindowStyle Maximized -Elevated

    .NOTES
        Default scope is User (current user).
        Alias: Administrator for -Elevated.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    # the type name is a string: the Shortcut class lives in another file, unresolvable at definition time
    [OutputType("Shortcut")]
    param (
        [Parameter(Mandatory, Position = 0)]
        [string] $Name,
        [Parameter(Mandatory, Position = 1)]
        [string] $Target,
        [string] $Folder,
        [string] $Arguments,
        [string] $RunLocation,
        [string] $Description,
        [ShortcutIcon] $Icon,
        [string] $Hotkey,
        [ShortcutWindowStyle] $WindowStyle = [ShortcutWindowStyle]::Normal,
        [Alias("Administrator")]
        [switch] $Elevated,
        [switch] $Force,
        [Parameter(Mandatory, ParameterSetName = "AllUsers")]
        [Alias("Machine", "All")]
        [switch] $AllUsers,
        [Parameter(ParameterSetName = "User")]
        [switch] $User
    )

    $shortcutFolder = $Folder `
        ? (New-StartMenuProgramsFolder -Name $Folder -AllUsers:$AllUsers) `
        : (Get-StartMenuProgramsLocation -AllUsers:$AllUsers)

    # an omitted run location is left to New-Shortcut, which defaults it to the folder of the target.
    # the name differs from the parameter: a [string] parameter would coerce the hashtable to its type name
    $runLocationArgument = $PSBoundParameters.ContainsKey("RunLocation") ? @{ RunLocation = $RunLocation } : @{}

    # New-Shortcut gates the creation behind its own ShouldProcess, inheriting -WhatIf / -Confirm from here.
    # -CreateFolder states that the folder is there: under -WhatIf New-StartMenuProgramsFolder only reports it.
    return New-Shortcut `
        -Location "$shortcutFolder\$Name.lnk" `
        -Target $Target `
        -Arguments $Arguments `
        @runLocationArgument `
        -Description $Description `
        -Icon $Icon `
        -Hotkey $Hotkey `
        -WindowStyle $WindowStyle `
        -Elevated:$Elevated `
        -CreateFolder `
        -Force:$Force
}

function Remove-StartMenuShortcut {
    <#
    .SYNOPSIS
        Removes a shortcut from Start Menu > Programs.

    .DESCRIPTION
        Removes a shortcut from the Start Menu Programs folder, for the current user (the default) or for all users.
        Mirrors New-StartMenuShortcut: the shortcut is expected at <Programs>\<Folder>\<Name>.lnk, or directly at
        <Programs>\<Name>.lnk when Folder is not specified. After removing the shortcut, its containing folder is
        removed too if it is now empty. If the shortcut does not exist, a terminating error is reported.

    .PARAMETER Name
        The name of the shortcut to remove from the Start Menu > Programs folder.

    .PARAMETER Folder
        The name of the folder in Start Menu > Programs that contains the shortcut. If not specified, the shortcut is expected directly in Start Menu > Programs.

    .PARAMETER AllUsers
        Remove the shortcut from the All Users (machine) Start Menu Programs folder. Aliases: Machine, All.

    .PARAMETER User
        Remove the shortcut from the current user's Start Menu Programs folder. (Default.)

    .EXAMPLE
        Remove-StartMenuShortcut MyApp

    .EXAMPLE
        Remove-StartMenuShortcut -Name MyApp -Folder MyCompany

    .EXAMPLE
        Remove-StartMenuShortcut -Name MyApp -AllUsers

    .NOTES
        Default scope is User (current user).
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory, Position = 0)]
        [string] $Name,
        [string] $Folder,
        [Parameter(Mandatory, ParameterSetName = "AllUsers")]
        [Alias("Machine", "All")]
        [switch] $AllUsers,
        [Parameter(ParameterSetName = "User")]
        [switch] $User
    )

    $programsLocation = Get-StartMenuProgramsLocation -AllUsers:$AllUsers
    $shortcutFolder = $Folder ? "$programsLocation\$Folder" : $programsLocation
    $shortcutLocation = "$shortcutFolder\$Name.lnk"

    if (-not (Test-Path -LiteralPath $shortcutLocation)) {
        Write-Error "Shortcut not found: '$shortcutLocation'" `
            -ErrorId "ShortcutNotFound" `
            -Category ObjectNotFound `
            -TargetObject $shortcutLocation `
            -ErrorAction Stop
    }

    if ($PSCmdlet.ShouldProcess($shortcutLocation, "Remove shortcut")) {
        Remove-Item -LiteralPath $shortcutLocation -Force

        # remove the containing folder if it is now empty, but never the Programs root
        if ($shortcutFolder -ne $programsLocation -and -not (Get-ChildItem -LiteralPath $shortcutFolder -Force)) {
            Remove-Item -LiteralPath $shortcutFolder -Force
        }
    }
}

function New-PowershellStartMenuShortcut {
    <#
    .SYNOPSIS
        Creates a new shortcut that runs a PowerShell command in Start Menu > Programs.

    .DESCRIPTION
        Creates a new shortcut that runs a PowerShell command in the Start Menu Programs folder, for the current user (the default) or for all users.

    .PARAMETER Name
        The name of the shortcut in the Start Menu > Programs folder.

    .PARAMETER Command
        The PowerShell command to run.

    .PARAMETER Folder
        The name of the folder in Start Menu > Programs to create the shortcut in. If not specified, the shortcut is created directly in Start Menu > Programs.

    .PARAMETER KeepOpen
        Whether to keep the PowerShell window open after the command has finished running. Alias: NoExit.

    .PARAMETER RunLocation
        The location the command runs in, shown as "Start in" in the shortcut properties.

    .PARAMETER Description
        The free-text description of the shortcut, shown as its comment / tooltip.

    .PARAMETER Icon
        The icon of the shortcut, as a ShortcutIcon record built by New-ShortcutIcon or read by Get-Shortcut.
        Default: no icon.

    .PARAMETER Hotkey
        The keyboard shortcut that opens the shortcut, e.g. "Ctrl+Alt+N".

    .PARAMETER WindowStyle
        The window state the PowerShell window launches in: Normal, Maximized or Minimized. Default: Minimized.

    .PARAMETER Elevated
        Run the PowerShell command elevated, ticked as "Run as administrator" in the advanced properties.
        Alias: Administrator.

    .PARAMETER Force
        Overwrite the shortcut if it already exists. Without -Force, a terminating error is reported when the shortcut exists.

    .PARAMETER AllUsers
        Create the shortcut in the All Users (machine) Start Menu Programs folder. Aliases: Machine, All.

    .PARAMETER User
        Create the shortcut in the current user's Start Menu Programs folder. (Default.)

    .OUTPUTS
        Shortcut record describing the created shortcut, as read by Get-Shortcut. Nothing under -WhatIf.

    .EXAMPLE
        New-PowershellStartMenuShortcut -Name "Kill Node.js" -Command "Stop-Process -Name node -Force"

    .EXAMPLE
        New-PowershellStartMenuShortcut -Name "Run System Update" -Script "C:\Scripts\system-update.ps1" `
            -KeepOpen -WindowStyle Maximized -Elevated

    .NOTES
        Default scope is User (current user).
        Alias: Script for -Command, NoExit for -KeepOpen, Administrator for -Elevated.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    # the type name is a string: the Shortcut class lives in another file, unresolvable at definition time
    [OutputType("Shortcut")]
    param (
        [Parameter(Mandatory, Position = 0)]
        [string] $Name,
        [Parameter(Mandatory, Position = 1)]
        [Alias("Script")]
        [string] $Command,
        [string] $Folder,
        [Alias("NoExit")]
        [switch] $KeepOpen,
        [string] $RunLocation,
        [string] $Description,
        [ShortcutIcon] $Icon,
        [string] $Hotkey,
        [ShortcutWindowStyle] $WindowStyle = [ShortcutWindowStyle]::Minimized,
        [Alias("Administrator")]
        [switch] $Elevated,
        [switch] $Force,
        [Parameter(Mandatory, ParameterSetName = "AllUsers")]
        [Alias("Machine", "All")]
        [switch] $AllUsers,
        [Parameter(ParameterSetName = "User")]
        [switch] $User
    )

    $shortcutFolder = $Folder `
        ? (New-StartMenuProgramsFolder -Name $Folder -AllUsers:$AllUsers) `
        : (Get-StartMenuProgramsLocation -AllUsers:$AllUsers)

    $arguments = @()
    if ($KeepOpen) {
        $arguments += "-NoExit"
    }
    $arguments += "-Command `"$Command`""

    # New-Shortcut gates the creation behind its own ShouldProcess, inheriting -WhatIf / -Confirm from here.
    # -CreateFolder states that the folder is there: under -WhatIf New-StartMenuProgramsFolder only reports it.
    # an omitted run location stays empty: the bare pwsh target has no folder to fall back to.
    return New-Shortcut `
        -Location "$shortcutFolder\$Name.lnk" `
        -Target "pwsh" `
        -Arguments ($arguments -join ' ') `
        -RunLocation $RunLocation `
        -Description $Description `
        -Icon $Icon `
        -Hotkey $Hotkey `
        -WindowStyle $WindowStyle `
        -Elevated:$Elevated `
        -CreateFolder `
        -Force:$Force
}