Azure DevOps Security, Governance and Modernisation: A Practical Guide
A practical guide to hardening and modernising Azure DevOps, covering service connections, usage gates, RBAC, YAML pipeline modernisation, Git policies, artifact scanning, Key Vault variable groups, and continuous governance.
Azure DevOps sits at the centre of how most organisations build and ship software. That makes it one of the highest value targets in your estate and one of the easiest places for governance to quietly erode over time. Permissions accumulate, service connections spread, classic pipelines linger, and nobody is entirely sure which controls are actually enforced. This guide walks through the controls that matter most and how to modernise the platform without slowing delivery down.
The themes below map to real assessment findings we see repeatedly. Each section explains what good looks like, why it matters, and the practical steps to get there.
Service Connection Security
Service connections are effectively standing credentials into your cloud. A single over-permissive connection can turn a compromised pipeline into a full subscription takeover, so they deserve more scrutiny than almost anything else in the platform.
- Use workload identity federation rather than service principal secrets or certificates. Federated credentials remove long-lived secrets entirely, so there is nothing to leak, rotate, or find in an old export.
- Scope to the minimum. Prefer resource group scoped connections over subscription-wide access, and create separate connections for production and non-production so a lower environment can never reach production.
- Never grant access to all pipelines. Disable the "Grant access permission to all pipelines" option and authorise only the specific pipelines that need each connection.
- Add approval and check requirements to sensitive connections so their use in a pipeline run requires a human decision or passes defined checks.
- Review and rotate. Audit connections regularly, remove ones that are unused, and rotate any remaining secrets on a fixed schedule.
Leaving "Grant access permission to all pipelines" enabled on a subscription-scoped connection is one of the most common and highest impact findings. Any pipeline in the project, including a new one added by a contributor, can then deploy with those rights.
Usage Gates: Branch Controls and Approvals
Service connections, environments, variable groups, and agent pools are all classed as protected resources in Azure DevOps, and each can carry checks that must pass before a pipeline is allowed to use them. These checks are where a great deal of practical governance actually lives.
- Put the gate on the service connection, not the environment. The service connection is the resource that actually holds the credentials into your cloud, so that is where deployment gates belong. Approvals and checks defined on a service connection govern every pipeline that tries to use it, regardless of which environment, stage, or branch the run comes from.
- Branch control check. Restrict the service connection so it can only be consumed by pipelines running from an allowed branch, typically your protected main branch. This stops someone from creating a throwaway branch, pointing a pipeline at production, and deploying without review.
- Business hours and other checks. Combine approvals with business hours windows, required template checks, and Azure Policy or REST-based checks on the connection where appropriate.
It is tempting to treat environment approvals and checks as your production gate, but they are weak from a security standpoint. In Azure DevOps, environment checks can be added, edited, or removed by anyone with relatively low privilege on the environment, and there is no strong separation between the people who deploy and the people who can change the gate. Someone who can edit the environment can simply remove the approval and deploy. Treat environment checks as delivery convenience and workflow signalling, not as a control you rely on. The gate that matters must live on the service connection.
Approval Gates on Service Connections
Because a service connection is a protected resource, you can attach an approval check directly to it. When any pipeline run tries to consume that connection, the run pauses until a named approver signs off. This is the control to rely on for production, and it is worth configuring carefully.
- Name specific approvers or a small group. Assign approval to a dedicated release-approver group rather than to broad teams, and keep that group tightly managed and auditable.
- Separate approvers from authors. Enforce the setting that prevents the user who requested the run from approving it, so a single person cannot both trigger and approve a production deployment.
- Set a timeout. Give approvals a sensible expiry so stale runs do not sit pending indefinitely, and so an unattended run fails closed rather than lingering.
- Combine with the branch control check. Approval alone does not verify where the pipeline definition came from. Pair the approval with a branch control check on the same connection so an approver is only ever asked to sign off runs originating from the protected branch.
- Use separate connections per environment. A distinct production connection with its own approval means a non-production pipeline can never trigger a production approval prompt in the first place.
- Remember approvals apply to the whole run. Once a connection is approved for a run, that authorisation covers the run, so keep production deployment logic in a dedicated, minimal stage rather than mixing it with other work.
RBAC Best Practices
Access control drift is almost universal. The goal is a model you can reason about at a glance, driven by groups rather than individuals.
- Assign through Entra ID groups, never to individual users. This keeps joiner and leaver processes clean and gives you a single place to review membership.
- Apply least privilege. Grant the minimum role required for each function. Most contributors never need Project or Collection Administrator rights.
- Guard the administrator groups. Keep Project Collection Administrators and Project Administrators as small as realistically possible and review the membership regularly.
- Separate environments. Use distinct projects, or even distinct organisations, to keep production and non-production access boundaries clear.
- Review on a schedule. Run quarterly access reviews covering users, groups, guests, and any personal access tokens, and remove anything stale.
- Constrain personal access tokens. Prefer Entra ID authentication, set short maximum lifetimes, scope tokens narrowly, and require approval for creation where the tenant supports it.
YAML Pipeline Modernisation
Classic UI-based pipelines are difficult to review, impossible to version alongside code, and easy to change without any trace. Moving to YAML is the single biggest step towards a governable delivery platform.
Multi-stage Pipelines
Model the full path to production as a single multi-stage YAML pipeline: build, test, and then one stage per environment. This gives you a complete, auditable view of how a change reaches production. Keep the enforceable gate (approval and branch control) on the production service connection each stage consumes, not on the environment, for the reasons set out above.
stages:
- stage: Build
jobs:
- template: templates/build.yml@templates
- stage: DeployTest
dependsOn: Build
jobs:
- deployment: DeployTest
environment: test
strategy:
runOnce:
deploy:
steps:
- template: templates/deploy.yml@templates
- stage: DeployProd
dependsOn: DeployTest
jobs:
- deployment: DeployProd
environment: production # visibility only; the gate lives on the prod service connection
strategy:
runOnce:
deploy:
steps:
- template: templates/deploy.yml@templates Centralised Pipeline Templates
Do not let every team reinvent the same build and deploy logic. Keep a central repository of reusable YAML templates covering build, test, scanning, and deployment steps, then reference those templates from each project's pipeline.
- One source of truth. Security scanning, signing, and deployment conventions are defined once and inherited everywhere.
- Safe rollout. Improvements to a template propagate to every consumer without touching each pipeline by hand.
- Enforceable standard. Combined with a required template check, you can insist that pipelines extend an approved base template before they are allowed to run against protected resources.
# azure-pipelines.yml in a consuming repository
resources:
repositories:
- repository: templates
type: git
name: Platform/pipeline-templates
ref: refs/heads/main
extends:
template: templates/secure-pipeline.yml@templates Git Policy Best Practices
Branch policies are how you turn "we review our code" into something the platform actually enforces. Protect every long-lived branch, main above all.
- Require pull requests. No direct pushes to protected branches under any circumstances.
- Require reviewers. Mandate at least one, preferably two, reviewers, and reset approvals when new changes are pushed so a rubber stamp cannot survive a later commit.
- Require successful build validation. A CI build must pass before a pull request can complete, so nothing merges in a broken state.
- Validate against a required YAML template. Use build validation and the required template check together so that pull requests are verified against your approved central pipeline template, not an arbitrary pipeline the author defined themselves.
- Link work items and resolve comments to keep traceability and ensure feedback is actually addressed before completion.
- Limit who can bypass policies. The ability to override or complete without policy checks should be tightly held and audited.
Where Pipeline Definitions Are Created From
This is a subtle but important control that many teams miss. A pipeline's YAML can be edited on any branch, and if the pipeline's default branch for manual and scheduled builds points at an unprotected branch, someone can change the pipeline definition itself outside of review and then run it.
- Set the default branch for manual and scheduled builds to your protected branch, so manual and scheduled runs always use the reviewed definition from main.
- Use branch control checks on protected resources, so even if a pipeline is triggered from a feature branch it cannot consume production connections or environments.
- Combine both. Together these ensure the definition that touches production is the one that passed review, not a modified copy on a side branch.
Branch policies protect the code in a branch, but they do not by themselves protect the pipeline definition from being run from elsewhere. Pinning the definition source and gating protected resources by branch closes that gap.
Artifact Scanning and Retention
Scanning a package once at build time is not enough. New vulnerabilities are disclosed constantly, so a package that was clean last month may be a known risk today.
- Scan on every build. Run dependency and vulnerability scanning as a standard step in your central template, and fail builds on critical findings.
- Re-scan existing packages regularly. Schedule periodic re-scans of artifacts already published to your feeds so newly disclosed vulnerabilities in older packages are surfaced rather than forgotten.
- Generate a software bill of materials. Produce and retain an SBOM for each release so you can answer "are we affected" quickly when the next major disclosure lands.
- Set retention policies. Define retention on both artifact feeds and pipeline runs so you keep what you need for audit and rollback while clearing out the noise that inflates cost and widens the attack surface.
- Control upstream sources. Vet upstream feeds and use scanning to prevent unreviewed public packages flowing straight into builds.
Secrets and Variable Groups in Key Vault
Secrets defined directly in pipelines or variable groups are easy to leak and hard to rotate. Azure Key Vault should be the single home for every secret your pipelines consume.
- Back variable groups with Key Vault. Link variable groups to a Key Vault rather than storing secret values in Azure DevOps, so secrets are centrally managed, versioned, and access controlled.
- Reference, do not copy. Pipelines read the current secret at runtime, which means rotation happens in one place and takes effect everywhere immediately.
- Restrict access to the vault using managed identity or workload identity federation, and keep production vaults separate from lower environments.
- Never hardcode. Treat any secret committed to a repository or pasted into a pipeline as compromised and rotate it.
variables:
- group: production-secrets # linked to Azure Key Vault
steps:
- task: AzureKeyVault@2
inputs:
azureSubscription: 'prod-workload-identity'
KeyVaultName: 'kv-prod'
SecretsFilter: 'api-key'
- script: |
curl -H "Authorization: Bearer $(api-key)" https://api.example.com What Else to Get Right
A few further controls round out a well governed platform:
- Audit logging. Stream Azure DevOps audit events to Azure Monitor or your SIEM, and alert on permission changes, policy edits, and service connection changes.
- Secret scanning. Enable GitHub Advanced Security for Azure DevOps to detect exposed secrets, block pushes that contain them, and surface secrets already in history.
- Agent hygiene. Prefer Microsoft-hosted agents for a clean environment per run. Where self-hosted agents are required, isolate them on their own network, patch them, and never run them with administrative rights.
- Limit job authorisation scope. Reduce the reach of the build identity so a job cannot access resources outside the project unless explicitly required.
- Organisation policies. Enforce Entra ID authentication, control creation of new organisations and projects, and restrict who can install extensions.
Make Governance Continuous with Drop Table Pulse
Every control above can be verified by hand, but manual checks across dozens of projects, hundreds of pipelines, and countless service connections do not scale, and they drift out of date the moment you finish. That is exactly the problem Drop Table Pulse is built to solve.
Pulse runs continuous automated assessments across your Azure DevOps organisation, covering service connections, branch policies, approvals and checks, RBAC, pipeline configuration, artifact and secret scanning, and Key Vault usage. Every finding comes with clear guidance towards the best practice described here, and common misconfigurations can be fixed through automated remediation rather than a manual click-through. You can schedule scans so posture is monitored over time, track your security and maturity scores as you improve, and export reports for auditors and stakeholders.
In short, this guide describes the destination and Pulse keeps you there.
Final Takeaway
Securing and modernising Azure DevOps is not a single project, it is an ongoing discipline. Lock down service connections, gate protected resources by branch and approval, drive access through groups with least privilege, move to multi-stage YAML built on centralised templates, enforce Git policies with required build validation and templates, pin where pipeline definitions run from, scan and re-scan artifacts with sensible retention, and keep every secret in Key Vault.
Want to see where your organisation stands today? Explore Drop Table Pulse or speak to our team for a governance and security review.
Want more insights?
Join our mailing list for future blog posts and practical Azure security guidance.