A Guide to Azure DevOps Branch Protection

Cloud & DevOps Engineer skilled in AWS, Linux/Windows, Bash, PowerShell & Python. Passionate about automation, CI/CD, and continuous learning toward DevOps mastery.
Inception
Hello everyone, this post is aimed at guiding you to protect your branch in Azure Repos and maintain stable, production-ready code. In addition, implementing a lap simulating the situation to explain the concept
Overview
First, let me explain what Azure DevOps is. Azure DevOps is a Microsoft platform that helps teams plan, build, test, and deploy software from idea to production using modern DevOps practices.
Think of it as an all-in-one DevOps toolbox that supports CI/CD, project management, source control, and automation.
It includes Azure Boards – Plan & Track Work, Azure Repos – Source Control, Azure Pipelines – CI/CD, Azure Test Plans – Testing, and Azure Artifacts – Package Management.
So why do teams use Azure DevOps instead?
Due to its end-to-end DevOps platform, Strong enterprise security & RBAC, Deep integration with Azure Cloud, support for large teams & enterprises, and support for Infrastructure as Code & automation.
What are the Azure DevOps Branch Policies & Security?
Azure DevOps Branch Policies and Security help teams protect their source code, enforce quality, and control how changes reach important branches like main, master, or release.
So What Are Branch Policies?
Branch Policies in Azure DevOps define rules that must be met before code can be merged into a protected branch.
For Example:
Require Minimum Reviewers, Check for Linked Work Items, Require Successful Build Validation (CI Pipeline), Require Passed Status Checks, Limit Merge Types, and Automatically Add Reviewers (via Code Owners / path filters). and more ...
How Branch Policies Work (Flow)
Developer creates a feature branch.
Opens a Pull Request (PR) to merge into
main.Branch Policies enforce:
Reviewers added
Build validation runs
Tests executed
Comments resolved Once all checks pass, PR can be merged.
This ensures code quality, testing, security, and traceability.
What is Branch Security in Azure DevOps?
Security determines who can read, write, push, delete, or configure policies on a branch.
We will go through the implementation step by step.
Workflow Diagram
Implementing Steps
Go to this website: https://dev.azure.com/, then Login
Click New Project
Fill in:
Project name:
FrogTech-LabVisibility: Private
Skip other settings (keep defaults)
Click Create
Now you have a completely empty Azure DevOps project.
Step one is to create a Repository
Go to Repos (left sidebar)
Azure DevOps will automatically create a repo for you called FrogTech-Lab as your project's name if not:
Click New Repository
Name:
frogtech-repoClick Create
Now your repo exists, but still empty.
So Step two is to create basic files, so your repo is not empty.
In Repos, click New, then File
File name:
README.mdContent: write anything (example:
# FrogTech Repository)Click Commit
The main branch now exists.
Step 3 is to create a user group, like Tech Lead + DevOps team, and this is important because we will apply permissions later.
Go to Project Settings (bottom-left)
Click Permissions
Click New Group
Group name:
Tech-LeadsAdd your account inside (so you can act as Tech Lead)
Click New Group again:
Group name:
DevOps-TeamAdd your account inside (so you can act as DevOps too)
💡Tip
For the lab, you can add yourself to all groups. In real life, these would be actual people.
Step 4 is to create the branch-name validation pipeline. This is the pipeline that will enforce correct naming.
Go to Pipelines
Click New Pipeline
Choose Azure Repos Git (YAML)
Select your repository
Choose Starter Pipeline
Replace everything with this YAML:
trigger: none
pr:
branches:
include:
- "*"
pool:
vmImage: ubuntu-latest
steps:
- bash: |
echo "🔍 Validating PR source branch..."
BRANCH="$(System.PullRequest.SourceBranch)"
BRANCH="${BRANCH#refs/heads/}"
echo "Detected branch name: $BRANCH"
REGEX="^(feat|fix|chore|refactor|docs|test)/[0-9]{5,7}-[a-z0-9._-]+$|^release/[0-9]+\.[0-9]+\.[0-9]+$"
if [[ ! "$BRANCH" =~ $REGEX ]]; then
echo "❌ Invalid branch name"
exit 1
fi
echo "✔ Valid branch name"
displayName: "Validate PR branch name"
Click Save
Name the pipeline:
PR-BranchName-Validationfor exampleCommit to main
Pipeline is now created.
The next step is to configure branch policies on main. You will now enforce PR-only merges, Tech Lead required reviewer, Developer cannot approve own PR, and Branch naming validation pipeline must pass
So here's how:
Go to Repos then Branches
On the main branch row, click the three dots (…)
Click Branch Policies
Now apply the following settings:
Turn ON:
Require a minimum number of reviewers → set to 1
Do not allow users to approve their own changes (OFF the toggle)
This enforces no direct push, and the author cannot approve their own PR
In the same screen:
Scroll to Automatically include reviewers
Click Add
Choose the
Tech-LeadsgroupSave
Now, every PR MUST include the Tech Lead.
Scroll to Build validation
Click Add build policy
Select pipeline:
PR-BranchName-ValidationTrigger: Automatic
Policy: Required
save
Now your PR fails if the branch name is wrong.
The step after is to deny direct pushes to main.
Go to Repos, then Branches
On main, click … then Branch security
Now configure:
For normal Project Contributors:
Find Contributors group then set:
Contribute → DENY
Force Push → DENY
Bypass Policies → DENY
This enforces PR-only merges.
For Tech-Leads group:
Contribute → allow
Bypass Policies → allow (optional, for emergencies only)
Force push → deny
For DevOps-Team
Contribute (Allow) → but this is overridden by Deny for main, so they can push to their own branches
Create branch (Allow)
Save.
Last step, giving the DevOps Team Repo-level Permissions, so allows DevOps to clone, create branches, push branches.
Go to Project settings, then Repositories
Choose your repo
Click Security
Find DevOps-Team
then set:
Contribute → Allow
Create branch → Allow
then save Now DevOps can create branches like: feat/12345-add-dashboard and push them, but cannot push to main.
Finally you can test by
Direct push to main. Create a commit on main locally then do git push origin main Expected: rejected
Create a branch named:
feat/332200-add_new_pipeline Push it then create a PR to main
Expected: Pipeline passes
another test is to PR from incorrect branch and it should fail
use branch like
feature/login
Issues encountered
Hosted agent issue
when trying to test PR from correct branch, it should succeed but actually, this error returns:
##[error]No hosted parallelism has been purchased or granted. To request a free parallelism grant, please fill out the following form https://aka.ms/azpipelines-parallelism-request
Pool: Azure Pipelines
After investigating the problem, I found that this error is normal when using Azure DevOps for the very first time. Azure DevOps pipelines cannot run until you enable or request parallel jobs.
Here’s the exact meaning of the error:
No hosted parallelism has been purchased or granted.
It means your Azure DevOps organization does not have permission to run Microsoft-hosted agents.
I found 2 different ways:
1- Solution 1 — Request Free Microsoft-hosted Parallelism Microsoft requires you to request free pipeline minutes the first time.
you can hit the link that is in the error https://aka.ms/azpipelines-parallelism-request and it will redirect you to the requesting page.
fill the form and at number 4 "Are you requesting a parallelism increase for Public or Private projects?" select private, Microsoft gives free parallelism for private projects if you request it through the form.
actually i follow this step but not helped me so i went to the solution 2 instead
2- Solution 2 — Use a Free Self-Hosted Agent (Instant Fix)
This bypasses Microsoft-hosted agents entirely.
Go to Organization, Settings, then Agent Pools
Click Default pool
Click New agent
Choose your OS (Windows, Linux, macOS)
Download the zip file
Follow the instructions on the page to configure the agent
Then, in your pipeline YAML, replace:
pool:
vmImage: ubuntu-latest
with
pool:
name: Default
Pipeline will now run on your machine, not Microsoft’s cloud.
[!tip] This is instant and works even if Microsoft-hosted agents are blocked.
After passing the Authentication
pipeline tried to run a bash command on your Windows self-hosted agent
Another issue, after configure host-based agent and when running a job it failed and this error returned:
The process 'C:\WINDOWS\system32\bash.exe' failed with exit code 1
This error means that the pipeline tried to run a bash command on your Windows self-hosted agent, but Windows can't run Linux-style bash commands unless you have WSL (Windows Subsystem for Linux) installed.
As we wrote the pipeline in bash in case we should use ubuntu hosted agent, but after we changed the hosted agent to our local machine, Windows OS, we need WSL (Ubuntu on Windows) installed.
After solving the issue, It works with me now as you see a successful job in the image below:
Thanks for reading.
References
https://eraki.hashnode.dev/locking-down-your-code-a-guide-to-azure-devops-branch-policies-and-security
https://learn.microsoft.com/en-us/azure/devops/repos/git/branch-permissions?view=azure-devops
https://learn.microsoft.com/en-us/azure/devops/repos/git/gitquickstart?view=azure-devops&tabs=visual-studio-2022



