Windows Shortcuts With PowerShell — How To Make, Customize And Point Them To Places

Danilo Bilanoski
10 min readMar 5, 2024

--

Every now and then, there will be a situation where you’ll encounter what seems like the most common task at hand, thinking confidently: “Pfff, I’ll be done in 30 minutes!” Cut to two hours later and some elusive documentation hunting, you find yourself looking at your code example thinking: “Jesus, is this really how it is?”

The other day, I was presented with a case for which a part of the solution involved creating and deploying a desktop shortcut to the “Recording” panel of the Control Panel’s Sound settings in Windows 11 to a group of users. I already knew that PowerShell lacks a “native” way of creating shortcuts so I’d be exploring some shell methods to accomplish it. But what if I wanted to point that shortcut to a Control Panel item’s sub-menu or perhaps use a custom icon already present on computers but stored inside a dynamic library?

Well, dear reader, there are ways, and sometimes you’ll be poking under the hood of the operating system to find a path to a menu window. Let’s take a good look into creating and customizing Windows shortcuts.

Photo by Dan Crile on Unsplash

Shortcuts, Symbolic Links and PowerShell

First things first — there is a built-in way in PowerShell to create Symbolic Links using the New-Item cmdlet.

New-Item -ItemType SymbolicLink -Path "C:\example-link.lnk" -Target "C:\Users\dbilanoski\Documents\some-text.txt"

You might come across instructions elsewhere suggesting creating shortcuts like that. However, be cautious, as these are not quite the same things.

Symbolic links are direct links to other files interpreted by the file system itself. They are not like regular files; they lack size, and applications are inherently aware of them. When used, it’s like you used the thing you are linking it to directly.

Shortcuts, on the other hand, are regular files containing paths to referenced objects interpreted by the Windows user interface. They have size, can be configured with icons, and when used, something must be able to read them and the target path they hold (Windows user interface).

When in a need of a shortcut in Windows, you’ll be wanting shortcut, not a symlink. Why?

  1. Shortcuts keep track of the file they reference and are updated on changes, reducing the likelihood of breaking.
  2. Besides files and folders, shortcuts can open network locations, configuration panels, and special folders that do not exist as paths to files in the file system.
  3. Shortcuts can be configured to start in a context of particular folder.
  4. You cannot include command-line arguments to executables in the target path using symlinks.
  5. Icons cannot be configured for symlinks.

What makes shortcuts user-friendly and convenient is that they are handled by the Windows user interface (shell), which is bringing all the additional features and the convenience. However, creating them in PowerShell requires tapping into shell methods, which is adding a bit more complexity to the process.

For Those In A Hurry

To create a shortcut with PowerShell and customize a unique icon for it, refer to the basic example below. Be sure to adapt the paths and file names to suit your specific circumstances.

$ShortcutTarget= "path-to-your-target\name-of-your-target"
$ShortcutFile = "path-to-your-to-be-shortcut\name-of-the-shortcut.lnk"
$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($ShortcutFile)
$Shortcut.TargetPath = $ShortcutTarget
$Shortcut.IconLocation = "path-to-your-icon\icon-name.ico"
$Shortcut.Save()

If you need a one-liner:

# For PowerShell scripts
$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('path-to-your-to-be-shortcut\name-of-the-shortcut.lnk'); $s.TargetPath = 'path-to-your-target\name-of-your-target'; $s.IconLocation = 'path-to-your-icon\icon-name.ico'; $s.Save()

# For batch (CMD) scripts
powershell -ExecutionPolicy Bypass -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('path-to-your-to-be-shortcut\name-of-the-shortcut.lnk'); $s.TargetPath = 'path-to-your-target\name-of-your-target'; $s.IconLocation = 'path-to-your-icon\icon-name.ico'; $s.Save()"

Specific Examples

Appropriate TargetPath and, when needed, Arguments configuration for different shortcut target types.

# Targeting a file
$Shortcut.TargetPath = "C:\Windows\System32\notepad.exe"

# Targeting a folder
$Shortcut.TargetPath = "C:\Users\Public\Documents"

# Targeting a network share
$Shortcut.TargetPath = "C:\Windows\explorer.exe"
$Shortcut.Arguments = "\\192.168.10.2\shares"

# Targeting with environment variables
$Shortcut.TargetPath = $env:userprofile + "\Downloads"

# Targeting a .cpl file
$Shortcut.TargetPath = "C:\Windows\System32\mmsys.cpl"

# Targeting by using MS:SETTINGS URI scheme
$Shortcut.TargetPath = "ms-settings:installed-apps"

# Targeting with shell command by name
$Shortcut.TargetPath = "C:\Windows\explorer.exe"
$Shortcut.Arguments = "shell:NetworkPlacesFolder"

# Targeting with shell command by CLSID GUID
$Shortcut.TargetPath = "C:\Windows\explorer.exe"
$Shortcut.Arguments = "shell:::{BB06C0E4-D293-4f75-8A90-CB05B6477EEE}"

# Targeting by traversing a dll with rundll32
$Shortcut.TargetPath = "C:\Windows\System32\rundll32.exe"
$Shortcut.Arguments = "shell32.dll,Control_RunDLL inetcpl.cpl,,4"

Details

Creating Shortcuts With Powershell

To create shortcuts with PowerShell, we need to generate a COM object that will function as a container for our instance of the WScript.Shell class, a part of a somewhat ancient COM interface probably originally intended for Visual Basic scripts. So, we’ll make an object equipped with all the goodies we will need to handle shortcuts with code. Since this involves working with variables and properties, let’s break it down into steps.

Let’s say we aim to create a desktop shortcut to notepad.exe (a commonly used text editing tool in Windows) for each user on a computer.

1. Begin by creating variables to store the paths of our target object and the shortcut itself.

# What my shortcut is pointing at
$ShortcutTarget = "C:\Windows\System32\notepad.exe"
# Where my shortcut will be saved and how it will be named
$ShortcutFile = "C:\Users\Public\Desktop\notepad.lnk"

2. Generate an instance of the WScript.Shell class as a COM object using the New-Object cmdlet.

$WScriptShell = New-Object -ComObject WScript.Shell

3. Create a shortcut using the CreateShortcut() method from the WScript.Shell instance

$shortcut = $WScriptShell.CreateShortcut($ShortcutFile)

Now, let’s pause a bit and see what we can do with the shortcut by examining the shortcut object’s properties and methods.

Screenshot showing available properties and methods of the shortcut object created with the WShell.Script.CreateShortcut()

We definitely need the TargetPath property to designate Notepad.exe for execution and the Save() method to produce the shortcut.

If we wanted to assign an icon to our shortcut, the IconLocation property becomes relevant. If we wanted to pass command line arguments to an executable referenced in the TargetPath, Arguments property becomes relevant.

WorkingDirectory is sometimes needed around RelativePath usage, but I advise avoiding relative paths when dealing with shortcuts.

4. Moving on with bare basics, assign the Target.Path property to the location and the name of the target, using a variable set earlier.

$Shortcut.TargetPath = $ShortcutTarget

5. Complete the process by calling the Save() method to save the shortcut to the path designated in the ShortcutFile variable

$Shortcut.Save()

Your shortcut will be ready at the location defined in the $ShortcutFile variable.

Pointing Shorctuts To Places

Now that we know how to programmatically create a shortcut with PowerShell, let’s explore how it looks when you want to point it to something other than a file. Because, unfortunately, it’s not always that obvious.

So, we’ll use real examples in code blocks with comments as we are only changing one or two properties — TargetPath, and Arguments when it’s needed.

Shortcut To A Folder

$Shortcut.TargetPath = "C:\Users\Public\Documents"

Shortcut To A Network Share

Note that here we are using the Arguments property to pass the UNC formatted path to explorer.exe configured in the TargetPath property.

This is primarily to avoid issues with the shortcut creation procedure, as the process would otherwise fail if the path was not accessible to the context within which the command was executed.

$Shortcut.TargetPath = "C:\Windows\explorer.exe"
$Shortcut.Arguments = "\\192.168.10.2\shares"

Shortcut Target Paths With Environment Variables

# By using standard concatenation with + sign
$Shortcut.TargetPath = $env:userprofile + "\Downloads"

# By using Join-Path cmdlet
$Shortcut.TargetPath = Join-Path -Path $env:userprofile -ChildPath "Downloads"

Shortcuts To Control Panel Files

These can be found in the C:\Windows\System32 folder and will be convenient for accessing Control Panel items. However, more often then not, you’ll be looking at other ways to reach places there.

$Shortcut.TargetPath = "C:\Windows\System32\mmsys.cpl"

Shortcuts To Panels Via Windows Settings App

This provides means of targeting specific pages from the Windows Settings application by following the ms-settings:<page-name> scheme.

$Shortcut.TargetPath = "ms-settings:installed-apps"

Shortcuts To Places Via Shell Commands

Many places in Windows lack absolute-path-to-a-file-like access, making it less obvious how to programmatically reach certain panels or folders. These are known as Shell Folders or Virtual Folders and can be accessed using shell commands, either with known names or with CLSID unique identifier keys (GUIDs), which are special keys from the registry that can be used to directly open items.

If you want to learn more about them or see a complete list with ready-to-use Windows 11 shortcuts, I’ve written about it more in-depth here.

# By using shell command with a name
$Shortcut.TargetPath = "C:\Windows\explorer.exe"
$Shortcut.Arguments = "shell:NetworkPlacesFolder"

# By using shell command with a CLSID GUID
$Shortcut.TargetPath = "C:\Windows\explorer.exe"
$Shortcut.Arguments = "shell:::{BB06C0E4-D293-4f75-8A90-CB05B6477EEE}"

Customizing Shortcuts With Icons

If your shortcut points to an item that already has an icon, the path to that icon will be stored in the IconLocation property.

To assign a custom icon to your shortcut using PowerShell, include an additional step during shortcut creation to configure the IconLocation property with a reachable path to an existing icon before saving your shortcut.

$Shortcut.IconLocation = "\\path-to-your-icon.ico"

Now that you have your shortcut configured with a custom icon, you probably need to ensure the icon’s availability if the script is to be deployed elsewhere. As always, a good approach will involve leveraging existing elements already present on all computers.

There are places in the Windows OS where general UI icons are stored and used. Typically, these icons are packed in dynamic link libraries (DLL), which are libraries containing data shared by multiple programs simultaneously.

Although there are many of those around the system, the most commonly utilized DLLs with icons used by the OS include:

  • C:\Windows\system32\imageres.dll
  • C:\Windows\system32\shell32.dll
  • C:\Windows\system32\ddores.dll

The catch is that these icons are not standard files, and you must reference them within the DLL file using an appropriate index number. And these are not easy to see — hence the two-hour line at the beginning of the text.

To check the DLL’s nested icon index number without resorting to third-party software or wasting too much time, use the “Properties” panel of an existing shortcut to browse the DLL file. Once there, select the desired icon, save the change and plug the shortcut to the COM instance of the WScriptShell in PowerShell. You’ll be able to inspect the IconLocation property and identify the index number you can use in your code.

Referencing A DLL Icon

Let’s see this through with an example. Suppose I wish to distribute my Notepad shortcut with a distinct icon sourced from one of these libraries — a keyboard icon from the imageres.dll library.

If you look at the current IconLocation of the Chrome shortcut on my Desktop, you’ll see it has an icon from within the chrome.exe package (green line), with an index number of 0 (pink line). This index is expressed as part of the path, where the file path is separated from the index number by a comma.

Screenshot showing how to change icon of the shortcut and retrive it’s path from within the DLL library.
Here I’m using the existing WScriptShell variable from my earlier examples. You might need to instantiate it as explained in Step 2 earlier.

Furthermore, within the chrome.exe package, you’ll notice there are other available icons we could potentially use.

We can use that same Chrome shortcut to explore the imageres.dll library, select the desired shortcut, and confirm its index number.

1. Access the properties page, click the “Change Icon” button, navigate to “C:\Windows\system32\imageres.dll” and press “Enter” to reveal the available icons.

Here I have chosen the keyboard icon I want to use.

Screenshot showing Chrome’s shortcut “Properties” panel pointing whre you need to click to browse icons.

2. Once changed, call the WScript.Shell’s CreateShortcut() on the shortcut itself and inspect the IconLocation property.

Screenshot showing properties of a shortcut object created with WScript.Shell.CreateShortcut() where the icon is changed to an item from within a DLL library.
Here I’m using the existing WScriptShell variable from my earlier examples. You might need to instantiate it as explained in Step 2 earlier.

There you’ll have it. Now, we can seamlessly add it into our Notepad shortcut using the IconLocation path we have retrieved.

$Shortcut.IconLocation = "C:\Windows\system32\imageres.dll,173"

One-liners

If you wish to have these as one-liners for simplicity, you can chain commands using semicolons in a “first command; second command; third command” manner. Here, we’ll be ditching path variables and putting them directly into properties and methods.

$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut("C:\Users\Public\Desktop\notepad.lnk"); $s.TargetPath = "C:\Windows\System32\notepad.exe"; $s.IconLocation = "C:\Windows\system32\imageres.dll,173"; $s.Save()

For those of us running things from the cmd:

powershell -ExecutionPolicy bypass -Command "$ws = New-Object -ComObject WScript.Shell; $s = $ws.CreateShortcut('C:\Users\Public\Desktop\notepad.lnk'); $s.TargetPath = 'C:\Windows\System32\notepad.exe'; $s.IconLocation = 'C:\Windows\system32\imageres.dll,173'; $s.Save()"

Conclusion

While the creation of shortcuts with PowerShell introduces complexity with using COM objects to instantiate the WScript.Shell class for accessing its shortcut creation goodies, the process boils down to the routine task of configuring property values. Thorough testing before deployment, attention to absolute path accuracy, and a mindset of reusability with existing system elements, such as icons or specific paths, are essential considerations.

What might consume your time is delving into the operating system’s intricacies to figure out how to target specific panels or virtual folders. However, in most cases, it can be distilled into a deployable single line of code, which is all we need in the end.

Author’s Note

You made it to this point! Well, kudos to you my friend — either I’m a decent writer or you’re an excellent reader. Let’s go with the latter😅.

I’m Danilo, a seasoned IT Service Delivery engineer navigating the corporate chaos. I write about scripting, sysadmin stuff, and topics that are poorly documented elsewhere, with the aim of sharing knowledge and improving my writing skills.

Clap hands and leave feedback if you can.

--

--

Danilo Bilanoski

Follow me for occasional read about scripting, system adminstration and problem solving where we dip our toes into technical guidance - all in plain English.