Assume you want to hand a PowerShell script to an unexperienced user. How can you reliably get that person to correctly launch your script file? Depending on the operating system and Group Policies, there might not be a context menu command to run PowerShell scripts. The user might have to fiddle with execution policy settings, too.

Yet there is a very simple solution: ship your PowerShell script together with a shortcut file. The shortcut file contains all the required command line switches and can be easily invoked via double-click. You can even assign a nice icon to the shortcut. However, you need a couple of little magic tricks to make this work. Typical shortcuts use absolute path names, so the shortcut would no longer work when you send your files to a customer because you never know in which place your customer would save these files. The trick is to make the shortcut use relative paths.

Just make sure you adjust the first line to point to the script you want to launch, then run this code. That’s it.

# specify the path to your PowerShell script
$ScriptPath = "C:\test\test.ps1"

# create a lnk file
$shortcutPath = [System.IO.Path]::ChangeExtension($ScriptPath, "lnk")
$filename = [System.IO.Path]::GetFileName($ScriptPath)

# create a new shortcut
$shell = New-Object -ComObject WScript.Shell
$scut = $shell.CreateShortcut($shortcutPath)
# launch the script with powershell.exe:
$scut.TargetPath = "powershell.exe"
# skip profile scripts and enable execution policy for this one call
# IMPORTANT: specify only the script file name, not the complete path
$scut.Arguments = "-noprofile -executionpolicy bypass -file ""$filename"""
# IMPORTANT: leave the working directory empty. This way, the 
# shortcut uses relative paths 
$scut.WorkingDirectory = ""
# optinally specify a nice icon
$scut.IconLocation = "$env:windir\system32\shell32.dll,162"
# save shortcut file
$scut.Save()

# open shortcut file in File Explorer
explorer.exe "/select,$shortcutPath"

The shortcut appears right next to your PowerShell script. It uses relative paths, so as long as you keep the shortcut in the same folder with your PowerShell script, it continues to work beautifully – so you can zip both files, send them to a customer, have him extract the files, and the shortcut will still continue to work. You can even rename the shortcut file to something fancy, i.e. “Double-click me to run”.

Important: the shortcut uses relative paths to make this solution portable. The shortcut will no longer work, obviously, when you move the shortcut to a folder different from the folder your script resides in.


psconf.eu – PowerShell Conference EU 2019 – June 4-7, Hannover Germany – visit www.psconf.eu There aren’t too many trainings around for experienced PowerShell scripters where you really still learn something new. But there’s one place you don’t want to miss: PowerShell Conference EU - with 40 renown international speakers including PowerShell team members and MVPs, plus 350 professional and creative PowerShell scripters. Registration is open at www.psconf.eu, and the full 3-track 4-days agenda becomes available soon. Once a year it’s just a smart move to come together, update know-how, learn about security and mitigations, and bring home fresh ideas and authoritative guidance. We’d sure love to see and hear from you!

Twitter This Tip! ReTweet this Tip!

Anonymous
Parents
No Data
Comment
  • Would you like this in the form of a function with pipeline support, array support to create more than one shortcut file, parameter validation on the script path, options for ExecutionPolicy with parameter set validation and a default value, and options for NoProfile and NoExit? I did. (Sadly, no help text.)

    function New-ShortcutForPowerShellScriptFile {
        param (
            [Parameter(Mandatory=$true,
                        ValueFromPipelineByPropertyName=$true)]
            [ValidateScript({Test-Path $_})]
            [string[]]$FullName,
            [ValidateSet("Restricted","AllSigned","RemoteSigned","Unrestricted","Bypass","Undefined")]
            [string]$ExecutionPolicy = "Bypass",
            [switch]$NoProfile,
            [switch]$NoExit
        )
        begin{}
        process
        {
            foreach ($ScriptPath in $FullName)
            {
                # create a lnk file
                $shortcutPath = [System.IO.Path]::ChangeExtension($ScriptPath, "lnk")
                $filename = [System.IO.Path]::GetFileName($ScriptPath)
                # create a new shortcut
                $shell = New-Object -ComObject WScript.Shell
                $scut = $shell.CreateShortcut($shortcutPath)
                # launch the script with powershell.exe:
                $scut.TargetPath = "powershell.exe"
                #Build arguments line
                $Arguments = "-ExecutionPolicy $ExecutionPolicy"
                if ($NoProfile)
                {
                    $Arguments += " -NoProfile"
                }
                if ($NoExit)
                {
                    $Arguments += " -NoExit"
                }
                $Arguments += " -File ""$filename"""
                # IMPORTANT: specify only the script file name, not the complete path
                $scut.Arguments = $Arguments
                # IMPORTANT: leave the working directory empty. This way, the
                # shortcut uses relative paths
                $scut.WorkingDirectory = ""
                # optionally specify a nice icon
                $scut.IconLocation = "$env:windir\system32\shell32.dll,162"
                # save shortcut file
                $scut.Save()
                # open shortcut file in File Explorer
                #explorer.exe "/select,$shortcutPath"
                Write-Host "Shortcut created: $shortcutPath"
            } #end foreach $ScriptPath in $FullName
        }
        end{}
    } #end function New-ShortcutForPowerShellScriptFile
Children
No Data