If you write PowerShell modules you may have faced this problem; You want to share these modules privately with all your company users and internal pipeline, but you don't grant access to external users.
Public gallery, like PowerShell gallery, is no longer an option. What can you use?
One of the responses is Azure DevOps. Azure DevOps not only has, git repositories, pipelines, and boards, but it also has an artefact manager, Azure Artefacts
There are several steps to use Azure Artefacts as a PowerShell modules repository.
First, you need an Azure DevOps organization and a project (go to https://dev.azure.com/).
On the left menu, select “Artifacts” and create a feed by clicking on the “Create Feed” button.
Give a name to the fee, ie ps-private-module and leave the default options, then click on Create.
You will also need to add the default build service as a contributor to the feed. Go to the feed settings (the little gear on the right) and then permission and add the build service as a contributor (its name should be your-project-name Build Service)
Now we can work on the module.
We need to create a module manifest
New-ModuleManifest -Path ./demomodule.psd1 -RootModule demomodule.psm1
We will need to put the two files into a demomodule folder in an Azure DevOps repository.
The next step is to package the module so it can be put in a PowerShell repository, public or private.
For that we use NuGet. NuGet (https://www.nuget.org/) is the .NET package manager used to share code and compiled code.
Open a shell and move to the demomodule folder then type
Nuget spec demomodule
It will create a NuGet package specification file, demomodule.nuspec in the folder. The nuspec file is an XML file.
<?xml version="1.0"?> <package > <metadata> <id>demomodule</id> <version>1.0.0</version> <authors>olivi</authors> <owners>olivi</owners> <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl> <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl> <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>Package description</description> <releaseNotes>Summary of changes made in this release of the package.</releaseNotes> <copyright>Copyright 2022</copyright> <tags>Tag1 Tag2</tags> <dependencies> <dependency id="SampleDependency" version="1.0" /> </dependencies> </metadata> </package>
You will need to change the URI of the license (your Azure Repos URI), and project (Azure board URI), you can remove the iconUrl (except if you have one) and remove the line and also the tag
The most important thing to take into account is the version, the version should be the same as what you have in the PowerShell module manifest.
The next step is to create the package and send it to Azure DevOps Artefact.
Let’s create a build folder and put a build.ps1 file. The purpose of this file will be to package the module as a nugget package and send it to Azure DevOps Artefacts.
Packaging the module as a nugget package is done by using the NuGet pack command in the module folder
Nuget pack demomodule.nuspec
This will create a nupkg file. Then will need to send this nupkg file to Artefact. For that NuGet source add and NuGet push will be used.
The first create a NuGet source in the local configuration using the NuGet API of Azure Artefact.
But we will need a username and password. We can use a Personal Access Token (PAT) but as we want to run the build task in an Azure DevOps pipeline we can use the system_accesstoken.
push-location -Path ./demomodule & nuget pack ./demomodule.nuspec $accessToken = $env:SYSTEM_ACCESSTOKEN [xml]$nugetFileData = Get-Content -Path ./demomodule.nuspec $moduleVersion = $nugetFileData.package.metadata.version nuget source add -Name "AZDevOpsPWSHModule" -Source "https://pkgs.dev.azure.com/AZ-OMDEMO/demo-psmodule-artifact/_packaging/ps-private-module/nuget/v3/index.json" -username $accessToken -password $accessToken nuget push -Source "AZDevOpsPWSHModule" -ApiKey AzureDevOpsService "./demomodule.$($moduleVersion).nupkg"
The first line set the location in the module folder. The second line package the module using the NuGet pack command. This will create a NuGet package called after the name of the module and the version found in the nuspec file. To get the version, the script parses the nuspec file for the package.metadata.version data.
Then the script creates a local source configuration with the URI of the Azure Artifact NuGet endpoint. You can find this URI by using “Connect to the feed” and selecting NuGet on the Azure DevOps Artifact page.
The last command sends the package to the Azure artifact feed.
As you can see, the SYSTEM_ACCESSTOKEN environment variable is used as the username and password for the NuGet source configuration. This variable is sent to the script by the pipeline configuration.
To run it inside an Azure pipeline, you will need a yaml pipeline definition.
trigger: - master pool: vmImage: ubuntu-latest stages: - stage: buildandpackage jobs: - job: buildandpackagejob steps: - task: PowerShell@2 inputs: filepath: '$(Build.SourcesDirectory)/build/build.ps1' pwsh: true failOnStderr: true env: SYSTEM_ACCESSTOKEN: $(System.AccessToken)
The first statement, trigger, enables the pipeline each time you push or make a pull request on the master branch.
The second bloc indicates that we will use an Ubuntu machine
The last bloc will execute the script with the help of the System Access Token to access Azure Artifact.
But how can we use Azure Factory as a private PowerShell registry?
You will need to create a Personal Access Token for the account that will install the module and you will need to use NuGet.
But as there is a bug in PowerShell (currently), you can’t use the v3 API and you will need to use the v2.
As we don’t want to store the PAT in the script, the PAT will be passed as an argument.
There is a need to force PowerShell to use TLSv3
To create the credential needed to login to the API, the PAT needs to be converted to a secure string. This secure string, along with the PAT will be used to create the credential
[CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $AzTocken ) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls3 $SecureToken = $AzTocken | convertTo-SecureString -AsPlainText $AzArtifactCredential = New-Object System.Management.Automation.PSCredential($AzTocken, $SecureToken) $AzArtifactFeedURI = "https://pkgs.dev.azure.com/<Organisation>/<Project>/_packaging/<FeedNqme>/nuget/v2" Register-PSRepository -name PSPrivateModule -SourceLocation $AzArtifactFeedURI -PublishLocation $AzArtifactFeedURI -InstallationPolicy Trusted -Credential $AzArtifactCredential install-module -Name <ModuleName> -Repository PSPrivateModule -Credential $AzArtifactCredential -Force
This is the startup point on how you can use Azure DevOps Artefact for your PowerShell Module.