Securing Azure DevOps When Using Private Repositories
..a simple step to avoid exposing your DevOps Org
Azure DevOps pipelines have the ability to reference code from private repositories in other projects. However, there are global settings that - if set - will grant pipelines access to repositories across the entire organization. A compromised pipeline can then expose your entire ADO organisation.
In this article, I’ll illustrate how to check you’re using a safer, more explicit authorisation scope, and if not, how to fix your pipelines to increase your security posture.
The recommended settings
The settings are available both within “project” and “organisation” settings, organisation settings are preferred as it will then be enforced across all projects:
Navigate to Settings -> (Pipeline) Settings and check the following settings are on:

Enabling this without first fixing existing pipelines will cause them to stop working if they rely on being able to call resources in other projects. Guidance on how to do this is given below.
Explaining each in turn:
Limit job authorization scope to current project for non-release pipelines
. This restricts the token for YAML and classic build pipelines to the current project.Limit job authorization scope to current project for release pipelines
. This is the same as the preceding setting, but for classic release pipelines.Protect access to repositories in YAML pipelines
. This setting forces your YAML pipelines to explicitly reference any repositories they need to access, preventing unintended repository access.
Go pragmatic by project
If the organisation settings are not set as recommended, you can approach this individually per project.
To find affected pipelines that use resources outside the current project, search for a ‘resources’ block:
|
|
If there are not any references to remote resources, you can safely enable the settings on this project.
In the following sections, we go into some background and then explore a couple common scenarios where remote resources are used, and how to configure the pipelines safely.
Background #1 - job authorisation scopes
Authentication between projects uses the concept of job authorisation scopes.
When a pipeline runs, it uses a job-scoped access token to fetch code and interact with resources. There are two key scopes:
- Collection-Scoped Token: Grants access to any repository in the organization.
- Project-Scoped Token: By default, restricts access to only repositories within the current project.
By using the recommended settings, you’re ensuring a project-scoped token is used, requiring explicit authorisation to access other projects.
Background #2 - build service accounts
When you create an ADO project, a build service account is automatically made, this is the default identity used by the pipeline.
When you’re using project-scoped tokens, if you want to access resources outside the current project, you need to explicitly grant permissions.
Here’s an example:

Scenarios
Now, let’s draw up two example scenarios that make use of cross project references:
- shared pipelines
- checking out a repository in a different private project
Scenario #1 - Shared Pipelines
Firstly, why do this?
Placing the pipeline logic in a central repository allows CI logic to be re-used across projects and also allows for secure pipeline usage via “extends” (that’s a topic for another day!)

For instance, in the shared pipeline project, you could make use of John Folberth’s excellent template collection in TheYAMLPipelineOne: Azure DevOps YAML Templates.
When the pipeline compiles, it fetches the yaml from the shared project.
It’s worth pointing out that you can also put the shared pipelines in a different repository within the same project, but that doesn’t always scale well and may not fit within your ADO RBAC model.
Example shared pipeline calling implementation
In case you’re unfamiliar, here’s a trivial example of a solution project pipeline calling a shared pipeline:
|
|
Breaking this down:
- The resources section defines the target repository where the shared pipeline is stored.
- The “name” references the remote project name and repo, the “ref” could be a branch like ref/heads/main, but typically a tag is used.
- The extends section will fetch the yaml template in the path templates/deploy.yml.
- “@pipeline-templates” matches the repository label in the resources section.
Runtime experience
When you run the pipeline for the first time, you’ll be asked to “permit” access to the pipeline template.
In stricter environments, an owner of the target repository may have to pre-stage that permission for you.
Steps required
When fetching pipeline templates, you simply need to grant the calling project’s build service account Reader access over the target repository.
Scenario #2 - checkouts from multiple private repos
In this scenario, we’re considering a pipeline that needs to check out code from multiple private repos across project boundaries.
Here’s an example where a solution calls a Terraform module in a shared location:

The calling code from the solution looks similar. I’ve left the pipeline templates in, and added a repository with a sample terraform module:
|
|
Scenario 2 steps required
Besides ensuring the build service has permission to access the target resource, you also need to explicitly reference resources the pipeline needs to access, in the case of repositories that is either using a checkout
, or a uses
statement.
Why is this extra step needed, compared to fetching the pipeline templates?
The fetching of the templates is done at pipeline compile time, whereas in this scenario we are accessing the repository code in the remote project at runtime.
The uses
attribute is specified against the pipeline job:
|
|
The uses
scenario is a little unintuitive, so I’ll expand on that further.
When terraform init
runs, it requires access to the module in the shared repository. The module itself isn’t checked out as part of the build, but it is fetched by Terraform. Since there is no checkout step for this remote repository, the uses
attribute allows us to signal to Azure DevOps to update the pipeline’s job access token appropriately.
Call to action
For enhanced security, always:
- Enable the three recommended settings. This confines pipeline access and forces explicit repository references. Do this at the organisation scope if you can, so it is enforced across all projects.
- Avoid classic pipelines and releases if possible. Multi-stage YAML pipelines offer better security controls and are more transparent for code review.
- Use the build service identity model. Grant the project-scoped build service account only the permissions it needs when accessing resources outside the current project (for example, Reader access on target repositories).