Using Stucco to Create Your PowerShell Module
Table of Contents
It all started one summer day… just kidding! You can skip to the “recipe” here…
It’s funny that I haven’t written this post before. I’ve done talks and have been a huge proponent for Stucco for a few years. I think I discovered it during one of Brandon Olin’s (aka devblackops) talk.
Some reasons I’m a huge fan
- Each function/cmdlet is it’s own file.
- Includes several high quality tests out of the box.
- It auto generates your docs via PlayPS.
- Ready for deploying to the CI tool of your choice.
- Include VSCode configs that make developing your code easy!
Before using Stucco I had hand crafted my modules but bespoke code is prone to trivial errors. This becomes even more of an issue when you work on the code as a larger team.
psake and PowerShellBuild #
One of the things that makes Stucco extremely powerful is it’s use of psake and PowerShellBuild. psake is a build system in PowerShell and PowerShellBuild is a set of pre-defined tasks for psake. Both of these are Brandon’s projects as well!
psake allows for easy extensibility and PowerShellBuild lays down a strong set of foundational tasks.
Your Entry Point #
After you create your first Stucco module you’ll be using the
script for essentially all your work. The tasks are called with the
parameter. So as you write your code and you want to test, you would just call
the same build script with the Test task and everything happens.
What’s everything? It cleans up previous builds, stages your files, builds your documentation, runs script analyzer, and tests your code. If you run the Publish task you’ll get all the same steps but then you’ll find your modules in the PSGallery! It’s that easy.
VSCode Tasks #
With the build script being the pivotal point for you to do your work, it makes sense that exposing those commands is critical. This was another huge feature for me. VSCode has a concept called tasks that you can call through the Command Palette or via Shortcuts. Stucco includes all the tasks that come from PowerShellBuild as VSCode tasks.
What’s that mean for you the developer? Write code. Hit shortcut run test. Rinse and repeat. It makes taking on Test Driven Development (TDD) very easy. See Run Your First Test to see an example.
Enough talk! Let do! #
At a high level we’ll be doing the following
- Install Stucco
- Create your GitHub repo and check it out
- Create your Stucco module
- Run your Test
- Commit your code
- Prep for PSGallery
- Publish your module
There are some tweaks for other repositories/CI systems but if you’re goal is to write public modules, I found this to be pretty easy.
Install Stucco #
Honestly that could be the whole thing. Under the hood Stucco depends on all the necessary modules so when you install Stucco it’ll pull down it’s requirements.
GitHub Repository #
If you don’t already have a GitHub account go ahead and sign up. I’ll skip on the details since there are several articles online to explain how and some best practices.
You’ll want to create a new repository for your module. So click on New.
Once you have your repository you’ll want to give a name and a description. Then you will check it out on your machine where you have Stucco installed.
Create Your Modules #
Now you can create you module in your repo folder. It’s as simple…
New-StuccoModule. Here’s an Example.
$ New-StuccoModule cmdlet New-StuccoModule at command pipeline position 1 Supply values for the following parameters: DestinationPath: . ____ _ _ | _ \| | __ _ ___| |_ ___ _ __ | |_) | |/ _` / __| __/ _ \ '__| | __/| | (_| \__ \ || __/ | |_| |_|\__,_|___/\__\___|_| v1.1.3 ================================================== Enter the name of the module: ExampleModule Enter a description of the module: An example Enter the version number (1.2.3) for the module (0.1.0): Enter your full name (Gilbert Sanchez): Select a license for your module [A] Apache [M] MIT [N] None [?] Help (default is "M"): Do you want to include a Code of Conduct? [Y] Yes [N] No [?] Help (default is "N"): Do you want to include MkDocs support? [Y] Yes [N] No [?] Help (default is "N"): Will you be using PowerShell classes? [Y] Yes [N] No [?] Help (default is "Y"): Do you want to use PlatyPS for help documentation? [Y] Yes [N] No [?] Help (default is "Y"): Do you want to include VSCode dev container support? [Y] Yes [N] No [?] Help (default is "N"): Do you want to include CI/CD support? [A] AppVeyor [P] Azure Pipelines [H] GitHub Actions [G] GitLab CI [J] Jenkins CI [M] MJenkins CI(MultiStage) [N] None [?] Help (default is "H"): Destination path: /Users/gsanchez/repos/ExampleModule Creating an opinionated high-quality PowerShell module with psake tasks and GitHub meta documents . Create ExampleModule/ExampleModule.psd1 Create .gitignore Create .gitattributes Create build.ps1 Create psakeFile.ps1 Create requirements.psd1 Create .github/workflows/CI.yaml Create .github/CONTRIBUTING.md Create .github/ISSUE_TEMPLATE.md Create .github/PULL_REQUEST_TEMPLATE.md Create CHANGELOG.md Create README.md Create ExampleModule/Classes/classA.ps1 Create ExampleModule/Private/GetHelloWorld.ps1 Create ExampleModule/Public/Get-HelloWorld.ps1 Create tests/Help.tests.ps1 Create tests/Manifest.tests.ps1 Create tests/Meta.tests.ps1 Create tests/MetaFixers.psm1 Create tests/ScriptAnalyzerSettings.psd1 Create .vscode/extensions.json Create .vscode/settings.json Create .vscode/tasks.json Create LICENSE Create docs/en-US/about_ExampleModule.help.md Create ExampleModule/ExampleModule.psm1 Your new PowerShell module project [ExampleModule] has been created
If you follow the prompts you’ll get the right files. Easy! Next you’ll want to check out the files and layout.
Some key ones are…
Build.ps1: I mentioned this above. This is the work horse of the whole process.
Requirements.ps1: PSDepends (which the build file loads) uses this to setup your environment and setup any needed files/modules.
psakeFile.ps1: The psake configuration.
Run Your First Test #
You now have the “Hello World” version of the module. It can actually compile and give you a Hello World cmdlet. That should be good enough for testing.
Let’s run the Test task. You can find it under Terminal -> Run Task -> Test.
At this point you’ll see the build output. You can see how all the dependant tasks are executed before testing.
./build.ps1 -Task Test Task: INIT Build System Details: Build Module: PowerShellBuild:0.6.1 PowerShell Version: 7.3.0 Environment variables: BHPSModulePath /Users/gsanchez/repos/ExampleModule/ExampleModule BHProjectName ExampleModule BHPSModuleManifest /Users/gsanchez/repos/ExampleModule/ExampleModule/ExampleModule.psd1 BHBuildNumber 0 BHProjectPath /Users/gsanchez/repos/ExampleModule BHBuildSystem Unknown BHModulePath /Users/gsanchez/repos/ExampleModule/ExampleModule BHBuildOutput /Users/gsanchez/repos/ExampleModule/Output/ExampleModule/0.1.0 Task: CLEAN Task: STAGEFILES Task: GENERATEMARKDOWN WARNING: [Update-MarkdownHelp] 01/08/2023 15:37:27 Path '/Users/gsanchez/repos/ExampleModule/docs/en-US' does not contain any markdown files. Task: GENERATEMAML Task: BUILDHELP Task: PESTER Pester v5.3.3 Starting discovery in 3 files. Discovery found 21 tests in 288ms. Running tests. Running tests from '/Users/gsanchez/repos/ExampleModule/tests/Help.tests.ps1' Describing Test help for Get-HelloWorld [+] Help is not auto-generated 61ms (36ms|25ms) [+] Has description 10ms (9ms|1ms) [+] Has example code 18ms (16ms|1ms) [+] Has example help 4ms (2ms|1ms) Context Parameter Value [+] Has description 8ms (5ms|3ms) [+] Has correct [mandatory] value 23ms (22ms|1ms) [+] Has correct parameter type 6ms (5ms|1ms) Running tests from '/Users/gsanchez/repos/ExampleModule/tests/Manifest.tests.ps1' Describing Module manifest Context Validation [+] Has a valid manifest 6ms (4ms|2ms) [+] Has a valid name in the manifest 18ms (17ms|1ms) [+] Has a valid root module 9ms (5ms|4ms) [+] Has a valid version in the manifest 19ms (15ms|4ms) [+] Has a valid description 10ms (7ms|3ms) [+] Has a valid author 13ms (10ms|3ms) [+] Has a valid guid 48ms (45ms|3ms) [+] Has a valid copyright 42ms (3ms|38ms) [+] Has a valid version in the changelog 5ms (3ms|1ms) [+] Changelog and manifest versions are the same 7ms (5ms|2ms) Describing Git tagging [!] Is tagged with a valid version 3ms (0ms|3ms) [!] Matches manifest version 1ms (0ms|1ms) Running tests from '/Users/gsanchez/repos/ExampleModule/tests/Meta.tests.ps1' Describing Text files formatting Context File encoding [+] No text file uses Unicode/UTF-16 encoding 4ms (3ms|2ms) Context Indentations [+] No text file use tabs for indentations 4ms (2ms|1ms) Tests completed in 1.16s Tests Passed: 19, Failed: 0, Skipped: 2 NotRun: 0 Task: ANALYZE Task: TEST psake succeeded executing ./psakeFile.ps1 Task: BUILD TIME REPORT Name Duration ---- -------- Init 00:00:00.086 Clean 00:00:00.002 StageFiles 00:00:00.169 GenerateMarkdown 00:00:00.399 GenerateMAML 00:00:00.260 BuildHelp 00:00:00.001 Build 00:00:00.000 Pester 00:00:01.954 Analyze 00:00:00.626 Test 00:00:00.000 Total: 00:00:03.609
You can see all the steps and how long they took. Nice!
Commit Your Code #
Now let’s publish our code. I prefer to check in the initial version and add each feature in separate commits but that’s just me.
git add . git commit -m "Initial commit!" git push
Setup PSGallery #
Once you have some versions checked into your repository on GitHub, you will eventually want to publish. First let’s create an API key on the PowerShell Gallery.
- Login to PSGallery
- Create API keys
- Copy them to your password vault.
With your key handy you’ll want to add it to GitHub.
- Go to your new repo and click the Settings pane
- You want to go to Secrets and then Actions
- Name it
Publish Your Module! #
With you API key in your GitHub secrets you’re not ready to publish from GitHub. I would recommend creating a publish workflow on GitHub. You can use mine as an example: publish.yaml.
My workflow does a few convenient things
- Uses Chrissy LeMaire’s psmodulecache to speed up the builds
- Checks if the version is already published
- If it’s newer, it attempts to Publish with the Publish task
Wrap Up #
And that’s it! You now have a module that’s testable, open source, and automatically published to the PowerShell Gallery!
There are so many more cool details I could go over but I don’t want this to run longer then it already has. If there was anything unclear, please let me know by adding a comment or messaging me on the various linked social medias. I hope this was helpful and I would love to see what you publish!
- My Stucco Presentation - Doc
- My Stucco Presentation - Slides
- Stucco on GithHb
- Plaster on GitHub
- PowerShell Module Cache GitHub Action
Cover Photo by Avel Chuklanov on Unsplash