How to push NuGet packages to Azure Artifacts from Bitbucket Pipelines

azure devops portal with a private nuget package

After the recent MyGet outage, some of their users are going to be migrating to other private feed services. Major git repo hosting providers, such as GitHub, GitLab, or Azure DevOps have built-in support for NuGet private feeds, unfortunately it is not the case for Bitbucket Pipelines. So developers who use Bitbucket Pipelines for their CI/CD will need to utilize 3rd party services to store NuGet packages privately.

This article demonstrates how to use Azure Artifacts private feeds from Bitbucket Pipelines. We explore how we can authenticate, push and pull packages from the Pipelines, as well as locally.

Packaging .NET code to a NuGet package

We can generate a NuGet package from .NET code either by enabling Produce a package file during build operations under Project properties > Package > General or by running the dotnet pack tool.

The second option gives us more control so we are going to use it. An example command that packs SecretCodeLibrary.csproj from the SecretCodeLibrary folder in the Release configuration and generates a NuGet package in the current folder with 1.0.42 version:

dotnet pack SecretCodeLibrary/SecretCodeLibrary.csproj -o . --configuration Release -p:Version=1.0.42

The result of the command - file with .nupkg extension:

content of a folder with .NET solution source code and NuGet package after dotnet pack command

Creating a private feed in Azure Artifacts

Now it’s time to create a private feed in Azure Aritfacts so that we can push and pull our newly created NuGet package.

Go to your Azure DevOps, select a project and navigate to Artifacts in the main menu:

azure devops artifacts screen

You should be able to click Create Feed button, and after providing a descriptive name, your new Azure Artifacts private feed is created.

In this article I created a project scoped feed, but feel free to use organization scoped feed if you want to broader access for your teams.

Connecting to the private feed from your local environment

Before accessing Azure Artifacts from your local environment we need to make sure the necessary tools are installed.

We need .NET Core SDK 2.1.400+, which I assume you already have installed since you’ve built your package. But just in case, here is the link. Get the latest 6.0.x since according to the documentation, the credential provider needs 6.0 SDK.

Install the credential provider. For Windows, the only thing needed is this command executed from powershell:

iex "& { $(irm }"

Once we have the tools installed, let’s follow the instruction we have in the “Connect to feed” screen:

azure devops artifacts screen

Let’s add the nuget.config we have in the first block to the root folder with our .sln file. My file looks like this, but yours will differ a little bit:

<?xml version="1.0" encoding="utf-8"?>
    <clear />
    <add key="PrivateArtifactsFeed" value="" />

Let’s try to push our package with the following command (--api-key is required, but the actual parameter is not used, can be any random string):

dotnet nuget push --source "PrivateArtifactsFeed" --api-key az SecretCodeLibrary.1.0.42.nupkg

And we get this result: console window indicating Nuget package was successfuly pushed to Azure Artifacts

As you can see, the tool was able to silently authenticate using the Microsoft Authentication Library (MSAL):

[CredentialProvider]VstsCredentialProvider - Acquired bearer token using 'MSAL Silent'

Since my local Windows user is the same one I used to create the feed, the authentication process happened automatically.

Let’s go back to the Azure DevOps portal and see if we can locate the new package. And indeed, we can: azure devops portal with a private nuget package

Consuming private packages from Visual Studio

So far we were able to create the private feed and access it locally from the command line. To finish our local environment setup we need to make sure we can work with Azure Artifacts from Visual Studio:

Visual Studio displaying a nuget package from Azure Artifiacts private feed

Visual Studio automatically picks up nuget.config file we added earlier, so there’s no additional configuration, since, again, we authenticate with the current Windows user.

You may have noticed that there’s only one feed in Visual Studio NuGet Package Manager. It happens because in our nuget.config we have <clear /> line, which clears all global feeds. Our private feed will use so-called upstream sources to fetch and cache the packages for public NuGet feed:

Azure Artifacts portal displaying Nuget public feed upstream source

A single feed for a solution is considered a best practice, read more here.

Pushing and pulling private NuGet packages in Bitbucket Pipelines

Bitbucket Pipelines is an external source, so we are going to authenticate using a Personal Access Tokent or PAT.

Go to User Settings > Personal access tokens in Azure DevOps and generate a new one with Read & Write packaging permissions:

generating personal access token in azure devops

Create a new Repository Variable in Bitbucket’s Repository Settings called ARTIFACTS_FEED_ACCESS_TOKEN with the value you obtained in the previous step:

creating a new repository variable in bitbucket with azure artifacts personal access token or pat

Do not store your PAT in your source code. PAT is a sensitive secret and storing secrets in your code is a bad practice. That’s why we have just used Repository Variables to separate it from our codebase.

Now we can finally use this variable for our feed authentication inside our pipeline. Let’s add the following step which programmatically replaces the nuget source from our local nuget.config with the same one, but the new source includes our PAT as the password (again, we are doing it only during the pipeline build, so the PAT never gets to the repo’s source code):

- step:
  name: "Push Nuget to Azure Artifacts"
    - dotnet nuget remove source PrivateArtifactsFeed
    - dotnet nuget add source --name PrivateArtifactsFeed --store-password-in-clear-text --username <YOUR_EMAIL> --password $ARTIFACTS_FEED_ACCESS_TOKEN
    - dotnet pack SecretCodeLibrary/SecretCodeLibrary.csproj -o . --configuration Release -p:Version=1.0.$BITBUCKET_BUILD_NUMBER
    - dotnet nuget push --source "PrivateArtifactsFeed" --api-key az SecretCodeLibrary.1.0.$BITBUCKET_BUILD_NUMBER.nupkg

You will need to change the feed URL with your own and provide a correct user name. Also, notice how we used $ARTIFACTS_FEED_ACCESS_TOKEN and $BITBUCKET_BUILD_NUMBER variables.

With this step added, you will be able to create a new NuGet package version and push it to your private Azure Artifacts feed each time the pipeline is run.

comments powered by Disqus