If you are using Renovate or a similar tool to manage some aspects of your Azure DevOps repositories, you might have come accross a security issue. Renovate wants to to use a PAT that you created, or the $(System.AccessToken) variable.

You probably want to stay away from creating a PAT yourself for the following reasons:

  • User Dependency: PATs are tied to the user account that creates them. If that user leaves the organization or if their permissions change, the PAT and any processes using it will be affected.

  • Rotation Challenges: For security reasons, PATs should be rotated frequently. However, manually rotating PATs for automated processes can be a time-consuming and error-prone task. In practice, oftentimes PATs are given an expiry time of months or even years, which poses a security risk.

The System.AccessToken variable grants access to the permissions of the special ‘Build Service’ account, which might have too many permissions, and might be used by other pipelines as well.

With the recent introduction of using service principal users in Azure DevOps, we have a viable alternative for which we can setup much more fine grained access control.

Service Principals

A service principal (or SPN) is essentially a security identity used by applications, services, and automation tools to access specific Azure resources. It allows you to assign specific permissions that are different from your personal account, providing a more fine-grained control over access to resources.

Recently, Azure DevOps introduced the ability to grant access to Service Principals, enhancing the security of system access to Azure DevOps. This feature empowers us to delegate specific permissions and roles to an SPN. This SPN can then be used by other systems or even in our own pipelines to do automatic administration.

So how do we set this up?

Creating an App Registration

Before you can use a service principal, you need to register an application in Azure Active Directory. This gives your application an identity, allowing it to interact with Azure AD. Here are the steps to create an App Registration:

  1. Sign in to the Azure portal.
  2. Navigate to Azure Active Directory.
  3. Select “App registrations” and click “New registration”.
  4. Provide a name for the application, select the supported account types, and click “Register”.

After registration, you will see the application (client) ID, which is the unique identifier for your app.

Adding the Service Principal to Azure DevOps

Once you have registered your application, the next step is to add the service principal to Azure DevOps. Follow these steps:

  1. Navigate to your Azure DevOps organization.
  2. Select “Project settings” at the bottom of the sidebar.
  3. Click “Users” under General.
  4. Click “Add Users” and search for the Service Principal/App Registration name that you just created.
  5. Choose which projects this Service Principal should be allowed to work with. In the case of Renovate, choose all the projects for which you want this specific service principal to be able to create pull requests.

Creating a Client Secret for the App Registration

Creating a client secret is crucial as it allows your app to authenticate with Azure AD. Follow these steps:

  1. Go back to the Azure portal and navigate to your app registration.
  2. Click on “Certificates & secrets” in the left-hand menu.
  3. Click “New client secret”, provide a description and choose when it should expire.
  4. Click “Add”, and make sure to copy the value of the client secret as it won’t be visible again.

Adding a Secret Variable to the Azure DevOps Pipeline

Once you have your client secret, you can add it as a secret variable to your Azure DevOps Pipeline. This keeps it secure and allows your pipeline to authenticate with Azure AD. Here’s how:

  1. Go to your renovate pipeline in Azure DevOps.
  2. Click “Edit” to open the pipeline editor.
  3. Click “Variables” in the editor toolbar.
  4. Click “New”, enter the name of the variable as ‘SPNClientSecret’, paste the client secret as the value, and make sure “Keep this value secret” is checked.

Renovate Pipeline

With the setup complete, we can now fill in the final gaps. Add two variables to your pipeline, SPNClientId and tenantId. You can find these on the main page of your App Registration.

Next, add the following step to your pipeline:

- bash: |
    clientId=$(SPNClientId)
    clientSecret=$(SPNClientSecret)
    tenantId=$(tenantId)
    requestParams="client_id=$clientId&scope=499b84ac-1321-427f-aa17-267ca6975798/.default&client_secret=$clientSecret&grant_type=client_credentials"
    url="https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"

    token=$(curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" -d $requestParams $url | jq -r '.access_token')
    echo "##vso[task.setvariable variable=AzDoToken;issecret=true]$token"    

This step will make a POST request to obtain an access token from Azure AD using the client ID, client secret and tenant ID associated with the SPN that we just created. The scope is limited to Azure DevOps, so this token will not have rights for anything else. Next it stores this access token in a secret variable called AzDoToken, which can be used by subsequent steps in the same job.

Next, make sure the token used by renovate is the newly added AzDoToken. It might look something like the following:

- bash: |
    git config --global user.email 'bot@renovateapp.com'
    git config --global user.name 'Renovate Bot'
    npx renovate          
  env:
    TOKEN: $(AzDoToken)

Wrap up

The introduction of service principal authentication in Azure DevOps marks a significant step forward in the management of automated processes. Unlike Personal Access Tokens (PATs), service principals aren’t tied to a specific user. This eliminates the risk of automated processes breaking if a user leaves the organization or their permissions change.

Service principals provide a more scalable and manageable solution for large organizations or complex projects. They allow for the assignment of specific roles and fine-grained access control, which is not feasible with PATs. This means you can give your automated processes precisely the permissions they need, no more and no less.

Now, instead of having to use a real user or the built in ‘Build Service’ user, we can have Renovate use a specifically assigned user.