The PowerShell Gallery is a fantastic resource for finding modules, scripts, and DSC resources written by Microsoft and others in the community. There’s been a shift from downloading MSIs to install PS modules to using the PowerShell Gallery and downloading directly from a PowerShell prompt using the Install-Module command. If you’ve written a module or script that you feel others could benefit using, definitely check out publishing it to the broader community. In fact, that’s what this post is about. I will walk through the steps I took to publish my first PowerShell module to the gallery.
If you’d rather watch a video demonstration of this, check out my presentation at the Chicago PowerShell Conference available on YouTube
First, I need to create an account over at https://www.powershellgallery.com. PowerShell Gallery accounts use a Microsoft account, such as one tied to Azure Active Directory or a Microsoft ID, like an email account from outlook.com or hotmail.com. For my account, I am using my jeffbrown.tech domain that is tied to my Office 365 tenant and Azure AD tenant.
Next, I need to generate an API key that allows publishing packages to my account. API keys can be scoped to specific packages and have different privileges such as pushing new items and versions or only pushing new versions. These API Keys also have expiration dates.
To generate a key, select your username in the upper right and choose API Keys.
Next, in the API Keys page, select the plus sign next to Create and the new API key form will display. From here I fill out the required information. The key I am creating will have permissions to push new packages and versions for any package associated with my account.
- Key Name: PushNewPackagesAndVersions
- Expires in: 365 days
- Select Scopes: Push new packages and package versions
- Glob Pattern: * (wildcard)
The Glob Pattern allows for scoping what package names the API Key applies to. Once all the options are configured, click Create.
Once the API Key is generated, the next part is very important:
Copy the API Key value as this is the only opportunity to do so!
If I were to navigate off this page, I would not be able to access this value again. At this page, I select the Copy option and save my API Key off into a safe place (for me, I’ve created an Azure Key Vault to store it).
The final prerequisite, of course, is I need something to publish! For this example, I wrote a module that uses Twilio’s API to send SMS messages, view sent SMS history, and verify phone numbers. I wrote this project as a way to get started working with APIs. You can view the project here in my GitHub:
Enough with these prerequisites, let’s move on to the remaining items.
Generating a Module Manifest
Before publishing to the gallery, I need to make a module manifest file. You might have seen these before if you’ve downloaded other modules, they end in a .psd1 file extension. The purpose of this manifest is to include additional information about the module, such as the author, other dependent modules, define system requirements, and what members to export from the module. The manifest file itself is a table of key:value pairs. The manifest file links to the module by naming the manifest the same as the module and storing them together in the module’s root directory.
Luckily I don’t have to write this module manifest from scratch. Included in PowerShell is the cmdlet New-ModuleManifest. The minimum parameters needed for the cmdlet are -Path to create the .psd1 file and -PassThru to send the output to the pipeline and to the file.
New-ModuleManifest -Path C:\Projects\twilio-powershell-module\twilio-powershell-module.psd1 -PassThru
Microsoft documents some recommendations on manifest elements that are important to fill out before publishing to the gallery. These items include:
- Script or Module Name: this comes from the name of the .ps1 script or the .psd1 for a module.
- Version: this is a required field and follows the SemVer guidelines of three numerica blocks separated by periods (e.g. 1.0.12).
– The first number represents major or breaking changes between versions
– The second number represents feature-level changes (new cmdlets)
– The third number represents non-breaking changes (new parameters, updated samples).
- Author: this is a required field
- Description: this is a required field
- ProjectURI: strongly recommended to include a link to a GitHub repo or similar location that describes the project
- Tags: strongly recommended to tag package based on compatibility with PSEditions and platforms. For mine, I’ve only testing with PowerShell 5.1, so I tagged my with **PSEdition_Desktop** and **Windows** as well as tags for Twilio and SMS for additional module discovery in the gallery.
I also included release notes and followed the pattern I founds in a .psd1 created by Kevin Marquette out in the gallery. I figured if there’s an example to follow, it would be Kevin’s. Check out the full manifest in my GitHub repository:
Next I can test the manifest and make sure everything is ready to go using the Test-ModuleManifest cmdlet. I point it to my .psd1 file and make sure no errors occur.
My next step is to perform some code analysis using PSScriptAnalyzer. This is a module that can be downloaded from the gallery using the command Install-Module -Name PSScriptAnalyzer.
Use the Invoke-ScriptAnalyzer cmdlet and point it to the module to run analysis, like this:
Invoke-ScriptAnalyzer -Path C:\Projects\twilio-powershell-module\twilio-powershell-module.psm1
I had a few Information warnings about whitespace that I attempted to clean up as well as some Warnings regarding some of my function names. Two functions use the Set verb, so it suggested adding ShouldProcess to them. This is something I did not implement but will consider for a future version. My Get-TwilioAccountPhoneNumber function returns a string but does not declare this in OutputType attribute, and finally my noun for Test-TwilioCredentials is plural and suggests it should be singular; however, I’m going to let this one slide as I don’t believe I’ve ever heard credentials referred to as “credential”. There were no errors, so I’m going to call it good for now.
Adding a License
Following another example from Kevin, I added a license file to my project. Using the example found on the MIT License on Wikipedia, I customized with the current year and my name. I added this to my project folder and pushed to my repo in GitHub.
Publishing the Module
Finally, I think I have everything ready to publish the module using Publish-Module cmdlet. Microsoft’s documentation recommends running this using -WhatIf and -Verbose to identify and remediate any issues before attempting to publish for real.
First, I take my API key and save it to a variable named $apiKey. Next, I run the Publish-Module cmdlet pointing to my folder containing my module and manifest and specify my API key.
Publish-Module -Path "C:\Projects\twilio-powershell-module\" -NuGetApiKey $apiKey -WhatIf -Verbose
And the result!
I’m sharing my error in hopes it saves someone else in the future. When I created the manifest file, it auto-generates a lot of information and also auto-comments a lot of it. While I had specified the author and description information, I forgot remove the hashtag that was commenting out my description. It took me about 20 minutes to realize the error after multiple attempts and Internet searches to figure out what was missing.
After correcting my manifest file, I tried again:
No warnings, no errors, everything looks good! Time to remove -WhatIf and publish for real:
Note: the aka.ms short link does not redirect to the correct page.
It looks like my version of PowerShellGet is out of date. Initially I tried to run Update-Module -Name PowerShellGet but it returned an error that this version of PowerShellGet was not installed using Install-Module so it couldn’t be updated. Next I tried Install-Module -Name PowerShellGet -Force to forcefully install the module over any existing ones. After this, I had to restart PowerShell to load the updated version of the module.
Alright, let’s try to publish again for real:
And it’s good! After waiting a few minutes and refreshing my account page, I see the package available:
Also navigating out to the public page, I can see where it pulls information from the manifest regarding the version number, copyright, author, and license information:
Overall this was a fun learning experience. While it may seem intimidating to publish something publicly like this, I’m glad to have gone through the process. Writing a module or script knowing it’s going to be viewed by others makes me think more about how to make it easier to use. Also by putting it out there publicly, I can get some feedback on how to make it better or implement things in a different way.
Looking forward I want to take this to the next level by seeing if I can automate the process of publishing the module using Azure DevOps pipelines (the reason why I stored my API key in an Azure Key Vault). It will trigger based off committing to the master branch in my GitHub repository and perform some of the same tests I did in this post manually.