Lately, I’ve become obsessed with automating local development environment tasks, especially when it comes to onboarding, bootstrapping, and managing local machine environments for various projects. When something like a web application starts to add external dependencies like services running in docker-compose, quickly that project’s onboarding steps can get quite lengthy. Managing issues around onboarding steps can become a pain, and troubleshooting them, especially when those tasks are expected to be run across operating systems, can be very difficult and time consuming. PowerShell Core, being cross-platform and JIT runnable, offers a friendly scripting experience for creating command-based command line interfaces for whatever application you need.
- Install PowerShell Core on your operating system of choice: https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell
- All the examples can also be found here. Download/clone this repo to follow along with each step: https://github.com/kavun/ps-cli
Let’s build a script with these commands:
.\cli.ps1 up .\cli.ps1 down .\cli.ps1 build .\cli.ps1 test .\cli.ps1 migrate <name> .\cli.ps1 ip
Validate the commands
To enable these ad-hoc command names (without the default
- prefix that PowerShell params normally require), we can use a combination of
Parameter(Position=0) attributes on the script’s first
This ensures that when you pass an incorrect command or if you exclude the command, you get a detailed error message.
Handle the commands
Now we need to handle the commands and run functions for each one. For this, we’ll use a
Now, when a command is passed, the associated function is run.
This is great, but it’s not immediately known what commands are available without opening up the
.ps1 file and reading it. We could add a command
help and then spit out some help content with
Write-Host "help string", but there’s a better way. PowerShell scripts can hook into the
Get-Help command to provide structured help documentation for the whole script. Let’s do both!
Let’s look at what we changed:
<# #>help content at the top of the file. And added
$Commandparam, so that we can show the help content when you call the script with no params.
- Added the
helpcommand, which ends up calling
This will show help for all 4 of these commands. The goal here is to make finding help foolproof.
.\3-help.ps1 .\3-help.ps1 help .\3-help.ps1 -? Get-Help .\3-help.ps1
Handle nested commands
What we have is great for simple one-liners, but eventually we’ll want to pass params to the commands and also support nested commands.
Let’s look at what we did:
- We used
ValueFromRemainingArgumentsto capture any trailing params in the
$Restparam and passed those along to the inner functions.
- We used
ValidateSeton an inner function’s param, just like we did on the script’s params.
- Added help docs to one of the inner functions.
These are just examples, but you can see the range of possibilities that open up when we start nesting commands and forwarding params. Here’s what this example looks like in use:
- Scripts from examples: https://github.com/kavun/ps-cli
- about_Comment_Based_Help (
<# #>): https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help