Microsoft

Azure DevOps: ottimizzare la gestione delle policy Git su larga scala con refName=~all

Dario Fadda Maggio 28, 2026

Gestire le policy Git su centinaia di repository in Azure DevOps è una sfida comune nelle grandi organizzazioni. Per farlo in modo affidabile, molti team utilizzano servizi di automazione che verificano e correggono periodicamente la configurazione delle policy. Fino a poco fa, questa automazione era penalizzata da un’importante limitazione dell’API REST di Azure DevOps: non esisteva un modo per recuperare tutte le policy applicabili a un dato repository in una singola chiamata.

Con un singolo miglioramento all’endpoint REST, il team di Azure DevOps ha ottenuto una riduzione del 50% nell’utilizzo CPU lato server e un’accelerazione da 10x a 15x nei tempi di esecuzione complessivi. Vediamo come funziona e perché è rilevante per chi gestisce pipeline di governance automatizzata.

Come funzionano le policy Git in Azure Repos

Azure Repos supporta due categorie di policy:

  • Push policies (o repository policies): si applicano all’intero repository, indipendentemente dal branch. Esempio: blocco dei commit contenenti segreti o credenziali.
  • Branch policies: proteggono branch specifici e richiedono che le modifiche passino attraverso pull request. Esempio: numero minimo di reviewer, stato delle build CI.

Tutte le policy di un progetto sono memorizzate in un contenitore logico a livello di progetto, non per singolo repository o branch. Ogni policy ha un campo Scope che specifica dove nella gerarchia si applica:

# Push policy per uno specifico repo (senza ref)
2c938d1f6e6f458d816484fc51e7cf74

# Branch policy per il branch main di un repo specifico
2c938d1f6e6f458d816484fc51e7cf74:refs/heads/main

# Branch policy cross-repo (tutti i branch releases/* in tutti i repo)
*:refs/heads/releases/*

Il problema: due endpoint, nessuno sufficiente

Azure DevOps espone due endpoint per recuperare le configurazioni delle policy:

GET /_apis/policy/configurations

L’endpoint più datato. Consente di filtrare per valore esatto dello scope, ma senza supporto per l’ereditarietà. Se si passa lo scope 2c938d...74:refs/heads/releases/v1, non vengono restituite le policy con scope *:refs/heads/releases/* che pure si applicano a quel branch.

GET /_apis/git/policy/configurations

L’endpoint più moderno, con supporto all’ereditarietà. Accetta repositoryId e refName e restituisce tutte le policy che si applicano a quel branch, incluse quelle ereditate da scope più generici. È utile per rispondere alla domanda “cosa protegge il branch releases/v1 nel repo X?”

Il problema: passando solo repositoryId senza refName, questo endpoint restituisce solo le push policy applicabili all’intero repository. Le branch policy non vengono incluse perché non si applicano all’intero repo ma a branch specifici.

Il risultato pratico: un servizio di governance che deve conoscere tutte le policy applicabili a un repository — push e branch — era costretto a recuperare l’intero set di policy del progetto e filtrare lato client. In progetti con migliaia di repository e centinaia di migliaia di policy, questo significava serializzare e trasferire centinaia di megabyte di dati per ogni singola chiamata.

La soluzione: refName=~all

L’endpoint GET /_apis/git/policy/configurations supporta ora un valore speciale per il parametro refName: ~all.

Quando si passa repositoryId=<ID>&refName=~all, la risposta include:

  • Tutte le branch policy che si applicano a qualsiasi branch nel repository (dirette e ereditate)
  • Tutte le push policy applicabili all’intero repository
  • Le policy di progetto che si applicano a tutti i repository (scope *)

Internamente, il server filtra l’intero set di policy del progetto mantenendo solo quelle con scope che inizia con * (policy a livello di progetto) o con l’ID del repository richiesto. Tutta la logica di filtering si sposta dal client al server, con payload di risposta enormemente ridotti.

Esempio di chiamata REST

GET https://dev.azure.com/{organization}/{project}/_apis/git/policy/configurations
    ?repositoryId=2c938d1f-6e6f-458d-8164-84fc51e7cf74
    &refName=~all
    &api-version=7.2

Authorization: Basic {token}

Prima di questa modifica, la stessa informazione richiedeva una chiamata all’endpoint /_apis/policy/configurations senza filtri (o con il solo repositoryId), seguita da filtering lato client su tutto il set di policy del progetto.

Impatto sulle prestazioni

Il team di Microsoft ha misurato l’impatto dopo aver migrato il proprio servizio interno di governance delle policy a usare refName=~all:

  • CPU lato server: riduzione del 50% del consumo complessivo per questo client
  • Tempo di esecuzione totale: da 1.000–3.000 ore/giorno a circa 100–150 ore/giorno, con un miglioramento da 10x a 15x

I guadagni sono proporzionali alla dimensione del progetto: più repository e policy contiene, maggiore è il vantaggio del filtering server-side rispetto al trasferimento dell’intero dataset.

Come aggiornare l’automazione esistente

Se si dispone di un servizio o script che recupera le policy per singolo repository, il refactoring è minimo. Ecco un esempio di migrazione in Python con la libreria requests:

import requests

ORG = "myorg"
PROJECT = "myproject"
REPO_ID = "2c938d1f-6e6f-458d-8164-84fc51e7cf74"
TOKEN = "..."  # PAT Azure DevOps

headers = {
    "Authorization": f"Basic {TOKEN}",
    "Content-Type": "application/json"
}

# PRIMA: recupero di tutte le policy del progetto + filtering client-side
# url = f"https://dev.azure.com/{ORG}/{PROJECT}/_apis/policy/configurations?api-version=7.2"
# response = requests.get(url, headers=headers)
# all_policies = response.json()["value"]
# repo_policies = [p for p in all_policies if REPO_ID in str(p.get("settings", {}).get("scope", []))]

# DOPO: server-side filtering con refName=~all
url = (
    f"https://dev.azure.com/{ORG}/{PROJECT}/_apis/git/policy/configurations"
    f"?repositoryId={REPO_ID}&refName=~all&api-version=7.2"
)
response = requests.get(url, headers=headers)
repo_policies = response.json()["value"]

print(f"Policy trovate per il repository: {len(repo_policies)}")

Il risultato è lo stesso, ma il server restituisce solo le policy rilevanti per quel repository, eliminando il traffico inutile e il carico di elaborazione lato client.

Disponibilità

La funzionalità è già disponibile per tutti gli utenti di Azure DevOps Services (cloud). Per quanto riguarda Azure DevOps Server (on-premise), sarà inclusa nel prossimo aggiornamento major previsto per la seconda metà del 2026.

Conclusione

Questo tipo di ottimizzazione — spostare il lavoro dal client al server tramite un parametro aggiuntivo — è un esempio classico di come miglioramenti relativamente semplici all’API possano avere impatti drastici sulle prestazioni a scala. Per chi gestisce governance automatizzata su organizzazioni Azure DevOps con molti repository, l’adozione di refName=~all è immediata e i guadagni in termini di latenza e carico sono significativi.

La documentazione completa degli endpoint è disponibile su Microsoft Learn:

Fonte originale: Optimizing Git policy management at scale — Azure DevOps Blog

💬 Unisciti alla discussione!


Questo è un blog del Fediverso: puoi trovare quindi questo articolo ovunque con @blog@spcnet.it e ogni commento/risposta apparirà qui sotto.

Se vuoi commentare su Azure DevOps: ottimizzare la gestione delle policy Git su larga scala con refName=~all, utilizza la discussione sul Forum.
Condividi la tua esperienza, confrontati con altri professionisti e approfondisci i dettagli tecnici nel nostro 👉 forum community