Automation,  Azure,  Powershell

Publish module to Powershell Gallery from Azure Pipelines

Now that I have my module on Github and built the module on Azure Pipelines, I want to publish it to the Powershell Gallery. The first step it of course to create a free account; once you have the account you can head to your profile, click on API Keys and then Create:

create powershell gallery api key
create powershell gallery api key

The Create dialog allows to choose the key name, the expiration and very important, the scope (what permission the key will have on the Gallery) and which packages the key is allowed to control through Global Pattern. In my case I want the key to expire every year, I want the key to grant permission to publish new packages and update existing ones and I want this key to be used only for LSE modules: notice I used LSE* as Global Pattern, this way this same key will allow me to publish and manage new packages as long as their name begins with LSE.

create powershell gallery api key
create powershell gallery api key

Azure Pipelines allows to securely store secrets (passwords and keys) as variables, if you want to do so you can use the Variables tab in your Pipeline then click the padlock icon:

azure pipeline variable as secret
azure pipeline variable as secret

Anyway I prefer to store the key in Azure KeyVault since I may decide to use it outside the Pipeline, in a script I can run manually for example. After adding the secret to KeyVault we need to authorize Azure Pipelines to retrieve the key: in Azure DevOps go to your project settings, select Service Connections under Pipelines, create a new service connection of type Azure Resource Manager

azure resource manager service connection
azure resource manager service connection

I am using a Service Principal created through the Service Connection dialog (you’ll get prompted to authorize Azure Pipelines to access your Azure Subscription)

azure resource manager service connection
azure resource manager service connection

I also have to grant the new Service Principal read access to Secrets under Access Policy in KeyVault.

Now I need to modify my Build Pipeline and add a Publish Pipeline Artifact task. It takes only two arguments: artifactName (LSECosmos on my example) and targetPath (this is the path on the Pipeline Agent disk where to dump the files produced by the Build):

- task: PublishPipelineArtifact@0
  inputs:
    artifactName: 'LSECosmos'
    targetPath: 'LSECosmos'

Now we can start to work on the Release pipeline: let’s start with an Empty Job.

First thing, let’s add an Artifact and configure it to use the LSECosmos we just added as last step of the Build Pipeline. I’m going to use the Build type:

build artifact
build artifact

I also want this Release to be triggered every time a Build succeeds, to do so let’s click on the round lightning bolt button and enable Continuous Deployment Integration

enable continuous deployment trigger
enable continuous deployment trigger

Then I need a new Stage and a number of Tasks to actually execute the steps needed to publish the module. My first step is to retrieve the Powershell Gallery Key from KeyVault:

azure key vault task
azure key vault task

Here I just need to use the appropriate Azure Subscription connection (you may have to authenticate and authorize Azure Pipelines to access the Subscription and KeyVault) and specify the KeyVault and Secret name to use:

download secrets from keyvault
download secrets from keyvault

It is not shown in the screenshot but under Output Variables I am storing the Key in a variable called LSECosmosPSGalleryKey so that I can use it in the next Task. In a typical classic scenario I would need a Download Pipeline Artifact task to copy the artifacts (files) published by the Release Pipeline to use them with the Build Pipeline:

add download pipeline artifact
add download pipeline artifact
download pipeline artifact
download pipeline artifact

Anyway using the new Pipeline Artifacts instead (still in preview) the copy step is not needed; also, for large projects Pipeline Artifacts is faster than Build Artifacts (one more reason to upgrade to the new version).

The second (and in my case last) task is a very simple Powershell inline script, I need to make sure dependencies are in place (my module needs Az.Accounts and Az.Resources) and simply run Publish-Module -Path "$(System.ArtifactsDirectory)/_carlocardella.LSECosmos/LSECosmos" -NuGetApiKey "$(LSECosmosPSGalleryKey)"

publish to psgallery powershell task
publish to psgallery powershell task

In my Build Pipeline I decided to execute a script from my repo because I wanted to be able to execute the same build/script manually if needed; with the Release Pipeline though I am not following this approach because the script needs to use some Pipeline specific environment variables that would not exist outside of Azure Pipelines to I would not be able to manually run this script anyway. The module path I’m using ( "$(System.ArtifactsDirectory)/_carlocardella.LSECosmos/LSECosmos") comes from the default Release Pipeline download folder $(System.ArtifactsDirectory) plus the name of my Build Pipeline (_carlocardella.LSECosmos) plus the published Artifact name (LSECosmos), while the Gallery Key is the same output variable created in the KeyVault Task: $(LSECosmosPSGalleryKey).

To test the Release pipeline we can use the Create Release button:

create release
create release
create a new release
create a new release
deploy
deploy
release succeeded
release succeeded

Next time I’ll merge some new commit to master in my Github repo I will get a new Build, then the Release pipeline will kick in (assuming the Build is successful of course šŸ˜‰) and the new LSECosmos module version will be published to the Powershell Gallery

lsecosmos powershell gallery
lsecosmos powershell gallery

TheĀ greatestĀ mistakeĀ you can make is to be continually fearing you will make one. – Elbert Hubbard

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.