Your global Personal Access Token dies on December 1 in Azure DevOps Services: Here's the migration

The deadline Microsoft's announcement forgot to mention

Your global Personal Access Token dies on December 1 in Azure DevOps Services: Here's the migration

Microsoft shipped the Azure DevOps service connection last week, and the announcement immediately makes the case you'd expect from a modern platform: no more tokens to create, store, and rotate. And better yet, the service connection gives per-pipeline permissions instead of a shared build account, including an audit trail.

But what the post doesn't mention is the date.

A date that was shared the previous year in December, when every global personal access token in Azure DevOps Services is going to get decommissioned.

That's sixteen weeks from today. And a global PAT exists for exactly one reason: reaching across organization boundaries. And that makes this news so interesting. The global PAT (or credential) and what seems to be the answer have never been put in the same sentence.

Here's a quick migration guide for one pipeline, and four traps that can cost you an afternoon scratching your head.

Step 1: Check whether this is actually your problem

Half of the anxiety about the deadline back in December belongs to people who actually don't even have the problem. Why do I say this?

A PAT is global if it reaches multiple organizations rather than a single named one. You can see that when you open your own under User settings -> Personal access tokens.

Figure 1: Global PATs

Admins who create it under Organization Settings -> Security -> Personal access tokens -> All PATs, is where Microsoft's own guidance says to go hunting for. You can use the PAT Lifecycle Management API that returns targetAccounts per token:

curl -H "Authorization: Bearer <entra-token>" \
  "https://dev.azure.com/<org>/_apis/tokens/pats?api-version=7.1-preview.1"

And that's where the anxiety can be lifted. Anything scoped to one organization keeps working on 2 December exactly as it does today. This retirement covers global PATs only, and it is also only for Azure DevOps Services, not Azure DevOps Server.

Now that you know about the token, you need to find where it actually lives. A rough list that people often forget:

  • Variable groups and secret pipeline variables
  • Key Vault-linked variable groups
  • Git remotes with credentials baked into the URL
  • Config files like .npmrc and nuget.config committed in the repo (bad, bad, bad)
  • The AZURE_DEVOPS_EXT_PAT environment variable

So a small triage rule: if a token only ever touches one organization, just re-scope it to an organization and stop reading. If it crosses organizations, read on. The service connection just might be the answer for you.

Step 2: Let the build length decide the identity

Okay, the service connection that is in preview is going to need a Microsoft Entra identity. But when you create one, it isn't going to make you one. It also won't add it for you in Azure DevOps.

Both have to exist before you start, which is something that wasn't included in the announcement's screenshots. That begs the question: which type do I need? A service principal or managed identity?

Most are going to say that it's a style preference, but it actually isn't. It's a token-lifetime decision. You're going to find it out when you got it wrong somewhere around minute sixty-one of a long build:

AADSTS700024: Client assertion is not within its valid time range

Entra tokens for a service principal expire hourly. For managed identities, they last for 24 hours. If your builds run long, that's the whole decision, and it's the only difference most teams will ever feel.

The rule here: builds under an hour either work (service principal or managed identity). For builds over an hour, use managed identity. If you're running agents in Azure, just use managed identity regardless, because you skip credential handling entirely.

There are also two blockers to check now rather than in November, which can potentially require someone else's approval:

  • AADSTS70052: multitenant app registrations (signInAudience: AzureADMultipleOrgs) aren't supported by the Entra issuer. Use AzureADMyOrg and one connection per tenant.
  • AADSTS700223 / AADSTS700238: workload identity federation is constrained or disabled at the tenant level. Only an Entra admin can lift that.

Step 3: Add it to Azure DevOps without tripping

Four clicks, three ways to get it wrong.

The tutorial on Microsoft Learn describes perfectly how to add a service principal or managed identity. But there can be three traps:

  1. The wrong object ID. For service principals, grab the object ID from the Enterprise applications, not the app registration one. The app registration ID is the easier one to find, which is why everyone grabs it in the first place.
  2. Stakeholder. Stakeholder licenses don't grant repository access, and nothing in the error says so:
The Git repository with name or identifier <name> does not exist or you do not have permissions
  1. Licensing arithmetic. Or better yet: calculation. Each identity consumes a license in every organization it joins. There's no discount for multi-org, and group licensing rules don't apply. Microsoft's guidance is to use separate service connections per project or organization, which is fine, because many connections can share one identity. The billable unit is identity × organization, not connection. Don't mint an identity per pipeline.

Then permissions. Make it easy for yourself and assign the least-privilege (Readers) on the project, plus whatever specific feed or repo permissions the identity needs.

Step 4: Swap the YAML pipeline code

The fun part for the developers, DevOps engineers, or others who create the DevOps pipelines. Every place the PAT appeared becomes a named connection, and the token handling easily disappears. For example, before:

variables:
  - group: shared-secrets      # holds GLOBAL_PAT

steps:
  - script: |
      dotnet nuget add source https://pkgs.dev.azure.com/platform-engineering/_packaging/shared/nuget/v3/index.json \
        --name shared --username az --password $(GLOBAL_PAT) --store-password-in-clear-text
      dotnet restore

And after:

resources:
  repositories:
    - repository: platform-templates
      type: git
      endpoint: platform-connection
      name: 'platform/build-templates'
      ref: refs/heads/main

steps:
  - task: NuGetAuthenticate@1
    inputs:
      nuGetServiceConnections: 'platform-connection'

  - task: DotNetCoreCLI@2
    inputs:
      command: 'restore'
      projects: '**/*.csproj'

Here's a short list of where the connection name goes for each task:

Task Input
NuGetAuthenticate@1 nuGetServiceConnections
npmAuthenticate@0, PipAuthenticate@1, MavenAuthenticate@0 azureDevOpsServiceConnection (alias: workloadIdentityServiceConnection)
AzureCLI@3 connectionType: 'azureDevOps' + azureDevOpsServiceConnection
InvokeRESTAPI@1 connectionType: 'connectedServiceNameAzureDevOps' + serviceConnection
repository resource endpoint:

[!IMPORTANT]
One small catch on the npm, pip and Maven task. In the announcement it isn't covered that when you use the service connection, the feedUrl is going to be required! It overrides the other feed inputs.

For simple REST calls (AzureCLI@3) with connectionType: azureDevOps, modify the Authorization: Basic <base64 PAT> header. Inside the script, the az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 gets you that token. That GUID is Azure DevOps, and it's worth memorizing.

What doesn't move yet

You've gone through the steps, am I there? Well, not fully. This won't get you to zero PATs.

The feature is still in preview, meaning that changes can be expected. Don't start building irreversible tooling on it. Service principals cannot create PATs or SSH keys, cannot sign in interactively, cannot own a whole organization, and don't support Azure DevOps OAuth flows.

Anything depending on those still needs a different plan, which isn't covered in this article. There are also some sovereign clouds blocking workload identity federation (AADSTS900382).

Then one more thing. Put one more date on the same calendar. The Azure DevOps issuer (https://vstoken.dev.azure.com) was deprecated on 1 July 2026 and reaches end of life on 1 July 2027, with warnings on existing connections in the meantime. A bulk conversion CLI is expected in autumn 2026.

December is the deadline for the global PAT specifically. Treat it as the forcing function for the wider mode.