Ah, the simple things in our lives. Requests like “push this shortcut to a folder in the start menu” or “show a link to our support page on the public desktop”. Nowhere near rocket science, but having something reusable is always nice.
This post isn’t about how you can use my solution (I explain that in this README). It’s more of a ride-along through my thought process, while I figure out what to do.
I decided to consider a shortcut to be a very (very) simple app. MEM already offers a lot of options for app provisioning (and everyone probably knows how they work) so let’s just use those. Why re-invent the wheel, right?
In all honesty, if you are (or will be) looking to provision more than just simple shortcuts: don’t use this. I suggest you consider alternative solutions, like the most excellent PSAppDeployToolkit.
Time to set some goals. My solution should be very lightweight, stand-alone, easily customizable, and revocable. I don’t want to have to edit the scripts every time I need to configure a new shortcut, and I need it to be able to update already provisioned shortcuts.
The result of all this is what you see on this GitHub repository: a payload you can package with Microsoft’s Win32 Content Prep Tool (IntuneWinAppUtil) and then deploy like any other Win32-app.
Your package has arrived
Let me introduce you to the components, first. The packaged Win32 app contains the following payload:
shortcut.json– the configuration file.
Shortcuts.psm1– the module containing all shared logic.
Install-Shortcut.ps1– the install script.
Uninstall-Shortcut.ps1– the uninstall script.
That’s all, folks. All 8.76 KB of it. Unless you add an icon.
Win32 app provisioning starts with a detection rule. Each Win32 app determines its installation status with at least one of those. If the app is deemed missing, the install command (Install-Shortcut.ps1) triggers.
Requirements and dependencies are checked as well, but I don’t use them here. If you want to, get creative with them! Like setting a shortcut only after downloading another executable, but never on x86-architectures.
My first thought was to just check for the existence of shortcut-files. This has a few drawbacks though. The rule doesn’t know anything about the configuration so it can’t determine how many shortcuts were set and where they’re located.
Also, its existence alone doesn’t guarantee we provisioned it. It might have been created by the user. But that may be overthinking it on my part.
To solve this, I have each package create a marker-file in a predictable location. The detection rule can then check for the existence of that file to trigger the install command.
Note that this allows the user to ‘permanently’ remove a shortcut-file (if they have the required permissions). As the marker-file remains firmly in place, the package will not re-deploy in such cases. I considered this to be good behavior but that’s up for discussion 😊.
If you do need mandatory shortcuts, you may want to set them in the public desktop, create a custom detection rule, or change the script to set file permissions).
After loading the module and configuration, the
Install-Shortcut.ps1 script does a couple of things:
- The (optional) icon-file is stored in a persistent location.
- The ‘Known folder’ locations are determined (and created if necessary).
- The shortcut-files are created in the configured locations.
- The (optional, but advisable) marker-file is created in a persistent location.
I mentioned “a persistent location” earlier, but never explained it. For this to work, I need a place to store data, and it needs to be accessible to all users. The most logical way to do this is to create a folder in
C:\ProgramData\ (which checks all the boxes), so that’s where it’s at.
Arrogantly, I hard-coded this folder to be named “ThreeIsACloud” 😊. Feel free to change it in
Shortcuts.psm1, line 129.
When you have MEM remove this app, the the uninstall command (
Uninstall-Shortcut.ps1) executes. It’s almost the same as the install command, but in reverse. Once again, the module and configuration are loaded and then it does the following:
- The (optional) icon-file is removed.
- The ‘Known folder’ locations are determined.
- The shortcut-file(s) are removed from the configured locations.
- The (optional) marker-file is removed.
You may notice that it doesn’t remove any folders. I can’t be sure that a start-menu folder isn’t used for anything else, so I figured I’d just leave it alone.
Cool. We can now install and uninstall shortcuts. But what if we need to update them? After all, IT is a volatile environment.
In essence, an update only needs to execute the install command again. This then overwrites (or re-creates) the shortcut-file(s) based on the new configuration. Triggering the install command was/is the detection rule’s job so let’s see if we can fool it into thinking the ‘app’ is missing.
What if this rule checks the marker-file’s modification timestamp? If it’s earlier (older) than the update’s deployment timestamp, it must need an update. That works, but these rules aren’t dynamic so I will have to tell it what the deployment timestamp is.
This is where I failed: I couldn’t think of a way to do this without providing the date/time in the rule’s configuration. I did, however, make it easier as the
Check-ShortcutConfig.ps1 script actually tells you exactly what your rule should look like. I’m sorry, forgive me.
Remember, this marker-thing can be turned off if you don’t want to use it. It’s all up to you.
In closing, let me repeat that I know there are many solutions out there (and, most of the time, there is nothing wrong with them). I do this stuff because it helps me to understand exactly what is going on under the hood… and what challenges arise when you throw a monkey-wrench in it.
For example: this one started as an exploration on how to deploy a PowerShell solution (like the PowerUser one) in a more controllable way… and then someone asked me to push a shortcut to a desktop.
[…] Shortcut provisioning: cutting it really, really short […]
Works like a charm. Thx!