Use an Azure Artifacts feed as a private PowerShell repository

Azure DevOps Services

Azure Artifacts provides a convenient solution for sharing PowerShell scripts. By using Azure Artifacts feeds, you can seamlessly publish your PowerShell modules from the command line and control access to them through your feed settings. This article guides you through setting up your Azure Artifacts feed as a private PowerShell repository to store and share your PowerShell modules.

In this article, you'll learn how to:

  • Create a Personal Access Token
  • Create a PowerShell module
  • Create a SecretStore vault and register a repository
  • Publish and consume packages from a feed

Prerequisites

Note

Azure Artifacts Credential Provider is not supported with PSResourceGet.

Create a personal access token

A personal access token acts as your digital identity and serves as an alternative password to authenticate you with Azure DevOps.

  1. Navigate to your Azure DevOps organization https://dev.azure.com/<ORGANIZATION_NAME>/

  2. Select the user settings icon, select Personal access tokens, and then select New Token.

  3. Enter a name for your PAT, set an Expiration date, select Custom defined, and then select Packaging > Read, write & manage.

  4. Select Create when you're done, and make sure you copy and store your PAT in a safe location.

    A screenshot that shows how to set up a new personal access token.

Create a PowerShell module

If you don't have your own module, follow the instructions in this section to create a sample PowerShell module. Otherwise, skip to the next step:

  1. Create a new folder PowerShell-Demo. Navigate into your folder and create a new file PowerShell-Demo.psm1.

  2. Paste the following script into your PowerShell-Demo.psm1 file:

    Function PowerShell-Demo{
        Write-Host "Hello World!"
    }
    
  3. Generate the module manifest by running the following command in your PowerShell-Demo directory:

    New-ModuleManifest -Path .\PowerShell-Demo.psd1
    
  4. Open your PowerShell-Demo.psd1 file and locate the RootModule variable. This setting specifies the main script file that PowerShell loads when the module is imported. Replace the empty string with the path to your PowerShell-Demo.psm1 file:

    RootModule = 'PowerShell-Demo.psm1'
    
  5. The FunctionsToExport section specifies which functions are accessible to users when they import your module. Include your PowerShell-Demo function:

    FunctionsToExport = @('PowerShell-Demo')
    
  6. Locate the FileList section, which lists the files included when packaging the module. Add the file you wish to package with your module:

    FileList = @('./PowerShell-Demo.psm1')
    

Register a repository

  1. Run the following command to create a credential object. Replace the placeholders with the correct information.

    $username = "<USER_NAME>"
    $patToken = "<PERSONAL_ACCESS_TOKEN>" | ConvertTo-SecureString -AsPlainText -Force
    
    $credentials = New-Object System.Management.Automation.PSCredential($username, $patToken)
    
  2. Ensure that SecretManagement and SecretStore are installed, then run the following command to create a vault and add a secret:

    Register-SecretVault -Name "MySecretVault" -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
    
    Set-Secret -Name "MyCredential" -Secret $credentials -Vault "MySecretVault"
    
    $CredentialInfo = [Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo]::new('MySecretVault', 'MyCredential')
    
  3. To verify if the vault and secret were successfully created, run the following command to list all your secrets:

    PS > Get-SecretInfo
    
    Name            Type         VaultName
    ----            ----         ---------
    MyCredential    PSCredential MySecretVault
    
    
  4. Run the following command to register your PowerShell repository. You can find the SourceLocation link by navigating to Artifacts > Connect to Feed > NuGet.exe, under the Project setup section > source URL.

    • Project-scoped feed:

      Register-PSResourceRepository -Name "PowershellPSResourceRepository" `
          -Uri "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v3/index.json" `
          -Trusted `
          -CredentialInfo $CredentialInfo
      
    • Organization-scoped feed:

      Register-PSResourceRepository -Name "PowershellPSResourceRepository" `
          -Uri "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/nuget/v3/index.json" `
          -Trusted `
          -CredentialInfo $CredentialInfo
      

    Tip

    Some versions of PowerShell may require starting a new session after running the Register-PSResourceRepository cmdlet to prevent encountering the Unable to resolve package source warning.

  5. To verify if the repository was successfully registered, run the following command to retrieve all registered repositories for the current user:

    Get-PSResourceRepository
    

Note

If you encounter the error: Response status code does not indicate success: 404 (Not Found)., make sure that your source URL points to nuget/v3/index.json instead of nuget/v2.

Publish a package

Run the following command to publish the package to your feed:

Publish-PSResource -Path <PACKAGE_PATH> -Repository <REPOSITORY_NAME> -ApiKey (Get-Secret <SECRET_NAME>) 

Example:

PS C:\AzureDevOps\Demos\PowerShellDemo> Publish-PSResource -Path .\scripts\ -Repository FabrikamFiberFeed -ApiKey (Get-Secret MyNewCredential) -verbose
VERBOSE: Performing the operation "Publish-PSResource" on target "Publish resource
'C:\AzureDevOps\Demos\PowerShellDemo\scripts\' from the machine".
VERBOSE: The newly created nuspec is:
C:\Users\xxxx\AppData\Local\Temp\xxxxxxxxx\PowerShell-Demo.nuspec
VERBOSE: credential successfully read from vault and set for repository: FabrikamFiberFeed
VERBOSE: Successfully packed the resource into a .nupkg
VERBOSE: Successfully published the resource to
'https://pkgs.dev.azure.com/ramiMSFTDevOps/DemoProject/_packaging/FabrikamFiberFeed/nuget/v3/index.json'
VERBOSE: Deleting temporary directory 'C:\Users\xxxx\AppData\Local\Temp\xxxxxxx'

A screenshot displaying the PowerShell demo package published to the feed.

Install a package

  1. To confirm if the module is available in your repository, use the following command to search for it:

    Find-PSResource -Name <RESOURCE_NAME> -Repository <REPOSITORY_NAME> -verbose
    
  2. Run the following command to install the latest stable version of your module:

    Install-PSResource <MODULE_NAME>
    

Tip

If you encounter the error: Exception calling "WriteObject"., start a new PowerShell window and run Get-SecretInfo. Enter your vault password before running Find-PSResource and Install-PSResource, as the SecretStore timeout period can expire. The default PasswordTimeout is 900 seconds, but you can modify this value as needed. See Use the SecretStore in automation for more details.

In this article, you'll learn how to:

  • Create a Personal Access Token
  • Create, package, and publish a PowerShell module
  • Connect to a feed as a PowerShell repository
  • Register and install a PowerShell module using Azure Pipelines

Prerequisites

Create a personal access token

A personal access token acts as your digital identity and serves as an alternative password to authenticate you with Azure DevOps.

  1. Navigate to your Azure DevOps organization https://dev.azure.com/<ORGANIZATION_NAME>/

  2. Select the user settings icon, select Personal access tokens, and then select New Token.

  3. Enter a name for your PAT, set an Expiration date, select Custom defined, and then select Packaging > Read, write & manage.

  4. Select Create when you're done, and make sure you copy and store your PAT in a safe location.

    A screenshot that shows how to set up a new personal access token.

Create a PowerShell module

If you don't have your own module, follow the instructions in this section to create a sample PowerShell module. Otherwise, skip to the next step:

  1. Create a new folder Get-Hello. Navigate into your folder and create a new file Get-Hello.psm1.

  2. Paste the following script into your Get-Hello.psm1 file:

    Function Get-Hello{
        Write-Host "Hello Azure DevOps!"
    }
    
  3. Generate the module manifest by running the following command in your Get-Hello directory:

    New-ModuleManifest -Path .\Get-Hello.psd1
    
  4. Open your Get-Hello.psd1 file and locate the RootModule variable. This setting specifies the main script file that PowerShell loads when the module is imported. Replace the empty string with the path to your Get-Hello.psm1 file:

    RootModule = 'Get-Hello.psm1'
    
  5. The FunctionsToExport section specifies which functions are accessible to users when they import your module. Include your Get-Hello function:

    FunctionsToExport = @('Get-Hello')
    
  6. Find the FileList section, which specifies the files included when packaging the module. Add the file you wish to package with your module:

    FileList = @('./Get-Hello.psm1')
    

Package and publish a module

  1. Generate a nuspec file for your module. This command creates a Get-Hello.nuspec file containing the necessary metadata for packing the module:

    nuget spec Get-Hello
    
  2. Run the following command to package your module:

    nuget pack Get-Hello.nuspec
    
  3. Run the following command to add your feed source URL. Make sure that you use V2 in your feed source URL, as NuGet V3 is not supported.

    • Organization-scoped feed:

      nuget sources Add -Name "<FEED_NAME>" -Source "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/nuget/v2" -username "<USER_NAME>" -password "<PERSONAL_ACCESS_TOKEN>"
      
    • Project-scoped feed:

      nuget sources Add -Name "<FEED_NAME>" -Source "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v2" -username "<USER_NAME>" -password "<PERSONAL_ACCESS_TOKEN>"
      
  4. Publish the package to your feed:

    nuget push -Source "<FEED_NAME>" -ApiKey "<ANY_STRING>" "<PACKAGE_PATH>"
    

Important

The version number in your Module Manifest (.psd1) must be identical to the version number in your .nuspec file.

Connect to a feed as a PowerShell repository

This section guides you through authenticating with a feed as a PowerShell repository and consuming a module hosted in your feed:

  1. In a PowerShell prompt window, run the following command to set up your credentials. Replace the placeholders with the appropriate information.

    $patToken = "<PERSONAL_ACCESS_TOKEN>" | ConvertTo-SecureString -AsPlainText -Force
    
    $credsAzureDevopsServices = New-Object System.Management.Automation.PSCredential("<USER_NAME>", $patToken)
    
  2. Register your PowerShell repository. You can find the SourceLocation link by navigating to Artifacts > Connect to Feed > NuGet.exe, under the Project setup section > source URL.

    • Project-scoped feed:

      Register-PSRepository -Name <REPOSITORY_NAME> -SourceLocation "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v2" -PublishLocation "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v2" -InstallationPolicy Trusted -Credential $credsAzureDevopsServices
      
    • Organization-scoped feed:

      Register-PSRepository -Name <REPOSITORY_NAME> -SourceLocation "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/nuget/v2" -PublishLocation "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/nuget/v2" -InstallationPolicy Trusted -Credential $credsAzureDevopsServices
      

    Tip

    Some versions of PowerShell may require starting a new session after running the Register-PSRepository cmdlet to prevent encountering the Unable to resolve package source warning.

  3. Register your package source:

    • Project-scoped feed:

      Register-PackageSource -Name <REPOSITORY_NAME> -Location "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v2" -ProviderName NuGet -Trusted -SkipValidate -Credential $credsAzureDevopsServices
      
    • Organization-scoped feed:

      Register-PackageSource -Name <REPOSITORY_NAME> -Location "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/nuget/v2" -ProviderName NuGet -Trusted -SkipValidate -Credential $credsAzureDevopsServices 
      

    Note

    • Register-PSRepository: Used for registering a PowerShell repository to find and install modules.
    • Register-PackageSource: Used to register a package source for finding and publishing packages.
  4. To verify if the repository was successfully registered, run the following command to retrieve all registered repositories for the current user:

    Get-PSRepository
    
  5. Run the following command to install the Get-Hello module.

    Install-Module -Name <PACKAGE_NAME> -Repository <REPOSITORY_NAME>
    

Note

If your organization uses a firewall or a proxy server, make sure that you allow access to Azure Artifacts Domain URLs and IP addresses.

Install a package from your pipeline

This example walks you through authenticating with an Azure Artifacts feed and installing a PowerShell module from your pipeline. To use your personal access token, add it as a pipeline variable, as shown below:

  1. Sign in to your Azure DevOps organization, and then navigate to your project.

  2. Select Pipelines, select your pipeline definition, and then select Edit to modify your pipeline.

  3. Select Variables at the top right corner, and then select New variable.

  4. Enter a Name for your variable, and then paste your personal access token into the Value textbox.

  5. Make sure that you select the Keep this value secret checkbox. Select Ok when you're done.

  6. Add a second variable for your userName. Enter a Name for your variable, then input your userName in the Value textbox.

  7. Select Save when you're done.

trigger:
- main

pool:
  vmImage: 'Windows-latest'

variables:
  PackageFeedEndpoint: 'https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v2' ## For organization scoped feeds use'https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/_packaging/<FEED_NAME>/nuget/v2'

steps:
- powershell: |
    $pat = ConvertTo-SecureString ${env:pat_token} -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential("${env:userName}", $pat)
    Register-PSRepository -Name <REPOSITORY_NAME> -SourceLocation "$(PackageFeedEndpoint)" -InstallationPolicy Trusted -Credential $credential
  displayName: 'Register PSRepository'
  env:
    pat_token: $patToken
    userName: $userName

- powershell: |
    nuget install <PACKAGE_NAME> -Source "https://pkgs.dev.azure.com/<ORGANIZATION_NAME>/<PROJECT_NAME>/_packaging/<FEED_NAME>/nuget/v3/index.json"
  displayName: 'Install module'