Creating a NuGet Build Pipeline for Powershell Modules within Azure DevOps

Justin Rice
10 min readOct 26, 2018

--

Photo by Ash Edmonds on Unsplash

In my previous article, I showed you how to create a private Powershell repository for your PowerShell modules. I got good feedback on it, but there is something missing, let's try and skip some steps.

Gotta Run? Source Code

Previous article: HERE

Within Azure DevOps, there is the ability for us to choose from any number of DevOps processes to run when we push to our repository. We can just push to the repository using Git (something most of us should be familiar with. If you are not, here is a link) to create new packages and version control on those packages, eliminating the need for the NuGet CLI and NuGet commands in general.

The End Product.

Our final product will be a deployment pipeline that will take our PowerShell module (file structure below) and automatically test it, package it and then put it within Package Manager where other team members can download the packages and share.

Crucial Steps:

  • Creating a deploy script
  • Creating a test script
  • Configuring the pipeline
  • Test the Pipeline

Creating A Deploy Script.

This is the script that Azure DevOps will run during our deployment. We can have as many as we want, but it is a deploy script so one should suffice.

Create a deploy.ps1 file and populate it with this:

#Add a new line to the markdown file.
$date = Get-Date -Uformat "%D"
#Update the manifest file
$manifest = Import-PowerShellDataFile .\VSTS-TestModule.psd1
[version]$version = $Manifest.ModuleVersion
# Add one to the build of the version number
$NewVersion = "{0}.{1}.{2}" -f $Version.Major, $Version.Minor, ($Version.Build + 1)
# Update the manifest file
Update-ModuleManifest -Path .\VSTS-TestModule.psd1 -ModuleVersion $NewVersion
#Sleep Incase of update
Start-Sleep -Seconds 5
#Find the Nuspec File
$MonolithFile = ".\VSTS-TestModule.nuspec"
#Import the New PSD file
$newString = Import-PowerShellDataFile .\VSTS-TestModule.psd1
$xmlFile = New-Object xml
# Load the Nuspec file and modify it
$xmlFile.Load($MonolithFile)
$xmlFile.package.metadata.version = $newString.ModuleVersion
$xmlFile.package.metadata.releaseNotes = "Version $($newString.ModuleVersion) was modified by $($env:USERNAME) on $($date)"
$xmlFile.Save($MonolithFile)
# Update the Markdown file to have the version update
Add-Content -Path .\README.md -Value " **Version: $($newString.ModuleVersion)**"
Add-Content -Path .\README.md -Value " by: $($env:USERNAME) on $($date)"

The script does a crucial thing in which the version of the Powershell Manifest File and the Version number of the NuGet build have to match. The rest adds a Version Update message to the ReadMe.md file.

Creating A Test Script

Pester is Powershell’s best friend when it comes to testing and building development pipelines to keep yourself and other developers on your team honest with the code they push. We have all had those days when someone goes and makes one fix and the rest of someone's day is ruined.

  • Install Pester as a module.
Install-Module -Name Pester -Force -SkipPublisherCheck
  • Create a Fixture for it
New-Fixture deploy Clean

Pester creates a file for the functions and also for the tests themselves. We will create a single function that checks to see if the deploy file is still where it is supposed to be.

  • Add this to the display/Deploy.ps1 file:
function Deploy($script) {
if(-not(Test-Path -Path $script)){
return $false
}else{
return $true
}
}

Pester also creates a Deploy.Tests.ps1 script. It gives us a function right off the creation, but we can delete that and add our own. Keep the here and sut variable, but substitute the one they give you with this:

$script = "$here\..\deploy.ps1"Describe "Deploy" {
It "Verifies if the deploy file is there." {
Deploy -Script $script | Should -Be $true
}
}

From the projects root folder, run:

Invoke-Pester. 

A passing test looks something like this:

Configuring Access

Azure DevOps automatically gives you a user that can be used as an account to run your DevOps builds. We will need to give the user the correct rights to make the changes we need it to make.

  • Within VSTS go to “Manage Repositories” and click on the repository on the left that you want to use for this build.
  • Click on the “ Project Collection Build Service (YOUR TEAM)” user.
  • The user will need to have the following rights: Contribute, Create Branch, Read, Tag Creation.
  • Be sure to click “Save Changes”

Configuring the pipeline

Before we build the pipeline, it is important to note three key terms:

  • Build — a small set of processes used to complete a task
  • Releases — the product of successful build processes within a pipeline
  • Pipeline — a pipeline is a series of build processes to execute to get a final product.
  • Packages — reusable bundles of code that can be implemented, downloaded and used in projects by developers.
  • Trigger — a process that, when it occurs, begins another process. For the sake of our pipeline, we will have a trigger that will read to see if there are any pushes to branches before merging them to master.

What we are going to do is build a pipeline with a build process. It will contain 4 things.

  • Deploy Script — to update the package number and the version number of the manifest
  • Pester Test to make sure the code is functioning
  • Git/Command line script to push the branch to the master so we update the repo and the package.
  • Nuget Pack “compiles” the source folder to go the package feed
  • Nuget Push- takes the compiled package and sends it to our VSTS package repo

Starting the Pipeline

  1. Login to Azure DevOps and find the project you wish to create a pipeline for. If you pushed to the repository recently, it will be in your “Recent” list. If you hover over it, “Pipelines” is an option.
  2. On the left-hand side, click “New” > “New Build Pipeline”
  3. Select the source of your repository.
  4. For this example, we are going to start with an empty build which is located at the top. I have also included the YAML document in the source code that you can select “YAML Build” and then use that as the build process.
  5. Before we build the tasks, go to “Triggers” on the left next to “Variables.” This is where we can configure the events that need to happen for the build process to start. I have mine set to “Exclude Master” and “Include DevBranch.” I have also used a file specific trigger.
  6. Go back to the “Tasks” tab and click on “Agent Build 1.” We can name this build something different. I named mine “Build, Update, Pack, Push.” A hosted agent is okay with most builds. These are any agent within Azure that will be doing the processing. You can also set it to be an agent within your Azure environment.
  7. The key thing here is to make sure that the “Allow Scripts to Access the OAuth Token” is checked. This makes sure that the build tasks that we run will have access to the repo.

8. Click on the “Get-Sources step and make sure that the “Clean” option is set to true. This clears the cache from the build process so nothing gets left behind. Once we have these configurations, we are good to go and build the Build Process!

The Build Process

Deploy Script

The first part is a deploy script. We will add this by selecting the Powershell Script option from the “Add Tasks.”

This Powershell task will run our deploy script. We can either set it to take the deploy.ps1 script from our repository or we can just put it in line. Whichever you choose, be sure this script runs. The deploy script is above.

Pester Test

This will be our Pester testing for us. Pester does not natively come with your Azure DevOps subscription, you do have to add it. Go ahead and add it to your organization and then add it to the pipeline.

We need to make one quick fix here. On the Pester task, there is an option called “Scripts Folder or Scripts” we need to add our folder so it looks like this.

Command Line Script

Add a command line script task to your build. This one will be built such that the repository is updated with the new version as well as the NuGet package.

Here is the Git script:

git config --global user.email "VssAdministrator@factoryvm-az459.(biib)"
git config --global user.name "VSTS Admin"
ECHO SOURCE BRANCH IS %BUILD_SOURCEBRANCH%
IF %BUILD_SOURCEBRANCH% == refs/heads/master (
ECHO Building master branch so no merge is needed.
EXIT
)
SET sourceBranch=origin/%BUILD_SOURCEBRANCH:refs/heads/=%
ECHO ADDING CHANGES FROM PIPELINE
git branch %sourceBranch%
ECHO ADDING MODIFIED FILES
git add --all
ECHO CREATING COMMIT
git commit -m "Changes made from Pipeline"
ECHO CHECKING MASTER
git reflog
git checkout -b master HEAD@{0}
git pull --strategy recursive -X theirs origin %BUILD_SOURCEBRANCH%
git pull --strategy recursive -X ours origin master
git add --all
git commit -m "Added from VSTS Pipeline"
git push origin master

The script goes and updates the branch and then updates the master branch using strategies to merge correctly.

Nuget Pack

We need to add two NuGet tasks. One to pack and one to ship. We can find these by clicking the plus button next to our “Agent 1 Build” and searching “NuGet”. The first result is the one we want.

  • On the “Command” drop-down, select “pack”. This will automatically update the name of the command for you.
  • Under the “Path to csproj or nuspec file(s) to pack” option, select the .nuspec file within your repository.
  • Under “Pack Options” be sure that “Auto Versioning” is switched to off. Our deployment script is taking care of the versioning.

NuGet Push

Add another “Nuget” task.

From the “Command” drop-down, select “push”. This will update the name of the task.

Path to NuGet package(s) to publish” should be set to: *.nupkg

The final step is to select the Target feed location. If you have Package Manager rights, you will see your feeds. You can also set this to go to a public NuGet package service.

We now have a completed pipeline! At the top of our build is “Save and Queue” be sure to click this, add a comment and queue the build. If you used the branch idea from above, make sure the build is building from that branch. Run the build and give yourself a high five. Dev and module creation life just got a lot easier. Yours should look something like this:

Testing the Pipeline

Wow cool! We now have a build pipeline that will build our Powershell repository as a NuGet package so other developers can use our tools and all we have to do is run some git commands and Azure DevOps will take care of the rest. Now let’s test it!

  1. Open a Powershell session as an Administrator.
  2. Run the following command within Powershell:
Register-PSRepository -Name "PowershellVSTS" -SourceLocation "https://MyOrganization.pkgs.visualstudio.com/_packaging/PowershellModules/nuget/v2" -PublishLocation "https://MyOrganization.pkgs.visualstudio.com/_packaging/PowershellModules/nuget/v2" -InstallationPolicy Trusted
  • You will notice above that the Publish and Source location both reference Version 2 of NuGet. At the time of writing this article, there were some discrepancies that would happen with Version 3 of NuGet.
  • The script above creates a new Powershell Repository named PowershellVSTS and sets the Publish and Source Location as the links to the NuGet feed. This link can also be found by clicking on “Connect to Feed” within the feeds page.

4. We can confirm that we now have a new repository by running:

Get-PSRepository

5. We now have a personal repository set up, but we will need credentials to be able to access it. It is private after all. Within Powershell, run the following:

$password = ConvertTo-SecureString 'YOUR PERSONAL ACCESS TOKEN FROM THE CREATING THE PIPELINE STEP' -AsPlainText -Force$credsVSTS = New-Object System.Management.Automation.PSCredential 'YOUR EMAIL FOR VSTS', $password

The following script takes in your Personal access token and then uses it within a PSCredential object which we will use to securely access our new feed.

6. Let’s see what we can install from this feed:

Find-Module * -Repository PowershellVSTS -Credential $credsVSTS

Wow, now we can see our Get-Hello module and install it!

Install-Module Get-Hello -Repository PowershellVSTS -Credential $credsVSTS
Get-Modules -ListAvailable Get-Hello

We now have a repository that pulls down NuGet packages with our Powershell Modules and scripts that we can have version control, access management and best of all, available to all our other developers!!

Conclusions and Questions

As with everything in dev, there are other ways to do this process:

  • Have the trigger be triggered by a change to a specific document.
  • Create a Powershell script that goes all the git commands that a developer might need to keep some form of uniformity.
function Update-VSTSModule($Message) {
git checkout master
git checkout -b $env:USERNAME
git add --all
git commit -m "$Message"
git push origin $env:USERNAME
}
  • Instead of having triggers, just have a scheduled build that goes through all the branches and if the branches pass, let them be a part of the build.

How you choose to make your team more productive is something that every development team should talk about and implement.

Happy Hacking

--

--

Justin Rice

Founder and CTO @ CloudHippie.com | DJ @ DJ White Rice | You May say I’m a dreamer, but I’m not.