Les pipelines Azure DevOps se décrivent aujourd’hui en YAML, dans un fichier azure-pipelines.yml versionné avec le code. L’éditeur graphique « classique » existe encore, mais toutes les nouveautés (modèles, environnements, vérifications) ne sont disponibles qu’en YAML. Ce tutoriel construit un pipeline complet pour une application Node.js : construction, tests avec publication des résultats, création d’un artefact, puis déploiement vers deux environnements dont un protégé par une approbation manuelle. Chaque bloc est expliqué, et la dernière partie montre comment factoriser le pipeline avec des modèles.
Prérequis : une organisation et un projet Azure DevOps (voir l’introduction à Azure DevOps), un dépôt Git contenant une application Node.js avec npm test, et le parallélisme gratuit accordé (ou un agent auto-hébergé).
Structure d’un pipeline YAML
| Niveau | Rôle | Exécution |
|---|---|---|
trigger / pr / schedules | Quand le pipeline démarre | — |
variables | Valeurs réutilisées ; peuvent venir d’un groupe de variables | — |
stages | Grandes phases (Build, Test, Déploiement) | Séquentielles par défaut |
jobs | Unité de travail exécutée sur un agent | Parallèles au sein d’une étape |
steps | Tâches (task), scripts (script, bash, pwsh) et mots-clés (checkout, publish, download) | Séquentiels |
Le pipeline complet
# azure-pipelines.yml
name: $(Date:yyyyMMdd)$(Rev:.r) # numéro de build lisible : 20260910.3
trigger:
branches:
include: [main, release/*]
paths:
exclude: [docs/*, README.md] # pas de build pour la documentation seule
pr:
branches:
include: [main] # validation des pull requests
schedules:
- cron: "0 3 * * 1" # tous les lundis 03:00 UTC
displayName: Build hebdomadaire
branches:
include: [main]
always: false # seulement s'il y a des changements
variables:
- group: app-commun # groupe de variables (Library) : registre, URLs…
- name: nodeVersion
value: '22.x'
- name: artifactName
value: webapp
pool:
vmImage: ubuntu-latest # agent hébergé par Microsoft
stages:
# ---------------------------------------------------------------- BUILD
- stage: Build
displayName: Construction et tests
jobs:
- job: BuildTest
displayName: npm ci, lint, test
steps:
- checkout: self
fetchDepth: 1 # clone superficiel : plus rapide
- task: NodeTool@0
displayName: Installer Node.js $(nodeVersion)
inputs:
versionSpec: $(nodeVersion)
- task: Cache@2
displayName: Cache npm
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
path: $(Pipeline.Workspace)/.npm
- script: |
npm ci --cache $(Pipeline.Workspace)/.npm --prefer-offline
npm run lint
displayName: Installer et analyser
- script: npm test -- --ci --reporters=default --reporters=jest-junit
displayName: Tests unitaires
env:
JEST_JUNIT_OUTPUT_DIR: $(Common.TestResultsDirectory)
- task: PublishTestResults@2
displayName: Publier les résultats de tests
condition: succeededOrFailed() # même si les tests échouent
inputs:
testResultsFormat: JUnit
testResultsFiles: '$(Common.TestResultsDirectory)/*.xml'
- script: npm run build
displayName: Construire l'application
- task: ArchiveFiles@2
displayName: Archiver dist/
inputs:
rootFolderOrFile: dist
includeRootFolder: false
archiveFile: $(Build.ArtifactStagingDirectory)/$(artifactName).zip
- publish: $(Build.ArtifactStagingDirectory)/$(artifactName).zip
artifact: $(artifactName)
displayName: Publier l'artefact
# ---------------------------------------------------------------- DEV
- stage: Dev
displayName: Déploiement dev
dependsOn: Build
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
jobs:
- template: templates/deploy.yml
parameters:
environmentName: dev
webAppName: $(webAppBaseName)-dev # vient du groupe de variables
# ---------------------------------------------------------------- PROD
- stage: Prod
displayName: Déploiement production
dependsOn: Dev
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- template: templates/deploy.yml
parameters:
environmentName: production # approbation configurée sur l'environnement
webAppName: $(webAppBaseName)-prodLe modèle de déploiement réutilisable
Un modèle (template) est un fichier YAML paramétré inclus par les étapes. Dev et Prod exécutent exactement les mêmes pas ; seule la cible change, ce qui élimine les divergences entre environnements.
# templates/deploy.yml
parameters:
- name: environmentName
type: string
values: [dev, staging, production]
- name: webAppName
type: string
jobs:
- deployment: Deploy
displayName: Déployer vers ${{ parameters.environmentName }}
environment: ${{ parameters.environmentName }} # déclenche approbations et vérifications
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: webapp
- task: AzureWebApp@1
displayName: Déployer sur App Service
inputs:
azureSubscription: sc-azure-labo # connexion de service (fédération d'identité)
appType: webAppLinux
appName: ${{ parameters.webAppName }}
package: $(Pipeline.Workspace)/webapp/webapp.zip
runtimeStack: NODE|22-lts
- bash: |
for i in $(seq 1 12); do
code=$(curl -s -o /dev/null -w '%{http_code}' "https://${{ parameters.webAppName }}.azurewebsites.net/healthz")
[ "$code" = "200" ] && echo "OK" && exit 0
echo "tentative $i : HTTP $code" ; sleep 10
done
echo "Le service ne répond pas" ; exit 1
displayName: Test de fuméeConfigurer l’approbation sur l’environnement production
- Menu Pipelines → Environments ; l’environnement
productiona été créé automatiquement au premier passage (ou créez-le manuellement). - Ouvrez-le, cliquez sur ⋮ → Approvals and checks → Approvals, ajoutez le groupe des responsables de mise en production, cochez Requester should not approve.
- Ajoutez éventuellement Branch control (seule
refs/heads/mainpeut déployer) et Business hours.
À la prochaine exécution, l’étape Prod s’arrête avec le statut En attente d’approbation ; les approbateurs reçoivent un courriel et valident depuis le résumé de l’exécution. Le fonctionnement détaillé (portes automatiques, permutation d’emplacements) est dans notre tutoriel sur les approbations et portes.
Variables, secrets et expressions
steps:
# Variables prédéfinies utiles
- script: |
echo "Dépôt : $(Build.Repository.Name)"
echo "Branche : $(Build.SourceBranchName)"
echo "Commit : $(Build.SourceVersion)"
echo "Raison : $(Build.Reason)" # IndividualCI, PullRequest, Schedule, Manual
echo "Agent : $(Agent.Name) / $(Agent.OS)"
displayName: Variables prédéfinies
# Secret : jamais interpolé directement dans le script, passé par env
- script: ./deploy.sh
env:
API_TOKEN: $(apiToken) # variable secrète du groupe de variables
# Définir une variable pour les pas suivants
- bash: echo "##vso[task.setvariable variable=version;isOutput=true]1.4.$(Build.BuildId)"
name: ver
- script: echo "Version calculée : $(ver.version)"Trois syntaxes coexistent : ${{ }} est évalué à la compilation du pipeline (paramètres, conditions de modèles), $[ ] à l’exécution (dépendances entre jobs), et $( ) est la substitution classique de variables dans les pas. Les secrets ne sont jamais affichés dans les journaux et ne sont pas transmis automatiquement aux scripts : il faut les mapper avec env:.
Exécuter le pipeline
- Pipelines → New pipeline → Azure Repos Git (ou GitHub) → votre dépôt → Existing Azure Pipelines YAML file →
/azure-pipelines.yml. - Cliquez Run. Au premier passage, autorisez l’accès au groupe de variables et à la connexion de service (Permit).
- Suivez les étapes en direct ; l’onglet Tests affiche les résultats JUnit, l’onglet Artifacts le fichier
webapp.zip.
# Depuis la ligne de commande avec l'extension Azure DevOps de l'Azure CLI
az extension add --name azure-devops
az devops configure --defaults organization=https://dev.azure.com/<votre-organisation> project=devops-lab
az pipelines run --name "webapp" --branch main
az pipelines runs list --top 5 -o tableErreurs fréquentes
- « No hosted parallelism has been purchased or granted » – demandez le parallélisme gratuit via le formulaire Microsoft ou utilisez un agent auto-hébergé.
- « Variable group was not found or is not authorized » – ouvrez le groupe dans Library et autorisez le pipeline (Pipeline permissions).
- Erreur d’indentation YAML – utilisez l’éditeur intégré d’Azure DevOps ou l’extension VS Code « Azure Pipelines » qui valide le schéma ; les tabulations sont interdites.
- L’étape Prod est ignorée – la condition sur
Build.SourceBranchn’est vraie que pourmain; les exécutions de pull request ont une brancherefs/pull/…. - L’approbation n’apparaît pas – le job est un
jobordinaire et non undeployment, ou l’environnement n’a pas de vérification configurée.
À retenir
- Un pipeline YAML = déclencheurs + variables + étapes → jobs → pas ; il vit dans Git avec le code.
- Publiez toujours les résultats de tests et un artefact ; déployez cet artefact, jamais un nouveau build.
- Les jobs
deploymentliés à un environnement apportent approbations, vérifications et historique. - Factorisez avec des modèles ; passez les secrets par
env:; préférez la fédération d’identité aux clés.
Pour continuer : agents auto-hébergés sur Linux et infrastructure avec ARM et Bicep. Documentation officielle : Schéma YAML Azure Pipelines.
Retour parcours Azure DevOps — hub de la série et leçons sœurs.


