WIKI
AZ-400 DevOps Engineer

04 · Pipelines : agents, triggers, YAML, ordre d'exécution, réutilisation et checks

46 min de lecture Mis à jour le 21 octobre 2026
Sur cette page

Version : 1.1 · Date : 2026-10-22 · Domaine 3 de l'examen : Design and implement build and release pipelines (50 à 55 %)

En une phrase : un pipeline YAML décrit quoi exécuter (stages, jobs, steps) et quand le lancer (triggers) ; les agents ou runners décident où il tourne (et donc le coût et l'accès réseau) ; les templates évitent de le réécrire ; les approvals and checks, posés hors du YAML sur les ressources protégées, décident s'il a le droit d'avancer.

Un pipeline YAML : du push au déploiement, sous contrôle

Ce que l'examen demande#

Texte officiel du guide d'étude AZ-400 (compétences au 27 juillet 2026), bloc Design and implement pipelines :

# Compétence mesurée (guide officiel, en anglais) Section
3.3.1 Select a deployment automation solution, including GitHub Actions and Azure Pipelines 2
3.3.2 Design and implement a GitHub runner or Azure DevOps agent infrastructure, including cost, tool selection, licenses, connectivity, and maintainability 3
3.3.3 Design and implement integration between GitHub repositories and Azure Pipelines 4
3.3.4 Develop and implement pipeline trigger rules 5
3.3.5 Develop pipelines by using YAML 6
3.3.6 Design and implement a strategy for job execution order, including parallelism and multi-stage pipelines 7
3.3.7 Develop and implement complex pipeline scenarios, such as hybrid pipelines, VM templates, and self-hosted runners or agents 8
3.3.8 Create reusable pipeline elements, including YAML templates, task groups, variables, and variable groups 9
3.3.9 Design and implement checks and approvals by using YAML-based environments 10

Ce qui touche au même domaine est traité ailleurs, sans répétition : les tests et quality gates dans 03 · Packages et tests, les stratégies de déploiement (blue-green, canary, feature flags) dans 05 · Déploiements, la migration classic → YAML, la santé, le coût et la rétention des pipelines dans 06 · IaC et maintenance, les service connections, identités, secrets et OIDC dans 07 · Sécurité et conformité.


1. Les bases : vocabulaire et correspondances#

1.1 La hiérarchie d'un pipeline Azure Pipelines#

Niveau Rôle À retenir
Pipeline Un fichier YAML versionné avec le code (souvent azure-pipelines.yml) Une exécution = un run ; le fichier est lu sur la branche du run
Stage Étape logique (Build, Test, DeployProd) Séquentiels par défaut ; approvals and checks évalués avant chaque stage
Job Ensemble de steps exécutés sur un agent (ou sans agent : pool: server) Chaque job démarre sur un agent propre ; il consomme un parallel job
Step Un script, bash, pwsh, task, checkout, download, publish, template Les steps d'un job partagent le disque de l'agent
Task Brique packagée et versionnée : AzureCLI@2, DotNetCoreCLI@2… Toujours fixer la version majeure (@2)
Artifact Fichiers publiés par un run (publish) et récupérés plus loin (download) Différent d'Azure Artifacts (les feeds de packages, chapitre 03)

Un pipeline sans stage (juste jobs: ou steps:) est accepté : Azure Pipelines crée un stage implicite. Dès qu'il y a du déploiement, écrire les stages explicitement (approvals, retry d'un stage, historique par environment).

1.2 Les mêmes idées dans GitHub Actions#

Azure Pipelines et GitHub Actions : les mêmes idées, deux vocabulaires

Un workflow GitHub (.github/workflows/*.yml) contient des jobs (parallèles par défaut, ordonnés par needs) faits de steps (run ou uses d'une action). Il n'y a pas de stage : un job avec environment: joue ce rôle pour un déploiement. La correspondance sert à lire les questions qui mélangent les deux outils.

# .github/workflows/ci.yml : un workflow GitHub Actions minimal mais réaliste
name: ci
on:
  push:
    branches: [main]
    paths-ignore: ['docs/**']
  pull_request:
    branches: [main]
  workflow_dispatch:            # bouton « Run workflow »
permissions:
  contents: read                # GITHUB_TOKEN au minimum (chapitre 07)
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true      # un nouveau push annule le run en cours sur la même branche
jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
  deploy:
    needs: test                 # ordre : après tous les jobs de la matrice
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production     # protection rules de l'environment (section 10)
    steps:
      - run: echo "déploiement"

2. Choisir entre GitHub Actions et Azure Pipelines#

Les deux outils savent builder n'importe quel langage, déployer partout, utiliser des agents privés et protéger la production. L'examen ne demande pas « lequel est meilleur » mais lequel colle au contexte.

Critère Plutôt GitHub Actions Plutôt Azure Pipelines
Où vit le code GitHub (cloud ou Enterprise) Azure Repos, ou GitHub avec un existant Azure DevOps
Suivi du travail et tests manuels GitHub Issues et Projects Azure Boards, Azure Test Plans
Contrôle des déploiements Environments + deployment protection rules (selon le plan, voir 10.5) Approvals and checks riches : Business Hours, Invoke Azure Function / REST API, Azure Monitor alerts, Required template, Exclusive lock
Écosystème GitHub Marketplace (actions), Dependabot, GitHub Advanced Security Tasks Microsoft et Marketplace Azure DevOps, Azure Artifacts
Classic (UI) existant Non applicable Release pipelines, task groups, deployment groups à migrer vers YAML (chapitre 06)
Facturation Minutes (quota inclus selon le plan ; gratuit pour les dépôts publics avec les runners standard) Parallel jobs (achetés à l'unité) ; minutes seulement pour les GitHub-hosted agents (preview)
Hors ligne / serveur GitHub Enterprise Server Azure DevOps Server

Combinaisons fréquentes et valides :

  • Code et PR sur GitHub, CI/CD dans Azure Pipelines (section 4) : l'équipe garde Azure Boards et les checks avancés.
  • GitHub Actions pour la CI, déclenchement d'un pipeline Azure Pipelines pour la CD (action Azure/pipelines, section 8.1).
  • Migration progressive d'Azure Pipelines vers Actions avec GitHub Actions Importer (gh actions-importer audit puis migrate), voir chapitre 06.

Réflexe d'examen : « l'entreprise utilise déjà Azure Boards et Test Plans et veut des approbations avec plages horaires » → Azure Pipelines. « Tout est dans GitHub, l'équipe veut réutiliser des actions de la Marketplace » → GitHub Actions. La question précise souvent un élément décisif : le chercher.


3. Agents et runners : coût, licences, connectivité, maintenance#

Où tournent les jobs : du plus managé au plus contrôlé

3.1 Les options côté Azure Pipelines#

Option Ce que c'est Quand la choisir
Microsoft-hosted agents VM neuve à chaque job, maintenue par Microsoft (images ubuntu-latest, windows-latest, macOS-latest), taille Standard_DS2_v2 Cas par défaut : rien à gérer, outils courants préinstallés
GitHub-hosted agents (preview) Runners GitHub utilisables par Azure Pipelines (pool GitHub-hosted Agents, activé dans Billing), tailles plus grosses, macOS arm64 (les nouveaux macOS n'arrivent plus que là) Grosses machines ou macOS récents sans rien gérer ; à la minute, sans gratuité, sans consommer de parallel jobs ; 8 agents par taille pendant la preview ; pas de réseau privé ; Azure DevOps Services seulement
Managed DevOps Pools (MDP) Service Azure : agents dans des VM gérées par Microsoft, injectées si besoin dans votre VNet Accès privé, images personnalisées, grosses VM, montée en charge, sans gérer de VM ; recommandé à la place des scale set agents
Azure Virtual Machine Scale Set agents VMSS dans votre abonnement, que Azure Pipelines agrandit et réduit Existant à conserver ; Microsoft oriente vers MDP
Self-hosted agents Logiciel agent installé sur vos VM, serveurs physiques, conteneurs Docker (Linux, Windows, macOS) On-premises, matériel spécifique, licences logicielles liées à une machine, contrôle total

Les agents sont rangés dans des agent pools (niveau organisation, référencés par les projets). Un pool est une ressource protégée : il faut autoriser chaque pipeline (ou ouvrir l'accès à tous) et on peut y poser des checks (section 10).

Choisir l'agent par ses capacités : chaque agent publie des capabilities (système, outils installés, variables personnalisées) ; le job exprime des demands. Utile dans un pool de self-hosted hétérogène :

pool:
  name: pool-onprem-linux
  demands:
  - Agent.OS -equals Linux
  - terraform          # l'agent doit déclarer la capability « terraform »

3.2 Le coût et les licences : les parallel jobs#

Dans Azure Pipelines, on n'achète pas des minutes mais des parallel jobs : le nombre de jobs qui peuvent tourner en même temps dans l'organisation. Au-delà, les jobs attendent dans la file.

Type Gratuit (projets privés) Payant
Microsoft-hosted 1 parallel job, 60 min par job, 1 800 min par mois ; il faut lier l'organisation à un abonnement Azure pour la gratuité des nouvelles organisations Chaque parallel job acheté : minutes illimitées, jusqu'à 360 min par job
Self-hosted (y compris MDP et scale set) 1 parallel job, sans limite de durée, + 1 par abonné Visual Studio Enterprise membre de l'organisation Parallel jobs self-hosted, moins chers que les Microsoft-hosted
GitHub-hosted agents (preview) Aucun À la minute, selon la taille

Points qui reviennent à l'examen :

  • Les utilisateurs (licences Basic, Stakeholder) n'ont rien à voir avec les parallel jobs : un Stakeholder peut lancer des pipelines.
  • Avec MDP ou scale set, on paie deux fois : les parallel jobs self-hosted (Azure DevOps) et les ressources Azure (VM, disques, réseau). Pour réduire la facture : arrêt des agents inactifs, standby agents seulement aux heures ouvrées (planification), images légères.
  • Les projets publics d'Azure DevOps sont en cours de retrait par Microsoft (conversion en privé à prévoir) : ne plus compter sur leur gratuité.
  • Côté GitHub : les runners standard sont gratuits pour les dépôts publics ; pour les dépôts privés, un quota de minutes est inclus par plan (2 000 en Free, 3 000 en Pro et Team, 50 000 en Enterprise Cloud), puis facturation à la minute, plus chère pour Windows et macOS que pour Linux ; les larger runners sont toujours facturés, même en public ; l'usage de self-hosted runners est gratuit côté Actions (on paie ses machines).

3.3 La connectivité : l'agent appelle, personne ne l'appelle#

Un agent privé : il appelle Azure DevOps, personne ne l'appelle

  • L'agent initie toujours la communication : il s'enregistre, puis interroge la file en HTTPS 443 sortant (long polling). Aucun port entrant à ouvrir, ni sur un serveur on-premises ni dans un VNet.
  • Il lui faut la « ligne de vue » vers ce qu'il déploie : un Key Vault en Private Endpoint, un AKS privé ou un serveur on-premises ne sont joignables que par un agent placé dans le réseau (MDP injecté dans le VNet, self-hosted, scale set).
  • Un Microsoft-hosted agent sort par des IP publiques larges et changeantes (plages Azure de la région de l'organisation) : ouvrir un pare-feu à ces plages est fragile. Préférer un agent privé.
  • Derrière un proxy d'entreprise : l'agent se configure avec l'URL du proxy et les adresses Azure DevOps à autoriser en sortie (dev.azure.com, *.visualstudio.com… liste officielle « Allowed address lists »).
  • Enregistrement d'un self-hosted agent : par PAT (scope Agent Pools : Read & manage, utilisé seulement à l'enregistrement), par service principal ou par device code. Ensuite l'agent utilise son propre jeton, et chaque job reçoit un jeton limité au job.
# Enregistrer un agent Linux self-hosted (exemple ; le PAT est saisi au prompt, jamais écrit dans un script)
./config.sh --unattended --url https://dev.azure.com/contoso --auth pat \
  --pool pool-onprem-linux --agent "$(hostname)" --replace
sudo ./svc.sh install && sudo ./svc.sh start   # agent en service, redémarre avec la machine

3.4 La maintenance et le choix des outils#

Sujet Microsoft-hosted / GitHub-hosted MDP Self-hosted
Mises à jour de l'OS et des outils Microsoft (images mises à jour toutes les 3 semaines environ) Images Azure Pipelines tenues par Microsoft, ou votre image (Azure Compute Gallery) Vous
Version de l'agent Automatique Automatique Mise à jour automatique quand une task l'exige (désactivable au niveau du pool)
État entre deux jobs Machine neuve Stateless (neuf à chaque job) ou stateful (réutilisé, jusqu'à 7 jours) Persistant : caches chauds, mais risque de résidus et de fuite entre jobs
Outils spécifiques Installer à chaque run (tasks Use…, Cache@2) Dans l'image personnalisée Installés une fois

Règle de choix des outils : si l'outil s'installe en quelques secondes, l'installer dans le job (versions explicites, reproductible) ; s'il est lourd, l'intégrer à une image (MDP, section 8.2) ; s'il dépend d'une licence liée à la machine ou d'un matériel, self-hosted.

Bonnes pratiques Microsoft pour les self-hosted : un agent par machine (deux agents sur la même machine se gênent : disque, ports, caches), compte de service à privilèges minimaux, agents éphémères quand c'est possible (conteneur lancé avec --once : un job puis l'agent s'arrête), jamais d'agent self-hosted partagé avec des pipelines de PR venant de forks.

3.5 Managed DevOps Pools en détail#

  • Ressource Azure (Microsoft.DevOpsInfrastructure/pools) rattachée à un Dev Center et un projet Dev Center ; les VM sont dans un abonnement Microsoft, vous ne voyez que la ressource pool.
  • Réseau : injection dans un subnet délégué de votre VNet (accès privé, UDR, pare-feu), ou réseau isolé géré par Microsoft.
  • Images : images Azure Pipelines (les mêmes que Microsoft-hosted), images Marketplace sélectionnées, ou images de votre Azure Compute Gallery ; plusieurs images par pool, choisies par le job (demands: ImageOverride -equals …).
  • Montée en charge : nombre maximal d'agents (jusqu'à des milliers), standby agents manuels ou automatiques selon une planification ; jobs jusqu'à 2 jours.
  • Identité : managed identity attachable aux agents (accès à Key Vault, ACR) ; certificats depuis Key Vault.
  • Un pool peut servir plusieurs organisations Azure DevOps.

3.6 Côté GitHub : runners, larger runners, runner groups, ARC#

Option À retenir
GitHub-hosted runners VM neuve par job ; job de 6 h maximum ; runs-on: ubuntu-latest
Larger runners Plans Team et Enterprise ; plus de CPU, GPU, ARM ; IP statiques ; Azure private networking (injection dans un VNet Azure) ; images personnalisées ; facturés à la minute
Self-hosted runners Vos machines ; job jusqu'à 5 jours ; ciblés par labels (runs-on: [self-hosted, linux, x64]) ; --ephemeral pour un job par runner
Runner groups Niveau organisation ou entreprise : limiter quels dépôts et workflows peuvent utiliser quels runners ; un runner appartient à un seul groupe
Actions Runner Controller (ARC) Opérateur Kubernetes (AKS) qui crée des runners éphémères à la demande (runner scale sets), voir AKS · GitOps

Piège de sécurité : jamais de self-hosted runner sur un dépôt public : n'importe qui peut ouvrir une PR depuis un fork et faire exécuter du code sur votre machine. GitHub le déconseille explicitement.

Arbre de décision pour l'examen :

flowchart TD
    A["Besoin d'accès à des ressources privées ?"] -->|non| B["Outils standards suffisent ?"]
    B -->|oui| C["Microsoft-hosted agents / GitHub-hosted runners"]
    B -->|"non, grosses machines"| D["GitHub-hosted agents ou larger runners"]
    A -->|oui| E["Machines on-premises ou matériel spécifique ?"]
    E -->|oui| F["Self-hosted agents / runners"]
    E -->|"non, Azure"| G["Managed DevOps Pools (VNet) / larger runners avec Azure private networking"]

4. Relier un dépôt GitHub à Azure Pipelines#

4.1 Les trois façons de s'authentifier à GitHub#

Méthode Identité qui écrit les statuts Quand l'utiliser
Azure Pipelines GitHub App (recommandée) L'app Azure Pipelines Cas par défaut : statuts riches en GitHub Checks, droits par dépôt choisis à l'installation, builds de PR de forks gérés
OAuth Votre compte GitHub Quand on ne peut pas installer l'app dans l'organisation GitHub ; statuts simples
Personal access token (PAT) Le propriétaire du PAT À éviter : jeton à faire tourner, lié à une personne

Créer le pipeline : Pipelines → New pipeline → GitHub (YAML), choisir le dépôt, installer l'app si demandé, partir d'un modèle ou d'un fichier existant ; Azure Pipelines peut committer azure-pipelines.yml dans le dépôt. Pour un dépôt GitHub référencé dans resources.repositories (templates, deuxième dépôt), il faut une service connection GitHub (endpoint:).

4.2 Protéger main avec le pipeline#

  1. Le YAML contient un trigger pr vers main (section 5.2).
  2. Dans GitHub, une branch protection rule ou un ruleset sur main exige le status check du pipeline avant le merge.
  3. Résultat : chaque PR est validée par Azure Pipelines, le résultat apparaît dans l'onglet Checks de la PR.

Commentaires de PR utiles : /azp run (relancer tous les pipelines liés), /azp run <nom> (un seul) ; seuls les membres autorisés peuvent les utiliser.

4.3 Les PR venant de forks#

Dans Edit → Triggers du pipeline (onglet Pull request validation) :

Réglage Valeur prudente Pourquoi
Build pull requests from forks of this repository Activé si contributions externes Sinon aucune validation des PR de forks
Make secrets available to builds of forks Désactivé (défaut) Un fork pourrait exfiltrer les secrets
Make fork builds have the same permissions as regular builds Désactivé Jeton du job limité
Require a team member's comment before building a pull request Activé pour les dépôts publics Un humain relit avant d'exécuter du code inconnu

Et toujours : pas d'agent self-hosted pour ces builds.


5. Les règles de déclenchement (triggers)#

Ce qui déclenche un pipeline

5.1 Le trigger CI#

trigger:
  batch: true                 # pendant un run, les pushes suivants sont regroupés en un seul run
  branches:
    include: [main, releases/*]
    exclude: [releases/old*]
  paths:
    include: [src/*]          # sensible à la casse ; pas de variables
    exclude: [docs/*]
  tags:
    include: [v*]             # les tags ne déclenchent rien sans cette ligne
Règle Détail
Pas de section trigger CI implicite sur toutes les branches, sauf si le réglage Disable implied YAML CI trigger est activé (organisation ou projet)
trigger: none Plus aucun déclenchement CI (pipeline manuel, planifié ou déclenché par une ressource)
exclude sans include Équivaut à include: ['*'] puis exclusion
Branches et tags Un push qui correspond à l'un ou l'autre déclenche
Templates Les triggers ne se déclarent que dans le fichier principal, jamais dans un template
Version du YAML utilisée CI : celle de la branche poussée ; PR : celle de la branche source de la PR
Messages de commit [skip ci], [ci skip], ***NO_CI***… dans le message : pas de run CI

5.2 Le trigger PR#

pr:
  branches:
    include: [main]
  paths:
    exclude: [docs/*]
  drafts: false               # GitHub : ne pas valider les PR en brouillon

Le mot-clé pr ne vaut que pour GitHub et Bitbucket Cloud. Pour Azure Repos, la validation de PR se configure par une branch policy → Build validation sur la branche cible (chapitre 02) ; un pr: dans le YAML y est ignoré.

5.3 Les schedules#

schedules:
- cron: '0 2 * * Mon-Fri'     # toujours en UTC
  displayName: Nightly
  branches:
    include: [main]
  always: false               # défaut : ne tourne que si le code a changé depuis le dernier run planifié réussi
  • Les horaires sont en UTC : un « 2 h à Paris » s'écrit 0 0 ou 0 1 selon l'heure d'été.
  • Si des schedules sont définis dans l'UI du pipeline, ils remplacent ceux du YAML.
  • Build.CronSchedule.DisplayName indique quel schedule a lancé le run ; Build.Reason vaut Schedule.

5.4 Les triggers par ressources#

resources:
  pipelines:
  - pipeline: appci            # alias local
    source: App-CI             # nom du pipeline amont dans le projet
    trigger:
      branches:
        include: [main]
      stages: [Build]          # se déclencher quand ce stage amont est terminé
      tags: [release]          # seulement les runs amont portant ce tag
  repositories:
  - repository: config
    type: git
    name: Platform/app-config
    trigger:
      branches:
        include: [main]        # un push dans un autre dépôt Azure Repos déclenche ce pipeline

Autres ressources déclenchantes : containers (nouvelle image dans Azure Container Registry), packages (nouveau package), webhooks (appel HTTP entrant d'un système externe via une service connection Incoming WebHook). Dans le pipeline aval, les artifacts du pipeline amont se récupèrent avec - download: appci.

5.5 Les équivalents GitHub Actions#

Besoin GitHub Actions
Push, PR, filtres on: push / pull_request avec branches, branches-ignore, paths, paths-ignore, tags
Planifié on: schedule: - cron: '…' (UTC, branche par défaut)
Manuel avec saisie on: workflow_dispatch: inputs:
Après un autre workflow on: workflow_run: workflows: [ci] types: [completed]
Événement externe on: repository_dispatch (appel d'API)
Appelé par un autre workflow on: workflow_call (section 9.5)

6. Écrire un pipeline en YAML#

6.1 Un pipeline complet, commenté#

# azure-pipelines.yml : build .NET puis déploiement App Service en dev puis prod
trigger:
  branches:
    include: [main]

parameters:
- name: runTests
  displayName: Lancer les tests
  type: boolean
  default: true

variables:
- group: app-common                  # variable group de la Library (section 9.4)
- name: buildConfiguration
  value: Release

stages:
- stage: Build
  pool:
    vmImage: ubuntu-latest
  jobs:
  - job: Build
    steps:
    - task: DotNetCoreCLI@2
      inputs:
        command: publish
        projects: src/**/*.csproj
        arguments: -c $(buildConfiguration) -o $(Build.ArtifactStagingDirectory)
    - ${{ if parameters.runTests }}:   # insertion à la compilation : le step n'existe pas si false
      - task: DotNetCoreCLI@2
        inputs:
          command: test
          projects: tests/**/*.csproj
    - publish: $(Build.ArtifactStagingDirectory)
      artifact: drop

- stage: DeployDev
  dependsOn: Build
  jobs:
  - deployment: Web
    environment: dev
    pool:
      vmImage: ubuntu-latest
    strategy:
      runOnce:
        deploy:
          steps:                       # l'artifact « drop » est téléchargé automatiquement
          - task: AzureWebApp@1
            inputs:
              azureSubscription: sc-dev-wif      # service connection (workload identity federation)
              appName: app-contoso-dev
              package: $(Pipeline.Workspace)/drop/**/*.zip

- stage: DeployProd                   # même deployment job que DeployDev, avec environment: prod,
  dependsOn: DeployDev                 # sc-prod-wif et app-contoso-prod (en vrai : un template, section 9)
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))

Les approvals and checks de la prod ne s'écrivent pas ici : ils sont posés sur l'environment prod (section 10).

6.2 Variables : trois syntaxes, trois moments#

Trois syntaxes, trois moments d'évaluation

Syntaxe Évaluée Où l'utiliser Si la variable n'existe pas
${{ variables.x }} (template expression) À la compilation du YAML, avant la mise en file Structure : if, each, choix de template, clé ou valeur Chaîne vide
$[ variables.x ] (runtime expression) Au démarrage du job ou du stage Conditions, variables calculées à partir de sorties Chaîne vide
$(x) (macro) Juste avant chaque task Entrées de tasks, scripts Reste écrit $(x)
  • Une variable est une chaîne, modifiable par les steps : echo "##vso[task.setvariable variable=x]valeur". La nouvelle valeur n'est visible qu'aux steps suivants.
  • Dans les scripts, les variables non secrètes sont aussi des variables d'environnement (nom en majuscules, . remplacé par _ : $BUILD_SOURCEBRANCH). Les secrets ne le sont pas : il faut les passer explicitement (env:), voir chapitre 07.
  • readonly: true empêche un step de modifier une variable.
  • Priorité quand le même nom existe à plusieurs endroits (du plus fort au plus faible) : job (YAML) > stage (YAML) > racine du pipeline (YAML) > valeur saisie au lancement (queue time) > variable de l'UI du pipeline. Une variable du YAML ne peut donc pas être changée au lancement : pour cela, la définir dans l'UI avec Let users override this value when running this pipeline, ou mieux, utiliser un parameter.
  • Deux variable groups liés au même niveau qui ont une variable de même nom : résultat imprévisible, éviter les doublons.

6.3 Parameters#

parameters:
- name: environment
  type: string
  default: dev
  values: [dev, qa, prod]          # liste déroulante dans « Run pipeline »
- name: regions
  type: object
  default: [westeurope, northeurope]

stages:
- ${{ each region in parameters.regions }}:   # un stage par région, généré à la compilation
  - stage: Deploy_${{ region }}
    jobs:
    - job: Deploy
      steps:
      - script: echo "Déploiement ${{ parameters.environment }} en ${{ region }}"
Parameters Variables
Type Typés : string, number, boolean, object, step, stepList, job, jobList, stage, stageList… Toujours des chaînes
Modifiables pendant le run Non (immuables après la mise en file) Oui
Disponibles à la compilation Oui : pilotent if et each Seulement les variables définies dans le YAML et certaines variables prédéfinies
Secrets Jamais Oui (variables secrètes, variable groups, Key Vault)

Microsoft recommande les parameters plutôt que les variables settable at queue time pour les choix de l'utilisateur : valeurs contrôlées, typées, non modifiables par un step.

6.4 Conditions et expressions#

- job: Notify
  dependsOn: Deploy
  condition: failed()                  # seulement si Deploy a échoué
- job: Cleanup
  dependsOn: Deploy
  condition: always()                  # même en cas d'échec ou d'annulation
- job: Release
  condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/releases/'))
  • Condition par défaut : succeeded(). Écrire une condition remplace la condition par défaut : une condition eq(variables['Build.Reason'], 'Manual') seule fait tourner le job même si le précédent a échoué. Toujours combiner avec succeeded() si c'est voulu.
  • Fonctions de statut : succeeded(), failed(), succeededOrFailed(), always(), canceled().
  • Fonctions utiles : eq, ne, and, or, not, in, contains, startsWith, endsWith, coalesce, format, counter (numéro de version auto-incrémenté).
  • Noms de variables avec un point : syntaxe index obligatoire dans les expressions : variables['Build.SourceBranch'].

6.5 Les variables prédéfinies à connaître#

Variable Contenu
Build.SourceBranch / Build.SourceBranchName refs/heads/main / main
Build.Reason IndividualCI, BatchedCI, PullRequest, Schedule, Manual, ResourceTrigger…
Build.BuildId, Build.BuildNumber Identifiant du run, numéro affiché (personnalisable avec name:)
Build.ArtifactStagingDirectory Dossier local où préparer les artifacts
Pipeline.Workspace Racine de travail ; artifacts téléchargés dans $(Pipeline.Workspace)/<artifact>
System.PullRequest.TargetBranch Branche cible d'une PR
System.AccessToken Jeton du job (identité Build Service), à mapper explicitement dans env:

7. Ordre d'exécution, parallélisme et multi-stage#

Ordre d'exécution : un graphe de stages, des jobs en parallèle

7.1 Les règles d'ordre#

Niveau Par défaut Changer l'ordre
Stages Séquentiels, dans l'ordre du fichier dependsOn: X ; dependsOn: [] pour démarrer tout de suite (parallèle) ; liste pour un fan-in
Jobs d'un stage Parallèles (si assez d'agents et de parallel jobs) dependsOn entre jobs
Steps d'un job Séquentiels Impossible de paralléliser des steps : en faire des jobs
stages:                       # jobs omis pour la lisibilité
- stage: Build
- stage: DeployUS
  dependsOn: Build
- stage: DeployEU
  dependsOn: Build            # en parallèle de DeployUS (fan-out)
- stage: Smoke
  dependsOn: [DeployUS, DeployEU]   # attend les deux (fan-in)
- stage: Lint
  dependsOn: []               # indépendant, démarre dès le début
- stage: Prod
  dependsOn: Smoke
  trigger: manual             # stage lancé à la main, quand on veut
  • Un pipeline doit avoir au moins un stage sans dépendance.
  • trigger: manual sur un stage : il ne démarre qu'à la demande ; le run se termine quand il ne reste que des stages manuels.
  • isSkippable: false : le stage ne peut pas être décoché dans Stages to run (ex. scan de sécurité imposé).
  • On peut relancer un stage échoué (Rerun failed jobs) sans tout reconstruire.

7.2 Paralléliser : matrix et multi-agent#

jobs:
- job: Test
  strategy:
    matrix:
      linux:
        imageName: ubuntu-latest
      windows:
        imageName: windows-latest
      mac:
        imageName: macOS-latest
    maxParallel: 2            # au plus 2 copies en même temps
  pool:
    vmImage: $(imageName)
  steps:
  - script: dotnet test
  • matrix : une copie du job par entrée (jusqu'à 256 jobs par stage) ; maxParallel limite la consommation de parallel jobs.
  • parallel: 4 : 4 copies identiques du job, pour découper une suite de tests (variables System.JobPositionInPhase, System.TotalJobsInPhase).
  • Une variable ne peut pas alimenter une matrix (elle n'existe qu'au début de chaque job) ; une sortie d'un job précédent le peut.
  • Le vrai plafond est le nombre de parallel jobs de l'organisation : 10 jobs parallèles possibles, 1 parallel job acheté → ils passent un par un.

7.3 Passer une valeur d'un job ou d'un stage à l'autre#

stages:
- stage: Build
  jobs:
  - job: Package
    steps:
    - bash: echo "##vso[task.setvariable variable=version;isOutput=true]1.4.$(Build.BuildId)"
      name: setver                     # le nom du step fait partie de la référence
  - job: Sign
    dependsOn: Package
    variables:
      version: $[ dependencies.Package.outputs['setver.version'] ]         # même stage
    steps:
    - script: echo $(version)
- stage: Deploy
  dependsOn: Build                     # obligatoire pour lire les sorties de Build
  variables:
    version: $[ stageDependencies.Build.Package.outputs['setver.version'] ]  # autre stage
  jobs:
  - job: Show
    steps:
    - script: echo $(version)
  • isOutput=true est obligatoire pour qu'une variable sorte du job.
  • Même stage : dependencies.<job>.outputs['<step>.<var>'] ; autre stage : stageDependencies.<stage>.<job>.outputs[...].
  • Pour une sortie d'un deployment job, la référence inclut le nom du hook ou du job (outputs['<job>.<step>.<var>'], ou outputs['<hook>_<ressource>.<step>.<var>'] selon la stratégie) : vérifier la doc avant d'écrire.
  • Le stage lecteur doit dépendre (dependsOn) du stage producteur.

7.4 Jobs sans agent, timeouts, artifacts et cache#

Agentless jobs (pool: server) : ils tournent sur le service Azure Pipelines, sans agent. Tasks possibles : Delay, InvokeRESTAPI, AzureFunction, ManualValidation, AzureMonitor (alertes), PublishToAzureServiceBus, queryWorkItems. Durée maximale : 30 jours.

- job: WaitForDba
  pool: server
  timeoutInMinutes: 1440               # 1 jour
  steps:
  - task: ManualValidation@1
    inputs:
      notifyUsers: '[email protected]'
      instructions: 'Valider le script de migration avant la mise en production'
      onTimeout: reject

Timeouts : timeoutInMinutes (défaut 60) ; 0 = le maximum possible (illimité en self-hosted, 60 ou 360 min en Microsoft-hosted selon gratuit ou payant) ; cancelTimeoutInMinutes pour laisser le temps de nettoyer.

Artifacts : publish / download (pipeline artifacts, rapides, liés au run). Dans un deployment job, les artifacts du run sont téléchargés automatiquement (- download: none pour l'éviter).

Cache (Cache@2) : restaure un dossier (packages npm, NuGet, Maven) à partir d'une clé ; mise à jour en fin de job réussi seulement si la clé était absente ; le cache expire après 7 jours sans usage ; pour le vider, changer la clé.

variables:
  npm_config_cache: $(Pipeline.Workspace)/.npm
steps:
- task: Cache@2
  inputs:
    key: 'npm | "$(Agent.OS)" | package-lock.json'   # change dès que le lock change
    restoreKeys: |
      npm | "$(Agent.OS)"
    path: $(npm_config_cache)
- script: npm ci

7.5 Côté GitHub Actions#

Besoin GitHub Actions
Ordre des jobs needs: [build, lint] (sinon parallèles)
Condition if: ${{ success() }}, failure(), always(), cancelled() ; if: always() pour un job de nettoyage
Matrice strategy.matrix (max 256 jobs), max-parallel, fail-fast, include / exclude
Sortie d'un job step : echo "version=1.4" >> "$GITHUB_OUTPUT" ; job : outputs: version: ${{ steps.setver.outputs.version }} ; lecture : needs.package.outputs.version
Limiter les exécutions simultanées concurrency: group: … cancel-in-progress: true ; queue: max garde une file (100 en attente au plus) au lieu d'annuler
Durées job GitHub-hosted 6 h, self-hosted 5 jours, run complet 35 jours
Cache actions/cache (10 Go par dépôt), cache intégré des actions setup-*

8. Scénarios complexes : hybride, images de VM, agents self-hosted#

8.1 Pipelines hybrides#

« Hybride » recouvre trois réalités à l'examen :

  1. Cloud + on-premises dans le même pipeline : build sur Microsoft-hosted (rien à gérer), déploiement sur un pool self-hosted dans le datacenter (ligne de vue vers les serveurs). Chaque job choisit son pool.

    Même structure que la section 6.1 ; seul le pool change d'un stage à l'autre : vmImage: ubuntu-latest pour le build, name: pool-datacenter-paris (self-hosted, HTTPS sortant seulement) pour le deployment job vers l'environment onprem-prod.

  2. GitHub Actions + Azure Pipelines : la CI dans Actions, la CD (avec ses checks) dans Azure Pipelines. L'action Azure/pipelines déclenche un pipeline Azure Pipelines depuis un workflow (elle demande un PAT Azure DevOps rangé dans les secrets GitHub, scope minimal, rotation) ; dans l'autre sens, un pipeline peut lancer un workflow par l'API workflow_dispatch.

  3. Classic + YAML : un pipeline YAML déclenché par la fin d'un ancien build classic (ressource pipelines), le temps de la migration (chapitre 06).

8.2 Les « VM templates » : images d'agents et déploiements sur VM#

Le guide ne définit pas le terme ; deux sens sont utiles :

  • Images personnalisées pour les agents : partir des modèles Packer publiés par Microsoft et GitHub (dépôt actions/runner-images, qui produit les images des Microsoft-hosted agents et GitHub-hosted runners), y ajouter ses outils, publier l'image dans une Azure Compute Gallery (versionnée, répliquée), puis la donner à un Managed DevOps Pool ou à un scale set. Un pipeline planifié reconstruit l'image chaque mois : correctifs de sécurité et outils à jour, sans toucher aux agents à la main.

  • Déployer sur des VM : ajouter les VM comme resources de type Virtual machine dans un environment (un script installe un agent sur chaque VM, avec des tags : web, api), puis utiliser un deployment job avec la stratégie rolling :

    - deployment: Web
      environment:
        name: prod-vms
        resourceType: VirtualMachine
        tags: web                        # seulement les VM marquées « web »
      strategy:
        rolling:
          maxParallel: 25%               # un quart des VM à la fois
          deploy:
            steps:
            - script: sudo systemctl restart contoso-web
    

    Les deployment groups font la même chose en classic (release pipelines) ; en YAML, ce sont les environments.

  • Sens plus large, si la question parle de créer les VM : templates Bicep / ARM ou modules Terraform déployés par le pipeline (IaC, chapitre 06).

8.3 Agents et runners self-hosted dans un scénario#

Contrainte de l'énoncé Réponse
Déployer sur un AKS privé ou un SQL en Private Endpoint Agent dans le VNet : MDP avec injection VNet (ou self-hosted)
Serveurs on-premises sans entrée depuis Internet Self-hosted agent on-premises : HTTPS sortant uniquement
Logiciel sous licence installé sur un poste précis Self-hosted, capability personnalisée + demands
Builds iOS / macOS Microsoft-hosted macOS-latest, GitHub-hosted (arm64), ou Mac self-hosted
Beaucoup de jobs courts, coûts à maîtriser, isolation Agents éphémères : MDP stateless, conteneur --once, runners ARC sur AKS
GitHub, dépôts privés, accès à un VNet Azure Larger runners avec Azure private networking, ou self-hosted / ARC dans le VNet
Isoler les runners d'une équipe sensible Runner group limité à ses dépôts (GitHub) ; pool dédié avec permissions (Azure DevOps)

Container jobs : faire tourner les steps d'un job dans un conteneur (outils figés, même environnement partout), sur un agent Linux ou Windows qui a Docker ; services: démarre des conteneurs annexes (base de test).

pool:
  vmImage: ubuntu-latest
container: mcr.microsoft.com/dotnet/sdk:8.0     # les steps tournent dans ce conteneur
steps:
- script: dotnet --info

9. Éléments réutilisables : templates, task groups, variables, variable groups#

Templates : inclure pour réutiliser, étendre pour imposer

9.1 Templates d'inclusion (includes)#

Un fichier YAML de stages, jobs, steps ou variables, avec des parameters, inséré là où on l'appelle.

# templates/build-dotnet.yml
parameters:
- name: configuration
  type: string
  default: Release
- name: projects
  type: string
steps:
- task: DotNetCoreCLI@2
  inputs:
    command: build
    projects: ${{ parameters.projects }}
    arguments: -c ${{ parameters.configuration }}
# azure-pipelines.yml
steps:
- template: templates/build-dotnet.yml
  parameters:
    projects: src/**/*.csproj
  • Template d'un autre dépôt : déclarer le dépôt dans resources.repositories et appeler - template: build-dotnet.yml@templates. Ancrer sur un tag ou un commit (ref: refs/tags/v1) pour qu'une modification du dépôt central ne casse pas tous les pipelines d'un coup.
  • Limites : 100 fichiers YAML inclus au plus, 100 niveaux d'imbrication, 20 Mo de mémoire pour analyser le YAML (en pratique 0,6 à 2 Mo de fichiers).
  • Templates de variables : variables: - template: vars/prod.yml.

9.2 Templates d'extension (extends) : imposer la structure#

# secure.yml, dans le dépôt Platform/pipeline-templates
parameters:
- name: buildSteps
  type: stepList                       # l'équipe fournit ses steps, le template les encadre
  default: []
stages:
- stage: Build
  jobs:
  - job: Build
    steps:
    - script: echo "contrôles imposés avant le build"
    - ${{ each step in parameters.buildSteps }}:
      - ${{ step }}
    - script: echo "scan imposé après le build"
      target:
        commands: restricted           # ce step ne peut pas lancer de logging commands dangereuses
# azure-pipelines.yml de l'équipe
resources:
  repositories:
  - repository: templates
    type: git
    name: Platform/pipeline-templates
    ref: refs/tags/v1
extends:
  template: secure.yml@templates
  parameters:
    buildSteps:
    - task: DotNetCoreCLI@2
      inputs:
        command: build
  • Un seul extends par pipeline, au niveau racine.
  • Le template peut parcourir les steps reçus et refuser certains types (par exemple interdire script et n'accepter que des tasks), injecter des scans avant et après.
  • Pour le rendre obligatoire : check Required template sur la service connection ou l'environment de production (section 10) ; un pipeline qui n'étend pas secure.yml@templates à la bonne référence est bloqué.
Includes Extends
But Réutiliser, éviter le copier-coller Imposer une structure et des contrôles
Combien Autant qu'on veut, à tous les niveaux Un par pipeline
Qui garde la main L'auteur du pipeline L'auteur du template
Contrôle possible Aucun Required template check

9.3 Task groups (classic)#

Un task group regroupe plusieurs tasks d'un pipeline classic en une seule, avec des paramètres, dans Pipelines → Task groups. Ils n'existent pas en YAML : leur équivalent est le template de steps. Lors d'une migration, un task group devient un template (chapitre 06). Les nouvelles organisations peuvent désactiver la création de pipelines classic (et donc des task groups).

9.4 Variables partagées et variable groups#

Variable group : ensemble de variables stocké dans Pipelines → Library, utilisable par plusieurs pipelines.

variables:
- group: app-prod                    # le pipeline doit être autorisé (Pipeline permissions du groupe)
- name: region
  value: westeurope
  • C'est une ressource protégée : autorisation par pipeline, sécurité par rôle, et checks possibles (approval, branch control).
  • Option Link secrets from an Azure key vault as variables : les secrets restent dans Key Vault, le groupe n'en garde que les noms ; valeurs lues à l'exécution par la service connection (droits Get et List sur les secrets, ou rôle RBAC Key Vault Secrets User). Détails et alternatives (task AzureKeyVault@2, OIDC) dans le chapitre 07.
  • Variables secrètes : masquées dans les logs, non exposées en variables d'environnement, non transmises aux builds de forks ; jamais dans un parameter ni dans le YAML.
  • Lier un groupe à un seul stage : déclarer - group: dans les variables de ce stage (ex. app-prod seulement dans DeployProd), ou utiliser une insertion conditionnelle ${{ if }}.

9.5 Côté GitHub : reusable workflows et composite actions#

GitHub Actions : reusable workflow ou composite action

# contoso/ci/.github/workflows/deploy.yml : le reusable workflow
on:
  workflow_call:
    inputs:
      environment:
        type: string
        required: true             # outputs: possibles aussi (on:workflow_call:outputs, relayés depuis un job)
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - run: echo "déploiement en ${{ inputs.environment }}"
# workflow appelant
jobs:
  prod:
    uses: contoso/ci/.github/workflows/deploy.yml@<sha-du-commit>   # SHA : le plus sûr ; pas d'expression dans uses
    with:
      environment: production
    secrets: inherit
# .github/actions/setup-tf/action.yml : une composite action
name: setup-tf
inputs:
  version:
    required: true
runs:
  using: composite
  steps:
    - uses: hashicorp/setup-terraform@v3
      with:
        terraform_version: ${{ inputs.version }}
    - run: terraform version
      shell: bash                  # shell obligatoire pour un run dans une composite action
Reusable workflow Composite action
Contient Des jobs complets (runner, environment, permissions) Des steps seulement
Appelé Au niveau d'un job (jobs.<id>.uses) Comme un step (steps[*].uses)
Secrets Oui (secrets: ou secrets: inherit) Non (passer des inputs)
Journaux Chaque job et step visible Vu comme un seul step
Imbrication Jusqu'à 10 niveaux Jusqu'à 10 composite actions imbriquées
Partage Dépôt public, ou interne / privé avec accès accordé Idem, et Marketplace

Pour partager des variables : variables de configuration au niveau organisation, dépôt ou environment (${{ vars.NOM }}) ; pour des secrets : secrets d'organisation, de dépôt ou d'environment.


10. Environments, approvals and checks#

Approvals and checks : l'ordre d'évaluation avant un stage

10.1 Les environments Azure Pipelines#

Un environment (Pipelines → Environments) est la cible logique d'un déploiement : dev, qa, prod. Il apporte :

  • l'historique des déploiements (quel run, quels commits et work items sont arrivés en prod) : traçabilité ;
  • des resources optionnelles : namespaces Kubernetes (AKS ou autre) et Virtual machines (agent installé) ;
  • des approvals and checks et une sécurité par rôle ;
  • il est créé automatiquement au premier déploiement s'il n'existe pas (sans checks : le créer avant pour la prod).

Un environment n'est utilisé que par un deployment job (- deployment: + environment:), qui ajoute une strategy :

Strategy Hooks Usage
runOnce preDeploy, deploy, routeTraffic, postRouteTraffic, on: success / failure Cas courant
rolling Idem VM seulement, maxParallel (nombre ou %)
canary Idem, avec increments: [10, 20] Kubernetes, montée progressive (chapitre 05)

10.2 Approvals and checks : qui, où, dans quel ordre#

  • Posés hors du YAML, par le propriétaire de la ressource, sur : environments, service connections, agent pools, repositories, variable groups, secure files. L'auteur du YAML ne peut ni les voir ni les contourner en modifiant le fichier.
  • Évalués avant le stage qui utilise la ressource ; tous les checks de toutes les ressources du stage doivent passer.
  • Cinq catégories, dans cet ordre ; dans une catégorie, les checks tournent dans leur ordre de création et sont réévalués à leur intervalle de retry ; un échec définitif (approbation refusée) ou un timeout = stage non exécuté (on peut relancer le stage après un timeout).
Catégorie Checks Exemple d'usage
1. Static checks Branch control (branches autorisées, branche protégée exigée), Required template, Evaluate artifact (politique sur une image de conteneur) Seule main déploie en prod ; seuls les pipelines qui étendent le template sécurisé
2. Pre-check approvals Approval évaluée avant les checks dynamiques Valider avant de consommer des ressources de test
3. Dynamic checks Approval, Invoke Azure Function, Invoke REST API, Business Hours, Query Azure Monitor alerts Ticket de change ServiceNow ouvert ; pas d'alerte active ; seulement en heures ouvrées
4. Post-check approvals Approval après les checks dynamiques L'humain valide en voyant le résultat des checks automatiques
5. Exclusive lock Un seul run à la fois sur la ressource Deux pipelines ne déploient pas en même temps sur la même prod

L'approval : approbateurs (utilisateurs ou groupes : un seul membre du groupe suffit), nombre minimal, instructions, option Approvers can't approve their own runs, timeout (30 jours par défaut ; sans réponse, le stage est sauté) ; l'approbateur peut différer (Defer approval) jusqu'à une heure donnée. En urgence (hotfix), Approval, Business Hours, Invoke Azure Function et Invoke REST API peuvent être contournés (Bypass check), seulement par un utilisateur qui a la permission Administrator sur la ressource ; le nom de celui qui a contourné reste visible dans le panneau des checks.

L'exclusive lock se règle dans le YAML, au niveau pipeline ou stage :

lockBehavior: sequential   # chaque run attend son tour ; défaut runLatest : seul le plus récent passe, les anciens sont annulés

Au niveau d'un stage, runLatest n'annule que les runs de la même branche.

10.3 ManualValidation, checks ou conditions : que choisir ?#

Besoin Outil
Une personne doit valider avant la prod, quel que soit le pipeline qui y déploie Approval sur l'environment ou la service connection
Une pause en plein milieu d'un pipeline, décidée par l'auteur Task ManualValidation dans un agentless job
Une règle technique (branche, heure, alerte, ticket) Check sur la ressource
Un choix logique du pipeline (branche, raison du run) condition du stage ou du job
Lancer la prod plus tard, à la main Stage trigger: manual (+ approval si contrôle humain obligatoire)

Séquence d'un run vers la production :

sequenceDiagram
    participant P as Pipeline run
    participant E as Environment prod
    participant A as Approbateur
    participant G as Agent
    P->>E: le stage DeployProd demande la ressource
    E->>E: Branch control et Required template
    E->>E: Business Hours et Azure Monitor alerts
    E->>A: Approval demandée
    A-->>E: Approve
    E->>E: Exclusive lock obtenu
    E-->>P: feu vert
    P->>G: deployment job runOnce
    G-->>E: déploiement enregistré dans l'historique

10.4 Sécurité des environments#

Rôles sur un environment : Reader (voir), User (l'utiliser dans un pipeline YAML), Creator (rôle de niveau projet : créer des environments ; les Contributors l'ont par défaut), Administrator (gérer les permissions et l'environment ; le créateur l'est d'office). Comme les autres ressources protégées, un environment a des Pipeline permissions : autoriser seulement les pipelines prévus.

10.5 Les environments GitHub#

Fonction Détail
Required reviewers Jusqu'à 6 personnes ou équipes ; une approbation suffit ; option Prevent self-review
Wait timer 1 à 43 200 minutes (30 jours) avant le démarrage du job
Deployment branches and tags Toutes, seulement les branches protégées, ou une liste de motifs
Custom deployment protection rules (preview) GitHub Apps tierces qui valident (ex. outil de change, monitoring) ; jusqu'à 6 règles
Secrets et variables d'environment Disponibles seulement aux jobs qui ciblent l'environment, après validation des règles
Contournement Par défaut, les administrateurs peuvent passer outre ; décocher Allow administrators to bypass configured protection rules

Disponibilité selon le plan (GitHub Docs, 22/10/2026) : sur les dépôts publics, tout est disponible ; en Free, rien sur les dépôts privés ; en Pro et Team, les dépôts privés ont les environments, leurs secrets et les deployment branches and tags, mais pas les required reviewers, le wait timer, l'admin bypass ni les custom rules ; il faut GitHub Enterprise pour les avoir sur des dépôts privés. Un classique des questions « pourquoi l'option n'apparaît pas ».

Dans le workflow : environment: { name: production, url: … } sur le job (exemple en section 1.2), et concurrency: avec cancel-in-progress: false au niveau du job pour un seul déploiement prod à la fois (l'équivalent de l'exclusive lock).


11. Labs conseillés#

Organisation Azure DevOps (1 parallel job Microsoft-hosted gratuit si liée à un abonnement Azure) et compte GitHub Free. Coût Azure nul sauf les labs 3 et 4 (quelques euros si on supprime tout le jour même). Toujours dans un abonnement de test.

  1. Pipeline multi-stage et triggers (gratuit) : importer un petit projet dans Azure Repos, écrire le pipeline de la section 6.1 avec trigger filtré par paths, un schedules et un parameter booléen. Attendu : un push dans docs/ ne déclenche rien ; le parameter fait apparaître ou disparaître le step de test. Ajouter [skip ci] à un commit.
  2. GitHub + Azure Pipelines (gratuit) : depuis un dépôt GitHub, créer un pipeline avec l'Azure Pipelines GitHub App, ajouter pr: sur main, puis une branch protection rule qui exige le check. Attendu : la PR est bloquée tant que le check échoue. Tester /azp run.
  3. Environment protégé : créer prod avec une Approval, un Business Hours et un Branch control (refs/heads/main), déployer une Web App (plan F1 gratuit ou B1) avec une service connection en workload identity federation. Attendu : le run depuis une autre branche est bloqué au check statique, celui de main attend l'approbation. Nettoyage : supprimer le resource group et la service connection.
  4. Managed DevOps Pool dans un VNet : enregistrer le resource provider Microsoft.DevOpsInfrastructure, créer un Dev Center et un pool de 1 agent (taille la plus petite, image Azure Pipelines, subnet délégué), lancer un job qui lit un secret d'un Key Vault en Private Endpoint. Attendu : réussi depuis le pool, échec depuis ubuntu-latest. Nettoyage : supprimer le resource group (pool, Dev Center, VNet, Key Vault) le jour même.
  5. Réutilisation (gratuit) : extraire le build dans un template d'un dépôt pipeline-templates ancré sur le tag v1, puis faire un template extends et un check Required template sur l'environment prod. Côté GitHub, transformer le job de déploiement en reusable workflow appelé par SHA.

12. Pièges d'examen#

Situation Réponse attendue Pourquoi
pr: dans un YAML sur Azure Repos, rien ne se passe Branch policy → Build validation pr ne vaut que pour GitHub et Bitbucket
Pipeline qui tourne sur toutes les branches sans trigger CI implicite ; écrire trigger ou activer Disable implied YAML CI trigger Comportement par défaut
Variable utilisée dans trigger.branches ou paths Impossible Triggers évalués avant toute variable
Trigger déclaré dans un template Ignoré : le mettre dans le fichier principal Règle documentée
Un push de tag ne lance rien Ajouter tags: include: Les tags ne déclenchent pas par défaut
Schedule à 2 h « heure de Paris » décalé Cron en UTC Pas de fuseau dans schedules
Schedules du YAML ignorés Des schedules existent dans l'UI L'UI l'emporte
Condition personnalisée : le job tourne alors que le précédent a échoué Ajouter succeeded() dans and(...) La condition remplace la condition par défaut
Lire une sortie d'un autre stage stageDependencies.Stage.Job.outputs['step.var'] + dependsOn + isOutput=true Les trois sont nécessaires
${{ variables.x }} vide alors que x est définie dans un script La valeur n'existe qu'à l'exécution : $[ ] ou $( ) ${{ }} est évalué à la compilation
Un secret passé en parameter Interdit : variable secrète ou Key Vault Parameters visibles et non masqués
Changer une variable du YAML au lancement Impossible : parameter, ou variable de l'UI overridable Le YAML a priorité et n'est pas settable at queue time
Jobs « parallèles » qui passent un par un Pas assez de parallel jobs Le parallélisme est une licence
Stage indépendant qui attend le précédent dependsOn: [] Stages séquentiels par défaut
Microsoft-hosted agent n'atteint pas un AKS privé MDP dans le VNet ou self-hosted Pas de ligne de vue
Ouvrir un port entrant pour l'agent on-premises Aucun : HTTPS sortant L'agent tire les jobs
Self-hosted runner sur un dépôt GitHub public Jamais Code de forks exécuté sur la machine
Required reviewers absents sur un dépôt privé GitHub Team Plan Enterprise nécessaire Disponibilité par plan
Approbation exigée par le YAML Les approvals se posent sur la ressource (environment…), pas dans le YAML Contrôle du propriétaire, non contournable
Deux runs déploient en même temps en prod Exclusive lock (Azure) ; concurrency (GitHub) Sérialiser l'accès
Task groups en YAML N'existent pas : templates de steps Classic uniquement
Deployment groups en YAML Environments avec resources VM Classic uniquement
Plusieurs extends dans un pipeline Un seul Règle du schéma
Template central modifié qui casse tous les pipelines Référencer un tag (ref: refs/tags/v1) ou un SHA Versionner les templates
Reusable workflow appelé avec uses: …@${{ inputs.ref }} Interdit : pas d'expression dans uses Référence figée
Composite action qui a besoin d'un secret Le passer en input Pas de contexte secrets dans une composite action

13. Scénarios corrigés#

1. Le code est sur GitHub. L'entreprise suit tout dans Azure Boards, exige que les déploiements en prod n'aient lieu qu'en heures ouvrées après approbation du CAB, et veut voir le résultat du build dans la PR.

Azure Pipelines avec l'Azure Pipelines GitHub App (statuts en GitHub Checks), pr: sur main + branch protection qui exige le check ; environment prod avec Approval (groupe CAB) et Business Hours.

2. Un pipeline doit déployer sur un AKS privé et lire des secrets dans un Key Vault en Private Endpoint. L'équipe ne veut gérer aucune VM.

Managed DevOps Pool injecté dans un subnet du VNet (images Azure Pipelines, agents stateless). Pas de Microsoft-hosted (pas de ligne de vue), pas de self-hosted (VM à maintenir).

3. Le pipeline a 3 jobs de test indépendants qui durent 20 minutes chacun et s'exécutent l'un après l'autre. Le YAML ne contient aucun dependsOn entre eux.

Le YAML est déjà parallèle : il manque des parallel jobs. En acheter (ou en ajouter en self-hosted) ; maxParallel permet ensuite de plafonner la consommation.

4. Un stage Integration doit démarrer en même temps que Build, et Release doit attendre Build et Integration.

Integration : dependsOn: [] ; Release : dependsOn: [Build, Integration].

5. Le stage Deploy doit utiliser la version calculée par un script du job Package du stage Build.

Dans le script : ##vso[task.setvariable variable=version;isOutput=true]…, step nommé (name: setver) ; dans Deploy : dependsOn: Build et version: $[ stageDependencies.Build.Package.outputs['setver.version'] ].

6. La sécurité veut que tous les pipelines qui déploient en production passent un scan de dépendances, sans dépendre de la bonne volonté des équipes.

Template extends central (scan injecté avant et après les steps de l'équipe, isSkippable: false si stage dédié), ancré sur un tag ; check Required template sur la service connection et l'environment de prod.

7. Un pipeline nocturne doit tourner à 22 h heure de Paris du lundi au vendredi, même sans changement de code.

schedules avec un cron en UTC (0 20 * * Mon-Fri en été, 0 21 en hiver) et always: true ; vérifier qu'aucun schedule n'est défini dans l'UI.

8. Le pipeline App-CD doit démarrer automatiquement quand le stage Build du pipeline App-CI réussit sur main, et récupérer son artifact.

resources.pipelines avec source: App-CI, trigger: branches: include: [main] et stages: [Build] ; - download: <alias>.

9. Une organisation sur GitHub Team veut que deux personnes de l'équipe Ops valident les déploiements en production d'un dépôt privé. L'option Required reviewers n'apparaît pas.

Limite de plan : les required reviewers sur dépôt privé demandent GitHub Enterprise. Sinon : passer par Azure Pipelines (approvals), ou une custom deployment protection rule si le plan la permet.

10. Plusieurs équipes copient les mêmes 6 steps de build Node.js dans leurs workflows GitHub, et une équipe impose en plus tout le déploiement (jobs, environment, runner).

Les steps → composite action dans un dépôt partagé ; le déploiement complet → reusable workflow (on: workflow_call) appelé au niveau du job, référencé par SHA, secrets: inherit ou secrets explicites.

11. Une application doit être déployée sur 40 VM Linux on-premises, par quart, en sautant les VM de test.

Environment avec resources Virtual machine (agent installé sur chaque VM, tags web et test), deployment job strategy: rolling avec maxParallel: 25% et tags: web. Deployment groups seulement en classic.

12. Deux runs successifs du même pipeline se retrouvent à déployer en même temps sur la prod, et l'équipe veut que seul le plus récent passe.

Check Exclusive lock sur l'environment ; lockBehavior: runLatest (défaut) annule les runs plus anciens en attente ; sequential s'il faut les passer tous, dans l'ordre.


14. Pour aller plus loin#

  • GitHub Actions Importer : gh actions-importer audit azure-devops puis dry-run et migrate pour estimer et convertir des pipelines Azure DevOps en workflows (chapitre 06).
  • Templates et sécurité : templateContext pour passer des propriétés aux stages ou jobs d'un template extends, injection de decorators (extensions qui ajoutent des steps à tous les pipelines d'une organisation).
  • Azure DevOps MCP Server : interroger runs, logs et échecs en langage naturel depuis un agent IA (analyse de causes, suggestions de correctifs YAML).
  • Historique de déploiement par groupe de stages : propriété group: d'un stage (Sprint 271).
  • Runner scale sets (ARC) sur AKS avec runners éphémères et nœuds Spot pour réduire le coût des runners self-hosted.
  • Merge queue GitHub et déclencheur merge_group pour valider les PR dans l'ordre de fusion.

15. Fiche à retenir#

  • Hiérarchie : stage → job (un agent, un parallel job) → step → task ; GitHub : jobs → steps, pas de stage.
  • Choix : tout sur GitHub → Actions ; Boards, Test Plans, checks avancés, existant Azure DevOps → Azure Pipelines (qui builde aussi GitHub).
  • Agents : Microsoft-hosted (neuf, 60 min gratuit, 360 payant) ; MDP pour le privé sans gérer de VM (recommandé à la place des scale sets) ; self-hosted pour l'on-premises ; GitHub-hosted agents en preview, à la minute.
  • Licences : parallel jobs ; gratuit = 1 Microsoft-hosted (1 800 min/mois) + 1 self-hosted (+1 par abonné VS Enterprise).
  • L'agent sort en HTTPS 443, aucun port entrant ; il lui faut la ligne de vue vers les cibles privées.
  • GitHub : larger runners (IP statiques, Azure private networking), runner groups, ARC ; jamais de self-hosted sur dépôt public.
  • GitHub + Azure Pipelines : GitHub App, pr: + branch protection exigeant le check ; forks sans secrets.
  • Triggers : pas de trigger = CI implicite ; trigger: none ; exclude seul = include * ; tags non déclenchants par défaut ; pas de variables ; pas dans un template ; cron en UTC, l'UI l'emporte ; pr = GitHub/Bitbucket seulement.
  • ${{ }} compilation, $[ ] runtime, $( ) macro ; parameters typés, immuables, jamais secrets.
  • Condition personnalisée = remplace succeeded().
  • Stages séquentiels (dependsOn: [] pour paralléliser), jobs parallèles, matrix 256 / maxParallel ; sorties : isOutput=true + dependencies / stageDependencies ; trigger: manual et isSkippable: false sur un stage.
  • Agentless job (pool: server) : ManualValidation, InvokeRESTAPI, Delay… ; timeoutInMinutes 60 par défaut.
  • Templates : includes (réutiliser, stages/jobs/steps/variables) vs extends (un seul, imposer) + Required template ; ancrer sur un tag ; task groups et deployment groups = classic.
  • Variable groups : ressource protégée, liaison Key Vault ; secrets à passer explicitement aux scripts.
  • Checks posés hors YAML sur environments, service connections, agent pools, repositories, variable groups, secure files ; ordre : static → pre-check approvals → dynamic → post-check approvals → exclusive lock (runLatest / sequential).
  • Deployment job : runOnce, rolling (VM), canary ; artifacts téléchargés automatiquement.
  • GitHub environments : 6 reviewers max, wait timer 30 jours, branches autorisées, prevent self-review ; privé = Enterprise.
  • Reusable workflow (jobs, secrets, appel au niveau job, 10 niveaux) vs composite action (steps, pas de secrets, Marketplace).

Références#

Pages consultées le 21/10/2026.

Microsoft Learn (Azure Pipelines) :

GitHub Docs :

Sur cette page
Échap

Tape au moins 2 lettres. Plusieurs mots : tous doivent apparaître.