Github Action: Accessing Azure DevOps NuGet Feed Using Service Principal and Federated Credentials
  1. Challenges
  2. Background
  3. Preparation
    1. Setup your Service Principal
    2. Setup Github variables
    3. Setup your nuget source via nuget.config
  4. Your pipeline
  5. Conclusion

Challenges

As we might have already know, if we want to access a Private Nuget Feed on Azure DevOps Artifact, we will first need create a PAT (Personal Access Token), then we use this token to authenticate the Nuget Feed.

However, this PAT :

  • Requires a user account in our Azure Entra
  • It has an expiration up to 1 year

If you struggle with using a user account and having to manually update tokens every year, this article is for you.

In this article, I will demonstrate how to use a Service Principal and Federated Credentials to create a temporary access token for authenticating a NuGet feed. This approach:

  • It doesn’t required any user account in Azure Entra.
  • It’s password-less that mean we won’t need to take care of the access token and it’s expiration date.

Background

What is Service Principal ?

To access resources that are secured by a Microsoft Entra tenant, the entity that requires access must be represented by a security principal.

This requirement is true for both users (user principal) and applications (service principal).

The security principal defines the access policy and permissions for the user/application in the Microsoft Entra tenant. This enables core features such as authentication of the user/application during sign-in, and authorization during resource access.

(See: https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals)

Preparation

Setup your Service Principal

Step 1: Creating an Application via Azure Portal

As mentioned earlier, to delegate identity and access management functions to Microsoft Entra ID, an application must be registered with a Microsoft Entra tenant.

Then take a note of your Client ID and Tenant ID, we will use them to generate Entra Id Access Token, and use this Token as the password to access the Nuget feed.

Step 2: Certificates & secrets

I always prefer paswordless approach when It’s possible, so that in this step I will use “Federated Credential

Using Github Action’s federated scenario.

Setup Github variables

It’s recommened to configure all dynamic variables or secret in Github “Actions secrets and variables”.

In this article I will store information of the service principal in variables.

There are 2 variables:

  • AZURE_SP_CLIENT_ID: which is the Client ID of the Application that we created.
  • AZURE_SP_TENANT_ID: which is the Tenant ID of the Application that we created.

Setup your nuget source via nuget.config

In the root directory that hold your solution file (*.sln), I create a nuget.config file to store the feed source:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="myprivatefeed" value="https://pkgs.dev.azure.com/hungdoan-lab/Project01/_packaging/hungdoan-test-privatefeeds/nuget/v3/index.json" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>

NOTE: In this file, I only define the source URL. I will update the credentials separately from the workflow pipeline

Your pipeline

Given Context: I’m setting up a GitHub Actions pipeline to build my .NET application whenever a commit is pushed to the main branch.

In that case, I would create a workflow file in .github\workflows\build_main.yml :

name: "Build on push"

on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: read

jobs:
  build:
    runs-on: windows-latest
    steps:
      - name: "Checkout"
        uses: actions/checkout@v4

      - name: "Setup .NET"
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 8.0.x

      - name: "Login to Azure Service Principal"
        uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_SP_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_SP_TENANT_ID }}
          allow-no-subscriptions: true

      - name: "Get Azure Entra Id Token for Azure DevOps"
        id: "get_nuget_access"
        shell: pwsh
        run: |
          # Set the SCOPE configuration variable to "499b84ac-1321-427f-aa17-267ca6975798/.default" 
          # to refer to the Azure DevOps resource and all of its scopes.
          $access_token = $(az account get-access-token --resource=${{ vars.AZURE_SP_CLIENT_ID }} --scope=499b84ac-1321-427f-aa17-267ca6975798/.default --query accessToken -o tsv)
          
          # Export variables to Github's Ouput Context
          echo "NUGET_TOKEN=$access_token" >> $env:GITHUB_OUTPUT

      # Assuming we have alreay had a feed source config in a Nuget.config file
      # Otherwise we can just use "dotnet nuget add source" instead
      - name: "Update Nuget source credentials"
        env:
          NUGET_TOKEN: ${{steps.get_nuget_access.outputs.NUGET_TOKEN}}
          NUGET_FEED_NAME: "myprivatefeed"
        run: |
          dotnet nuget update source $env:NUGET_FEED_NAME --username optional --password $env:NUGET_TOKEN

      - name: "Build project"
        run: |
          dotnet restore
          dotnet build --no-restore --configuration Release


In this workflow:

  • id-token permission is required for “Azure/login” job.

How does it work actually?

The whole script is simply about creating a Azure Entra Id Token, then use this token to access Nuget, and then we use that token to access Nuget.

You can see that I use Azure/login (See https://github.com/Azure/login) tool to login into our Service Princial, which support federated identity credential on my service principal. 

Conclusion

We went through the steps to define an Azure Action workflow to access a private NuGet feed on Azure DevOps, which use Service Pricipal and federated identity

I believe this approach is more secure and easier to maintain compared to the PAT approach.

Hopefully, this will be helpful.

2 responses to “Github Action: Accessing Azure DevOps NuGet Feed Using Service Principal and Federated Credentials”

  1.  Avatar
    Anonymous

    Yes it does

    Like

  2.  Avatar
    Anonymous

    Did this work?

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

I’m Hung

Welcome to my personal space. This is a small corner where I share ideas and things I find interesting,..

Let’s connect

Don’t hesitate to reach out if there’s anything we can discuss, or simply to connect :)