If you’ve written a module or script that you feel others could benefit from 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.

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’d rather watch a video demonstration of this, check out my presentation at the Chicago PowerShell Conference available on YouTube.


To publish to the PowerShell Gallery, head over to https://www.powershellgallery.com and create an account. 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.

powershell gallery api keys
Navigating to the API Keys for your PowerShell Gallery account

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.

powershell gallery api key publish permissions
Configuring API key permissions

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).

powershell gallery api key
Viewing the generated API key

The final prerequisite, of course, is you 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 a module 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 `
powershell module manifest
Creating a PowerShell module manifest

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 found 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.

test powershell module manifest
Testing a module manifest file

Following Best Practices with PSScriptAnalyzer

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 it with the current year and my name. I added this to my project folder and pushed it to my GitHub repo.

Completing the Publish to the PowerShell Gallery

Finally, I think I have everything ready to publish the module to the PowerShell Gallery using the 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 `

And the result!

Module cannot be published because it is missing required metadata. Verify that the module manifest specifies Description and Author.

powershell gallery publish error

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 to 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:

powershell gallery publish success

No warnings, no errors, everything looks good! Time to remove -WhatIf and publish for real:

Failed to publish module. A client version ‘4.1.0’ or higher is required to be able to publish packages. Install the latest version of the NuGet client or install the latest version of PowerShellGet module using the instructions provided at https://aka.ms/installing-powershellget.

powershellget version error

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:

powershell gallery publish success

And it’s good! After waiting a few minutes and refreshing my account page, I see the package available:

view published module in powershell gallery

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:

published powershell module information page


Overall this was a fun learning experience. While it may seem intimidating to publish something publicly like this to the PowerShell Gallery, 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 on committing to the master branch in my GitHub repository and perform some of the same tests I did in this post manually.


Microsoft Docs: Creating and Publishing Packages

Microsoft Docs: PowerShellGallery Publishing Guidelines and Best Practices

Check out more of my PowerShell content here!