An Azure DevOps organisation holds the credentials that deploy to production, so whoever controls a pipeline, a personal access token or a service connection effectively controls your Azure estate. Attackers rarely need a software flaw: a long-lived PAT in a script or a subscription-wide service connection open to every pipeline is enough. This guide covers how identity and access work in Azure DevOps Services, the admin roles that matter, the non-human identities it runs on, what the audit log records and how to stream it, and a hardening baseline with the exact setting names. It is written for platform engineers, DevOps leads and security teams who own or review an Azure DevOps organisation. For the equivalent controls on GitHub, see the GitHub and GitHub Actions Security Guide.
Key takeaways
- Service connections are the crown jewels. Move Azure Resource Manager connections to workload identity federation, scope them to a resource group, and stop granting them to all pipelines.
- PATs should become the exception. Use the tenant PAT policies (no global PATs, no full-scope PATs, a maximum lifespan) and replace automation PATs with Microsoft Entra tokens for service principals or managed identities.
- The job access token is an identity too. Without the job authorization scope settings, every YAML job runs with a collection-scoped build service token that can reach every repository in the organisation.
- Auditing is off until you turn it on. Enable Log Audit Events, stream to a SIEM, and remember that the built-in log keeps only 90 days and does not record sign-ins.
- Checks and approvals are your flow control. Protected resources (environments, service connections, agent pools, secure files, variable groups) need branch control, approvals and required templates, or any pipeline author can deploy.
How identity and access work in Azure DevOps
Azure DevOps Services authenticates people through Microsoft Entra ID (or Microsoft accounts for organisations not backed by Entra). Authorisation is Azure DevOps' own: access levels (Stakeholder, Basic, Basic + Test Plans), security groups at organisation, project and object level, and per-resource permissions. Entra application permissions are not used for authorisation inside Azure DevOps, which surprises many cloud teams.
| Credential or identity | Who or what uses it | Lifetime | Main risk |
|---|---|---|---|
| Microsoft Entra user token | People in the portal, Git over HTTPS via credential manager | About one hour, refreshed | Stolen session, missing Conditional Access |
| Personal access token (PAT) | Scripts, CLIs, legacy integrations | User-chosen, often a year | Leaks in code, global or full-scope tokens |
| SSH key | Git over SSH | Until removed | Forgotten keys on old laptops and build hosts |
| Service principal or managed identity | Automation calling the Azure DevOps REST API | Entra tokens, about one hour | Over-broad access level or group membership |
| Job access token | Each pipeline job, via the build service identity | Per job | Collection-wide scope if not restricted |
| Service connection | Pipelines deploying to Azure, registries, Kubernetes, other clouds | Depends on scheme (federated, secret, certificate) | Broad Azure RBAC, open to all pipelines |
| OAuth or GitHub app connection | Pipelines building GitHub repositories | Until revoked | Access to more repositories than are built |
Service principals and managed identities are added to an organisation directly (Organization settings, Users, Add users, using the Enterprise application Object ID) and must be added to each organisation separately. Removing an identity from Entra ID does not remove its Azure DevOps membership, so offboarding needs a step in Azure DevOps as well.
- Conditional Access: service principals can be targeted by Entra Conditional Access under the supported conditions and licensing; managed identities cannot. Enable the organisation policy for IP Conditional Access validation on non-interactive flows so PAT and token use is checked against IP-based policies. See the Conditional Access Guide.
- Guests: disable the Azure DevOps "External guest access" policy and the Entra setting that allows invitations to any domain unless there is a business need.
- Public projects: new public projects can no longer be created and Microsoft states existing ones convert to private in 2027.
Admin roles and privileged access
Privilege in Azure DevOps sits in a few groups and one Entra role. Each is worth an inventory and a named owner.
| Role or group | What it can do | Control |
|---|---|---|
| Organization owner | Everything; automatically a Project Collection Administrator | Assign to a managed admin account, not a personal one; document a second owner path |
| Project Collection Administrators (PCA) | Manage users, projects, processes, extensions, group memberships and permissions at every level; view audit log and manage streams | Keep membership small; grant through an Entra group activated with Privileged Identity Management |
| Azure DevOps Administrator (Entra role) | Sets tenant-level policies such as the PAT restrictions | Eligible assignment only, with approval |
| Project Administrators | Project security, service connections, pipelines settings, "Open access" on resources | One owning team per project; review quarterly |
| Service connection security roles | Create, share and administer service connections | Restrict creation to the platform team |
| Project Collection Build Service and {Project} Build Service | Identities behind job access tokens | Never add to admin groups; grant only needed repository and feed permissions |
- Just-in-time administration: Microsoft recommends a PIM group for admin access. Make PCA membership an eligible Entra group so standing admin rights disappear. See the Privileged Access Management Guide if your PAM programme covers developer platforms.
- Admin PATs: revoke PATs held by administrators. An admin PAT with full scope is a portable copy of the whole organisation.
- Extensions: PCA members install Marketplace extensions, which can add pipeline tasks that run with job tokens. Approve extensions through a request process and watch
Extension.Installedevents.
Integrations, API tokens and service accounts
This is where Azure DevOps differs most from user-centric platforms: most of the risky credentials are non-human, and several are created implicitly.
Personal access tokens
Azure DevOps offers four tenant policies (set by the Azure DevOps Administrator) and one organisation policy (set by a PCA). All are off by default except leak revocation.
| Policy | Level | Default | Recommended setting |
|---|---|---|---|
| Restrict global personal access token creation | Tenant | Off | On: tokens tied to one organisation |
| Restrict full-scoped personal access token creation | Tenant | Off | On: custom scopes only |
| Enforce maximum personal access token lifespan | Tenant | Off | On: a short value in days, for example 30 to 90 |
| Automatically revoke leaked personal access tokens | Tenant | On | Leave on (covers tokens found in public GitHub repositories) |
| Restrict personal access token (PAT) creation | Organisation (Entra-backed) | Off | On, with an allowlist group; optionally allow packaging-scope tokens only |
Use groups, not named users, in each policy allowlist. Existing PATs keep working until expiry after the creation restriction is enabled, so pair it with a revocation campaign. Token lifecycle practice (inventory, owners, rotation) is covered in the SaaS API Token Security Guide.
Service connections
For Azure Resource Manager, Microsoft recommends workload identity federation with an app registration or a user-assigned managed identity, and keeps secret-based options (automatic app registration with a secret, manual service principal with key or certificate, publish profile, agent-assigned managed identity) only for backward compatibility.
- Convert existing connections: the Convert action in Project settings, Service connections works for connections Azure DevOps created and only one project uses; you can revert within seven days. Manually created or shared connections need a new federated connection.
- Issuer change: new federated connections in the Azure public cloud use the Entra issuer (
https://login.microsoftonline.com/); the Azure DevOps issuerhttps://vstoken.dev.azure.comis due to retire on 1 July 2027 for single-tenant apps and managed identities. Review any federated credential that pins the old issuer before then. - Scope: assign Azure RBAC at resource group level and avoid subscription-wide Contributor. Avoid classic (Azure Classic) connections, which cannot be scoped. See the Azure RBAC and Service Principals Guide.
- Usage history: review it regularly. Azure Pipelines is starting to disable service connections after 100 days of inactivity, which is helpful but not a substitute for an owner.
The general model for federated pipeline credentials is in the Workload Identity Federation Guide.
Job access tokens and the build service
Every job receives a job access token for the Project Collection Build Service ({Org}) identity or, when scope is restricted, the {Project} Build Service ({Org}) identity. Turn on, at organisation level so projects cannot override them:
- Limit job authorization scope to current project for non-release pipelines (YAML and classic build).
- Limit job authorization scope to current project for release pipelines (classic release).
- Protect access to repositories in YAML pipelines, which limits the token to repositories referenced by a
checkoutstep orusesstatement (on by default for organisations created after May 2020).
Secrets in pipelines
- Prefer no secret: service connections and managed identities beat stored keys.
- Where a secret is needed: store it in Azure Key Vault and control access with Azure RBAC; otherwise use secret variables or variable groups governed by library security. See the Azure Key Vault Security Guide.
- Masking is best effort: Microsoft states log scrubbing is not foolproof. Do not store JSON, XML or YAML blobs as one secret, do not echo secrets or pass them on command lines, do not set secrets with logging commands, and use
--output noneon Azure CLI commands that return secrets.
Logging and detection
Enable auditing under Organization settings, Policies, Security, Log Audit Events. It is off by default, requires an Entra-backed organisation, and keeps events for 90 days. Viewing needs PCA membership or the View audit log permission. Each event carries actor, IP address, timestamp, area, category and details; exports add the authentication mechanism, user agent and correlation IDs.
Stream the log: Azure DevOps supports Splunk (HTTP Event Collector), Azure Monitor Logs (the AzureDevOpsAuditing table, usable from Microsoft Sentinel) and Azure Event Grid. Events are batched roughly every 30 minutes, a disabled stream catches up only seven days of history when re-enabled, and auditing is still labelled public preview. Two blind spots matter: sign-ins and Entra group membership changes are not in the Azure DevOps log, so correlate with Entra sign-in and audit logs.
| Detection | Audit actions | ATT&CK |
|---|---|---|
| PAT created with broad scope or long lifetime, or by an admin | Token.PatCreateEvent, Token.PatUpdateEvent | T1098.001 Additional Cloud Credentials |
| PAT found public or revoked by the system | Token.PatPublicDiscoveryEvent, Token.PatSystemRevokeEvent | T1552.001 Credentials In Files |
| Service connection created, shared to other projects or modified | Library.ServiceConnectionCreated, Library.ServiceConnectionShared, Library.ServiceConnectionModified | T1098.001 |
| Service connection used by an unexpected pipeline | Library.ServiceConnectionExecuted, Pipelines.ResourceAuthorizedForPipeline | T1078.004 Cloud Accounts |
| Pipeline definition edited outside normal change | Pipelines.PipelineModified, CheckConfiguration.Deleted or Disabled | T1677 Poisoned Pipeline Execution |
| Branch policy removed or bypassed | Policy.PolicyConfigRemoved, Git.RefUpdatePoliciesBypassed | T1195.002 Compromise Software Supply Chain |
| Permission or organisation policy change | Security.ModifyPermission, OrganizationPolicy.PolicyValueUpdated | T1098 Account Manipulation |
| Extension installed | Extension.Installed | T1195.002 |
| Audit stream tampering | AuditLog.StreamDisabledByUser, AuditLog.StreamDeleted | T1685.002 Disable or Modify Cloud Log |
New actions are added over time; query https://auditservice.dev.azure.com/{organisation}/_apis/audit/actions to see what your organisation emits. Keep at least a year in the SIEM. See the Microsoft Sentinel Guide and the SaaS Audit Log Retention and Licensing Guide.
Metrics that show the programme is working:
- Percentage of Azure Resource Manager service connections using workload identity federation (target 100 per cent).
- Number of active PATs, and the share that are full-scope or global (target zero of either).
- Service connections with "Open access" or subscription-wide roles (target zero for production).
- Standing PCA members outside PIM (target zero, apart from documented break-glass).
Hardening baseline
Roll out in phases; most settings can break existing automation, so measure before enforcing.
- Visibility (week 1): enable Log Audit Events and a stream to the SIEM. Inventory PAT use, service connections with their schemes and scopes, PCA and Project Administrator members, and installed extensions.
- Admin access (weeks 1 to 2): move PCA membership to a PIM-eligible Entra group, revoke admin PATs, disable external guest access, and enable IP Conditional Access validation for non-interactive flows after testing with build agents' egress addresses.
- Pipeline scope (weeks 2 to 4): enable the job authorization scope settings and Protect access to repositories in YAML pipelines in a pilot project first. Pipelines that check out other projects' repositories will fail; fix them by granting the project build service explicit read access, not by reverting the setting. Disable classic pipeline creation.
- Service connections (weeks 3 to 8): convert eligible ARM connections to workload identity federation (seven-day revert window gives a rollback), recreate shared ones, reduce Azure RBAC to resource groups, turn off Open access and add branch control plus approvals on production.
- PAT policies (weeks 4 to 8): announce dates, enable Restrict global and Restrict full-scoped PAT creation, set a maximum lifespan, then enable Restrict PAT creation with an allowlist group for remaining exceptions. Replace automation PATs with service principals or managed identities.
- Guardrails (ongoing): publish central
extendstemplates with the required template check, restrict Marketplace tasks, separate agent pools by trust level, and review the baseline quarterly.
Pipeline resources, checks and agents
Azure Pipelines separates user permissions (who can edit a resource) from pipeline permissions (which pipelines may use it). The protected resources are repositories, environments, service connections, agent pools, secure files and secret variables in variable groups.
| Control | What it stops | Where to set it |
|---|---|---|
| Pipeline permissions, Open access off | A new or edited pipeline silently using a production connection | Resource, Security, Pipeline permissions |
| Approvals check | Unreviewed deployments | Environment or resource, Approvals and checks |
| Branch control check | Runs from feature or fork branches reaching protected resources | Approvals and checks |
| Required template check | Pipelines that bypass your extends template guardrails | Approvals and checks |
| Disable creation of classic build and release pipelines | Classic pipelines that share service connections without YAML review | Organization settings, Pipelines, Settings |
| Fork protections | Pull requests from forks reading secrets | Pipeline triggers; keep "Make fork builds have the same permissions as regular builds" off |
- Templates as policy: start every pipeline from a central
extendstemplate (for example forcing jobs into containers with read-onlyexternals,tasksandtoolsmounts) and enforce it with the required template check. - Agents: use separate self-hosted agent pools per project or trust level, and rebuild agents from images rather than patching them in place.
Pipeline-wide risks such as poisoned pipeline execution (ATT&CK T1677) are covered in the CI/CD Security Guide and the OWASP CI/CD Top 10 CICD-SEC-4 Poisoned Pipeline Execution Guide.
Decision guidance: a small organisation with one or two projects can usually enable every setting in a fortnight and live without an allowlist. A large organisation with hundreds of projects should enforce scope settings at organisation level (so projects cannot opt out), run the PAT policies with a time-boxed allowlist, and treat service connection conversion as a migration project with owners. Choose a managed identity service connection when the team cannot create app registrations or wants Azure-side lifecycle; choose an app registration with federation when the connection must span subscriptions under a management group with one identity. Keep secret or certificate schemes only as dated exceptions.
| Framework | Relevant controls |
|---|---|
| NIST CSF 2.0 | PR.AA-01 identity and credential management, PR.AA-05 least privilege, PR.PS-01 configuration management, PR.PS-04 log generation, DE.CM-03 monitoring of personnel activity and technology usage |
| NIST SP 800-53 Rev. 5 | AC-2, AC-6, IA-5, AU-2, AU-6, AU-11, CM-3, SA-15 |
| CIS Controls v8.1 | Control 5 Account Management, Control 6 Access Control Management, Control 8 Audit Log Management, Control 16 Application Software Security |
| ISO/IEC 27001:2022 Annex A | 8.2 Privileged access rights, 8.5 Secure authentication, 8.15 Logging, 8.25 Secure development life cycle, 8.32 Change management |
| MITRE ATT&CK v20 | T1528 Steal Application Access Token, T1552.001, T1078.004, T1098.001, T1677, T1195.002, T1213.003 Code Repositories |
Common misconfigurations
- Subscription-wide Contributor on a shared connection: one service connection, created years ago with an automatic app registration secret, used by every pipeline in the project. Any contributor who can edit a YAML file can deploy or exfiltrate from the whole subscription.
- Unrestricted job authorization scope: organisations created before the newer defaults still hand every job a collection-scoped token, so one compromised pipeline can read every repository. Check the setting at organisation level, not just in one project.
- Full-scope PATs on build and integration accounts: a single "svc-ado" user with a one-year full-access PAT stored in a wiki or a scheduled task. Stored engineering secrets of this kind turned one compromise into many in the LastPass breach, where keys in a DevOps engineer's vault unlocked cloud backups.
- Auditing enabled but not streamed: the 90-day built-in retention expires before many investigations start, and a stream disabled for more than seven days loses the gap permanently.
- Self-hosted agents shared across trust levels: a pull request validation build and a production deployment run on the same persistent agent, so workspace files, cached credentials or tools planted by the first are used by the second.
- Offboarding only in Entra: service principals and users removed from Entra ID remain members in Azure DevOps, and their PATs and SSH keys need separate revocation.
Practitioner checklist
- Enable Log Audit Events and stream to Splunk, Azure Monitor Logs or Event Grid, with an alert on stream disablement.
- Put Project Collection Administrators behind a PIM-eligible Entra group and revoke administrator PATs.
- Enable both Limit job authorization scope settings and Protect access to repositories in YAML pipelines at organisation level.
- Convert Azure Resource Manager service connections to workload identity federation and scope them to resource groups.
- Turn off Open access on every service connection, agent pool, variable group and secure file used for production.
- Add approvals, branch control and required template checks to production environments and connections.
- Enable the tenant PAT policies and the organisation Restrict PAT creation policy with a group allowlist.
- Disable creation of classic build and release pipelines and restrict Marketplace tasks.
- Separate self-hosted agent pools by project or trust level and rebuild agents from images.
- Review service connection usage history and inactive identities quarterly, removing them in Azure DevOps as well as Entra ID.
Standards and references
- Microsoft Learn: Make your Azure DevOps secure
- Microsoft Learn: Manage personal access tokens using policies
- Microsoft Learn: Connect to Azure with an Azure Resource Manager service connection
- Microsoft Learn: Job access tokens
- Microsoft Learn: Protect pipeline resources
- Microsoft Learn: Auditing events list
- Microsoft Learn: Create audit streaming
- MITRE ATT&CK: Poisoned Pipeline Execution
Related NHI Mgmt Group resources: GitHub and GitHub Actions Security Guide · CI/CD Pipeline Identity Security Guide · Workload Identity Federation Guide · Azure RBAC and Service Principals Guide · SaaS API Token Security Guide