---
title: "Build Azure DevOps Pipelines for PowerShell Modules"
description: "Create an Azure DevOps pipeline for a PowerShell module and make modules first-class citizens. Enhance your automation skills and IT expertise."
canonical: "https://adamtheautomator.com/azure-devops-powershell-module/"
---

# Build Azure DevOps Pipelines for PowerShell Modules

> Create an Azure DevOps pipeline for a PowerShell module and make modules first-class citizens. Enhance your automation skills and IT expertise.

Source: https://adamtheautomator.com/azure-devops-powershell-module/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Build Azure DevOps Pipelines for PowerShell Modules](https://adamtheautomator.com/wp-content/uploads/2020/02/cat-2934720_1280.jpg)

# Build Azure DevOps Pipelines for PowerShell Modules

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)4 February 202011 min. read

Categories: [Cloud](/category/cloud/), [DevOps](/category/devops/)

Tags:[Azure DevOps](/tag/azure-devops/)[PowerShell](/tag/powershell/)

Table of Contents

*   [The Azure DevOps Pipeline for a PowerShell Module Project Overview](#project-overview)
*   [Prerequisites](#prerequisites)
*   [Importing the Sample GitHub Repo](#importing-the-sample-github-repo)
*   [PowerShell Repo Overview](#powershell-project-overview)
*   [The PowerShell Module Script and Functions](#the-powershell-module-script-and-functions)
*   [The Module Manifest](#the-module-manifest)
*   [Pester Tests](#pester-tests)
*   [Build Script](#build-script)
*   [Azure DevOps Project Quick Setup](#azure-devops-project-quick-setup)
*   [Pipeline Setup](#pipeline-setup)
*   [YAML Pipeline Explanation](#yaml-pipeline-explanation)
*   [The Trigger](#the-trigger)
*   [The Name](#the-name)
*   [Variables](#variables)
*   [The Pool](#the-pool)
*   [Stages](#stages)
*   [Build](#build)
*   [Test](#test)
*   [Deploy](#deploy)
*   [Summary](#summary)

You can build just about anything with PowerShell from teeny scripts to multi-hundred-line behemoths. But once you grow tired of creating PS1 script after PS1 script, you tend to start thinking of reusable tools via modules. Let’s see how to create an Azure DevOps pipeline for a PowerShell module in this step-by-step guide!

To create a PowerShell module, open up a text editor, and save it as a PSM1 file. Done. You’ve created a module! But only if it were that easy. Building a professional, reusable module you can use in production requires a software development approach. Brandon Olin provides a good example in his eBook [Building PowerShell Modules](https://leanpub.com/building-powershell-modules).

Building a “professional” PowerShell module means treating the module like C#, Java, or Python code. You should apply all of the same disciplines to a module as a developer would to a production software application. How do you do that? One way is by building an Azure DevOps pipeline for a PowerShell module.

Related: [How to Build an Azure Pipeline (Build/Release) from Scratch](https://adamtheautomator.com/azure-pipelines/)

In this article, you’re going to learn how to create an [Azure DevOps pipeline](https://azure.microsoft.com/en-us/services/devops/pipelines/) for a PowerShell module. You’ll see an example of how to squeeze a ton of value out of an automated build and test scenario (deployment to follow)!

> _But wait, there’s more! This article is the first part in a two-part series. In the [next article](https://adamtheautomator.com/azure-devops-add-nuget-feed/), you will learn how to take your PowerShell module pipeline and integrate automated deployments to a NuGet feed._

## The Azure DevOps Pipeline for a PowerShell Module Project Overview

This article is going to be broken down as a tutorial. You are meant to follow the article from top to bottom. By the time you’re done, you will have a complete [Azure DevOps pipeline](https://adamtheautomator.com/azure-pipelines/) created based on an example PowerShell module.

This article will be broken down into four rough sections including:

1.  Setting up the demo environment – In this section, you will get the sample GitHub repo set up.
2.  Example PowerShell module overview – In this section, you will get an overview of a simple PowerShell module and associated artifacts stored in a GitHub repo. What the module does is unimportant. The module explained here will simply be a placeholder for you to learn how to insert _your_ custom module.
3.  Creating an Azure DevOps project – In this brief section, you will see a series of commands that expedite setting up an Azure DevOps project for this tutorial.
4.  Building the pipeline – In this final section, you will learn an example of how to build a YAML-based Azure DevOps pipeline which versions the module and runs Pester tests.

> _You’ll be building the pipeline using the [Multi-Stage Pipeline UI experience](https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/multi-stage-pipelines-experience?view=azure-devops). As of this writing, this feature is in Preview._

## Prerequisites

You’re going to learn a lot but you’re also expected to come to the table with a few things. If you plan to follow along, be sure you have the following:

*   An Azure subscription
*   A GitHub account
*   An Azure DevOps organization – Check out the [Microsoft QuickStart guide](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/create-organization?view=azure-devops#create-an-organization) for instructions on how to do this. In this article, you’ll be working on a project called _PowerShellModuleProject._
*   [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) installed – You will _not_ need this if you already have an Azure DevOps project created.
*   The [Pester Azure DevOps extension](https://marketplace.visualstudio.com/items?itemName=Pester.PesterRunner) installed

> _Warning: The actions you’re about to perform may cost real money unless you have some Azure credit. Refer to the [Azure DevOps](https://azure.microsoft.com/en-us/pricing/details/devops/azure-devops-services/) pricing page for more information._

## Importing the Sample GitHub Repo

If you already have a PowerShell module in a GitHub repository, you can use that. But, if you don’t already have a repo created, you can use the one built for this tutorial. To use the [included sample repo](https://github.com/adbertram/PowerShellModuleProject), you first need to make a copy of your own or _clone_ it. One way to clone a GitHub repo is to “import” the code to a blank repo.

From your GitHub account, [create a new repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository). You should see a page like below. When you do, click on the **Import code** button.

![Creating New Repository from GitHub Account](https://adamtheautomator.com/wp-content/uploads/2020/06/1-5.png)

Creating New Repository from GitHub Account

You will then be presented with the page below. In the **Your old repository’s clone URL** field, paste in this tutorial’s sample PowerShell module repo URL _[https://github.com/adbertram/PowerShellModuleProject](https://github.com/adbertram/PowerShellModuleProject)_ and click on **Begin Import** as shown below.

![Importing to GitHub](https://adamtheautomator.com/wp-content/uploads/2020/06/2-5.png)

Importing to GitHub

After a short period of time, you should soon then see all of the contents of the Azure DevOps pipeline for a PowerShell module tutorial available in your repo.

![New Repository Prompt](https://adamtheautomator.com/wp-content/uploads/2020/06/3-5.png)

New Repository Prompt

## PowerShell Repo Overview

At this point, you should have your own copy of the sample GitHub repo with a file structure looking like the below. Some of these files may look familiar like a PSM1 or PSD1 file but some may not. If you going to talk PowerShell and DevOps, you better have a GitHub repo!

![Powershell Project Overview in GitHub](https://adamtheautomator.com/wp-content/uploads/2020/06/4-5.png)

Powershell Project Overview in GitHub

Related: [Understanding and Building PowerShell Modules](https://adamtheautomator.com/powershell-modules/)

In this GitHub repo, you’ll find the following items:

*   _PowerShellModuleProject.psm1_ – The PSM1 file containing code to export all functions in the module
*   _PowerShellModuleProject.psd1_ – The module manifest
*   _PowerShellModuleProject.Tests.ps1_ – A set of sample Pester tests
*   A _public_ folder containing PS1 scripts with a single function of the same name that will be exported
*   A _private_ folder containing PS1 script with a single function that will not be exported
*   A _build\_scripts_ folder that contains the YAML pipeline and all of the PowerShell scripts that Azure Pipelines will be executing.

### The PowerShell Module Script and Functions

Exporting functions in a module can be done a few different ways. A common way to expose functions is to include them all in the PSM1 file. This works for small modules but if you’ve ever found yourself wrangling dozens of functions, it becomes unwieldy.

To keep module functions more manageable, I like to create two folders called _public_ and _private_ containing one script per function in each folder. Then, use the PSM1 file as the script to dot source all of the functions and export all of the public functions in the _public_ folder.

You can see what the _PowerShellModuleProject.psm1_ file looks like below.

```powershell
Set-StrictMode -Version Latest
# Get public and private function definition files.

$Public = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue)

$Private = @(Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue)

# Dot source the files.
foreach ($import in @($Public + $Private)) {
    try {
        Write-Verbose "Importing $($import.FullName)"        . $import.FullName
    } catch {
        Write-Error "Failed to import function $($import.FullName): $_"
    }
}

## Export all of the public functions making them available to the user
foreach ($file in $Public) {
    Export-ModuleMember -Function $file.BaseName
}
```

### The Module Manifest

You should always include a module manifest with every PowerShell module project. Although the below example is not using all of the available attributes, it hits the main ones.

Notice the `ModuleVersion` and `FunctionsToExport` hashtable keys. These two keys both have placeholders while stored in GitHub. The placeholders exist because you’ll be managing an Azure DevOps build for this PowerShell module. The build will be running a build script to replace these values at build-time.

```powershell
@{
    RootModule        = 'PowerShellModuleProject.psm1'
    ModuleVersion     = '<ModuleVersion>'
    GUID              = '73a14fd2-886e-43a6-a203-e53b2152cd5b'
    Author            = 'Adam Bertram'
    CompanyName       = 'Adam the Automator, LLC.'
    PowerShellVersion = '6.0'
    FunctionsToExport = @('<FunctionsToExport>')
}
```

### Pester Tests

The Pester test script (_PowerShellModuleProject.Tests.ps1_) included with this tutorial is nothing fancy. Tests are a critical component to any Azure DevOps pipeline for a PowerShell module. This test script exists to include some sample tests for the build process. Do _not_ just use these two tests and consider your module fully-tested! Read [_The Pester Book_](https://leanpub.com/pesterbook/) to learn how to write real tests.

The below test suites ensures that:

*   the module will import successfully
*   the module has an associated manifest
*   the module passes all default code-linting rules defined by [PSScriptAnalyzer](https://www.interfacett.com/videos/use-powershell-script-analyzer-clean-code/)

```powershell
Install-Module -Name PSScriptAnalyzer -Force

describe 'Module-level tests' {
    it 'the module imports successfully' {
        { Import-Module "$PSScriptRoot\PowerShellModuleProject.psm1" -ErrorAction Stop } | should -not throw
    }

    it 'the module has an associated manifest' {
        Test-Path "$PSScriptRoot\PowerShellModuleProject.psd1" | should -Be $true
    }

    it 'passes all default PSScriptAnalyzer rules' {
        Invoke-ScriptAnalyzer -Path "$PSScriptRoot\PowerShellModuleProject.psm1" | should -BeNullOrEmpty
    }
}
```

### Build Script

Finally, we have the build script. The PowerShell project you’ll be creating an automated build for is simple, a single build script below will do most of the heavy lifting.

You can read the comments with the code to understand what each step does. The build script is updating the module manifest’s version and dynamically adding all exportable functions.

```powershell
$buildVersion = $env:BUILDVER
$moduleName = 'PowerShellModuleProject'
$manifestPath = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath "$moduleName.psd1"

## Update build version in manifest
$manifestContent = Get-Content -Path $manifestPath -Raw
$manifestContent = $manifestContent -replace '', $buildVersion

## Find all of the public functions
$publicFuncFolderPath = Join-Path -Path $PSScriptRoot -ChildPath 'public'
if ((Test-Path -Path $publicFuncFolderPath) -and ($publicFunctionNames = Get-ChildItem -Path $publicFuncFolderPath -Filter '*.ps1' | Select-Object -ExpandProperty BaseName)) {     $funcStrings = "'$($publicFunctionNames -join "','")'"
} else {
    $funcStrings = $null
}
## Add all public functions to FunctionsToExport attribute
$manifestContent = $manifestContent -replace "''", $funcStrings
$manifestContent | Set-Content -Path $manifestPath
```

## Azure DevOps Project Quick Setup

You should already have an Azure DevOps organization. If so, you now need a project for your Azure DevOps pipeline for a PowerShell module. You can create a project in many different ways. My favorite is to use the Azure CLI. Below you’ll find the necessary commands to build a project from scratch.

Open up a PowerShell console and run the below commands replacing the variables with your own subscription (required).

```powershell
$subscriptionName = '<your subscription name>'
$azDoOrgName = '<your Azure DevOps org name>'
$projectName = 'PowerShellModuleProject'
az login
az account set --subscription $subscriptionName
az extension add --name azure-devops
az devops configure --defaults organization=https://dev.azure.com/$azDoOrgName
az devops project create --name $projectName
az devops configure --defaults project=$projectName
```

> _If you’d like an explanation of each command, be sure to check out [Building an Azure DevOps Pipeline \[Soup to Nuts\]](https://adamtheautomator.com/azure-devops/). The first part of this article covers building an Azure DevOps project using the Azure CLI._

## Pipeline Setup

Once the project is created, it’s time to create the pipeline. The pipeline contains all of the instructions necessary to run the build, testing and release processes. If you’re not familiar with creating a pipeline, be sure to check out the [Azure DevOps documentation](https://docs.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=java%2Ctfs-2018-2%2Cbrowser) or the [_Creating a Pipeline_ section of the _Building an Azure DevOps Pipeline \[Soup to Nuts\]_ article](https://adamtheautomator.com/azure-devops/#creating-the-pipeline).

The sample PowerShellModuleProject GitHub repo already has a YAML pipeline created for you. When creating the pipeline in Azure DevOps and get to the **Configure** section, choose **Existing Azure Pipelines YAML file** as shown below.

![PowerShellModuleProject GitHub Repo](https://adamtheautomator.com/wp-content/uploads/2020/06/5-6.png)

PowerShellModuleProject GitHub Repo

The YAML pipeline is located in the _build\_scripts_ folder. Azure Pipelines should already recognize this and provide you an option to choose the path in the **Path** dropdown as shown below. Once you’ve selected the path, click **Continue** to review the pipeline.

![YAML Pipeline](https://adamtheautomator.com/wp-content/uploads/2020/06/6-6.png)

YAML Pipeline

On the **Review** section, click on **Run** to start the pipeline. Clicking **Run** will create and then start the pipeline.

![Starting YAML Pipeline](https://adamtheautomator.com/wp-content/uploads/2020/06/7-5-1024x132.png)

Starting YAML Pipeline

Once started, you should then see the pipeline jumping through each of the three stages it was built with.

![Azure Pipeline for a Powershell](https://adamtheautomator.com/wp-content/uploads/2020/06/8-4-1024x447.png)

Azure Pipeline for a Powershell

You’ve now created an Azure Pipeline for a PowerShell project! But wait…what exactly did that do?

## YAML Pipeline Explanation

Azure Pipelines uses YAML to define all steps in a pipeline (preview as of this writing). In this sample project, the pipeline was stored in the GitHub repo under the _build\_scripts_ folder and called _[azure-pipelines.yml](https://github.com/adbertram/PowerShellModuleProject/blob/master/build_scripts/azure-pipelines.yml)_.

The example pipeline in the GitHub repo isn’t the simplest yet definitely isn’t the most complicated pipeline you can build. The steps within are dependent on what your module does and what functions are within. However, it should provide an example of what’s possible when creating an Azure DevOps pipeline for a PowerShell module.

Rather than explaining each item individually, let’s cover what this pipeline does on an overview level. If you’d like more information on specific elements, be sure to start out with the [Microsoft YAML reference](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema). The YAML reference set of articles goes into detail about what purpose each element in the pipeline does.

### The Trigger

The trigger is defined at the top. Choosing `master` here tells the pipeline to run as soon as a Git commit is detected. This trigger can be changed but is the default.

```yaml
trigger:
  - master
```

### The Name

The name is an optional attribute that dictates what you see when the pipeline runs among other places.

```yaml
name: 'PowerShell Module Project'
```

![Naming the Pipeline](https://adamtheautomator.com/wp-content/uploads/2020/06/9-2.png)

Naming the Pipeline

## Variables

Recall that `ModuleVersion = '<ModuleVersion>'` line in the module manifest.  The module version is a placeholder to be defined at build time. You need to ensure this placeholder gets replaced by an actual version at build time.

One way to do that is to use [semantic versioning](https://www.geeksforgeeks.org/introduction-semantic-versioning/). Semantic versioning is a common method to assign a version to a build artifact. In the YAML syntax below, you can see a major, minor, and patch number is defined. As-is, the `major` and `minor` numbers are static `0` numbers while the `patch` variable increments by one for every build that’s performed in this Azure DevOps pipeline for a PowerShell module.

All of these variables are then brought together in the form `[major].[minor].[patch]` via the `buildVer` variable. When the build runs, the `buildVer` variable will be 0.0.1, 0.0.2, 0.0.3 and so on.

```yaml
variables:
  major: 0
  minor: 0
  patch: $(Build.BuildID)
  buildVer: $(major).$(minor).$(Build.BuildID)
```

The `buildVer` variable comes into play when the already-mentioned build script runs. You can see in the below snippet from the build script, the PowerShell script is reading the `buildVer` pipeline variable and inserting it into the module manifest.

```powershell
$buildVersion = $env:BUILDVER
$manifestContent = $manifestContent -replace '<ModuleVersion>', $buildVersion
```

Related:[Understanding Azure DevOps Variables \[Complete Guide\].](https://adamtheautomator.com/azure-devops-variables/)

## The Pool

Continuing down the Azure DevOps pipeline for a PowerShell module, you’ll then come across the `pool`. The pool is what defines the VM image the build agent will run on. Since you don’t need anything fancy here, the default Ubuntu build agent works just fine.

```yaml
pool:
  vmImage: "ubuntu-latest"
```

## Stages

We finally come to the `stages`. In this section is where the real magic happens. You have a lot of options here but for this tutorial, you have two common stages – `Build`, and `Test` . The `Deploy` stage will come in the next article.

Since the pipeline isn’t doing any fancy, each stage has an optional `job` with the same name.

### Build

The `Build` stage is where the PowerShell script is executed. You can see that the [PowerShell task](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/powershell?view=azure-devops) is executed finding the _build.ps1_ script in the _build\_scripts_ folder.

```yaml
- stage: Build
  jobs:
  - job: Build
    steps:
    - task: PowerShell@2
      inputs:
        filePath: '$(System.DefaultWorkingDirectory)/build_scripts/build.ps1'
```

### Test

Next, you have the `Test` stage that’s executing the Pester Test Runner task which is running all tests in the _PowerShellModuleProject.Tests.ps1_ script and returns all test results to the _PowerShellModuleProject.Tests.xml_ file. Once complete, the pipeline is publishing the test results which will show up on the pipeline’s test result page.

Almost done with your Azure DevOps pipeline for a PowerShell module!

```powershell
Install-Module -Name PSScriptAnalyzer -Force

describe 'Module-level tests' {

    it 'the module imports successfully' {
        { Import-Module -Name "$PSScriptRoot\PowerShellModuleProject.psm1" -ErrorAction Stop } | should not throw
    }

    it 'the module has an associated manifest' {
        Test-Path "$PSScriptRoot\PowerShellModuleProject.psd1" | should -Be $true
    }

    it 'passes all default PSScriptAnalyzer rules' {
        Invoke-ScriptAnalyzer -Path "$PSScriptRoot\PowerShellModuleProject.psm1" | should -BeNullOrEmpty
    }

}
```

![Pester Test Runner Task](https://adamtheautomator.com/wp-content/uploads/2020/06/Untitled-98.png)

Pester Test Runner Task

### Deploy

Once the `test` stage is over, it’s onto the `deploy` stage. To learn how you can deploy this project to a NuGet feed using [Azure Artifacts](https://azure.microsoft.com/en-us/services/devops/artifacts/), check out [Publishing NuGet Packages to Azure Artifacts Automatically](https://adamtheautomator.com/azure-devops-add-nuget-feed/)!

## Summary

In this tutorial, you learned one example of how to build an automated Azure DevOps pipeline for a PowerShell module. You should now have the basic knowledge to get more professional with your PowerShell modules and treat them as real code!

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fazure-devops-powershell-module%2F&text=Build%20Azure%20DevOps%20Pipelines%20for%20PowerShell%20Modules)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fazure-devops-powershell-module%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fazure-devops-powershell-module%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2020/04/jared-rice-O6DUoIl6NWA-unsplash.jpg)

### [Optimize ARM Output Variables in Azure DevOps Pipelines](/arm-output/)

Learn how to manage ARM output variables in Azure DevOps pipelines using PowerShell for efficient and streamlined workflows.

![](https://adamtheautomator.com/wp-content/uploads/2020/02/braydon-anderson-wOHH-NUTvVc-unsplash-scaled.jpg)

### [Deploy Azure DSC Configurations with ARM Templates](/azure-dsc-2/)

Master the deployment of Azure DSC configurations to Azure VMs using ARM templates. Boost your cloud management skills and IT expertise.

![](https://adamtheautomator.com/wp-content/uploads/2020/02/horses-1396651_1280.jpg)

### [Publish to Azure Artifacts NuGet Feed with Azure DevOps](/azure-devops-add-nuget-feed/)

Learn to package and publish Azure DevOps pipeline build artifacts to an Azure Artifacts NuGet feed. Enhance your IT skills and workflow.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
