In Part 1 of this lab, we laid the foundation for building secure Azure DevOps pipelines by setting up protected repositories, managing identities, and enforcing least privilege access. Now, in Part 2, we take security to the next level focusing on safeguarding pipeline resources, securing access to Azure Repos, protecting sensitive variables, and introducing reusable templates for consistency and compliance.
By the end of this section, your pipelines won’t just deliver code they will enforce security at every stage, ensuring that deployments are both efficient and compliant with best practices.
Continuation
Step 4: Use Secure Variable Management
Secure variable management involves storing sensitive information (like passwords, tokens, or API keys) in Azure DevOps as secret variables or through Azure Key Vault. These secrets are encrypted, hidden from logs, and only accessible during pipeline runtime to protect against unauthorized access or exposure.
i. Go to ** Pipelines > Library >** and click + Variable Group to create a Variable Group.
ii. Add variables like dbPassword, apiKey
and mark them as secrets.
iii. Reference the Variable Group in Your Pipeline YAML. Open your pipeline YAML file and click on Edit.
iv. Reference the variable group by adding👇🏻:
trigger:
main
variables:
group: SecureSecrets This is your variable group
trigger:
- main
variables:
- group: SecureSecrets # 👈 This is your variable group
stages:
- stage: Build
jobs:
- job: BuildJob
steps:
- script: |
echo "This is your API key: $(apiKey)"
displayName: Print API Key
Save and commit your YAML changes
vii. When the pipeline runs, it will automatically pull values from the linked variable group—ensuring secrets are securely injected without hardcoding them in the file.
Step 5: Secure Pipeline Access to Azure Repos Using Personal Access Token (PAT) Authentication
In this step, We will configure the pipeline to use a Personal Access Token (PAT) stored securely as a secret variable, enabling secure authentication to Azure Repos without exposing credentials in the code. This ensures that only authorized builds and scripts can interact with your repository.
i. Click your user profile picture (top right). Click on User Settings and select Personal access tokens.
ii. Click + New Token.
iii. Fill out:
Name → something descriptive, e.g., Pipeline-GitAccess
Organization → select your Azure DevOps org
Expiration → e.g., 30 or 90 days (shorter is safer)
Scopes → pick only what’s needed: For Git repo access → Code: Read & Write. For REST API calls → add Build or Release scopes as needed
Click on SAVE
Note: Copy the token right away and save it on your note, you won’t be able to see it again
iii. Store PAT in Azure DevOps Securely: Go to Pipelines → Library → + Variable group.
iv. Fill Out:
Name it → e.g.,
AuthTokens
.Add a variable →
PAT_SECRET
.Paste your PAT in the value field.
Mark it as Keep this value secret.
Click Save.
iv. Use the PAT in Your Pipeline: Example: Using PAT to access Azure Repos inside a pipeline job. Validate the below YAML and SAVE
variables:
- group: AuthTokens
steps:
- checkout: none
- script: |
git config --global user.email "pipeline@devops"
git config --global user.name "Azure DevOps Pipeline"
git clone https://$(PAT_SECRET)@dev.azure.com/<org>/<project>/_git/<repo>
displayName: "Clone repo using PAT"
✅ Security Tips:
Keep PAT scopes minimal (e.g., Code:
Read
if you only need to read code).Use short expiry and regenerate PATs periodically.
Never commit PATs to your repo always store as secret variables.
Remove unused PATs from User settings → Personal access tokens.
v. Audit Repository Permissions in Azure DevOps Go to Project Settings.
- In the left-hand menu, under Repos, click Repositories. This will show a list of all repositories in the project. Click the repository you want to audit
SecurePipelineRepo
.
Inside the repository settings page, select Security. This will display a list of users and groups with access to the repository.
-
Review Permissions
For each user or group, you can see permissions like:- Read
- Contribute
- Force Push
- Manage Permissions
Check whether permissions are Allowed, Denied,
or Not set
.
vi. Identify Over-Privileged Accounts
Look for accounts with unnecessary Contribute
or Manage
Permissions
rights. Pay attention to Project Collection Administrators
or Build Service accounts
. They often have high-level permissions by default.
Outcome: You now have a clear picture of who can read, modify, or manage your repository, ensuring you can enforce least privilege.
Step 6: Modularize Your Pipeline Using Templates
Breaking your pipeline into reusable YAML templates allows you to centralize common tasks, reduce duplication, and maintain consistency across projects, while securing access to shared logic.
i. Create a new repo (e.g.,pipeline-templates
). Go to Project Settings. under Repos, click Repositories. And click on + Create.
ii. Give the repo a name e.g. pipeline-templates
. Keep Add a README checked (optional but recommended). Keep Default branch as main
and click on Create.
iii. Write a Reusable build.yml
Template: Open your new pipeline-templates
repository. Click on the three dots(...) in the front of the repo and select Browse.
iv. On the New Repository page:
- Click on the three dots(...) in the front of the Repo
- On the drop-down select NEW
- And click on FILE.
v. Give the File a name build.yml
vi. Paste the below example reusable build template and Commit the file to the main branch.
parameters:
- name: buildConfiguration
type: string
default: 'Release'
- name: vmImage
type: string
default: 'ubuntu-latest'
jobs:
- job: Build
displayName: 'Build Application'
pool:
vmImage: ${{ parameters.vmImage }}
steps:
- checkout: self
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- script: |
npm install
npm run build -- --configuration ${{ parameters.buildConfiguration }}
displayName: 'Run build'
What this template does:
Accepts parameters for build configuration (Release, Debug) and VM image type.
Checks out code.
Installs Node.js (can be changed to .NET, Python, etc.).
Runs the build script.
vii. Secure Access to the Templates Repo: Select your pipeline-templates repo
:
Select your pipeline-templates repository.
Open Security tab.
Remove “Contribute” permissions for groups/users who do not need to edit templates.
Grant:
Read access to teams that only need to use templates.
Contribute access only to DevOps engineers responsible for maintaining templates.
Deny Force Push and Bypass Policies for everyone except admins.
And that’s a wrap for Part 2!
We’ve gone beyond just writing YAML and pushing code, we have built a pipeline that guards your resources, keeps secrets locked away, controls who can access what, and uses reusable templates to keep things both consistent and secure.
Security is not something you add at the end, it’s something you bake in from the start. With what you have set up here, every run of your pipeline is not just a deployment, it is a statement that you value stability, compliance, and trust.
This completes our lab on Implementing Security Through a Pipeline in Azure DevOps, giving you the skills and mindset to build pipelines that are as safe as they are efficient
If you have any question, insight, or your own tips on securing pipelines? Drop them in the comments, I’d love to hear from you.
Follow me for more hands-on DevOps, Azure, and Cloud Security labs and guides.
Top comments (0)