Introduction
Azure Pipelines, le service CI/CD d'Azure DevOps, est l'outil incontournable pour automatiser les builds, tests et déploiements en 2026. Contrairement aux pipelines basiques, les implémentations avancées gèrent des environnements multiples (dev, staging, prod), des approbations manuelles, des templates réutilisables et des stratégies de déploiement zéro downtime.
Ce tutoriel expert vous guide pas à pas pour créer une pipeline complète sur un projet Node.js : build, tests unitaires, linting, publication d'artefacts, et déploiement progressif sur Azure App Service. Vous apprendrez à optimiser les performances avec le caching, à sécuriser les secrets via Azure Key Vault, et à implémenter des gates pour la compliance.
Pourquoi c'est crucial ? Dans un monde DevOps mature, 80% des incidents de prod proviennent de déploiements défaillants. Une pipeline experte réduit ce risque de 90%, accélère les releases et scale horizontalement. Prêt à bookmarker ce guide de référence ? (128 mots)
Prérequis
- Compte Azure DevOps avec projet et repo Git (Node.js app exemple).
- Azure Subscription avec App Service créé (noms :
myapp-dev,myapp-staging,myapp-prod). - Azure CLI installé et connecté (
az login). - Variables d'environnement dans Azure DevOps :
AZURE_SUBSCRIPTION_ID,KEY_VAULT_NAME. - Connaissances avancées en YAML, Git et Node.js.
- Service Connection 'AzureRM' configuré pour le déploiement.
Pipeline YAML basique : Build et Tests
trigger:
branches:
include:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
nodeVersion: '20.x'
stages:
- stage: BuildAndTest
displayName: 'Build & Test'
jobs:
- job: Build
displayName: 'Node Build'
steps:
- task: NodeTool@0
inputs:
versionSpec: $(nodeVersion)
displayName: 'Install Node.js'
- script: |
npm ci
npm run build
displayName: 'npm ci & build'
- job: Test
displayName: 'Unit Tests'
dependsOn: Build
steps:
- task: NodeTool@0
inputs:
versionSpec: $(nodeVersion)
- script: |
npm ci
npm run test:ci
npm run lint
displayName: 'Tests & Lint'
env:
CODECOV_TOKEN: $(codecovToken)
- task: PublishTestResults@2
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '**/junit.xml'
condition: succeededOrFailed()
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '**/coverage/cobertura-coverage.xml'Ce pipeline de base déclenche sur main et develop, utilise un pool Ubuntu avec Node 20. Il sépare build et tests en jobs parallélisables, publie les résultats et coverage. Piège : sans dependsOn, les jobs parallèles risquent des faux positifs ; activez condition: succeededOrFailed() pour capturer les échecs.
Optimisation : Caching et Artefacts
Après le build basique, optimisez avec le caching des dépendances npm pour réduire les temps de 70%. Publiez des artefacts pour les stages suivants, évitant les rebuilds inutiles. Analogie : comme un CDN pour vos node_modules, cela accélère les runs itératifs.
Ajout du Caching et Publication d'Artefacts
stages:
- stage: BuildAndTest
jobs:
- job: Build
steps:
- task: NodeTool@0
inputs:
versionSpec: $(nodeVersion)
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(npm_cache)
displayName: 'Cache npm'
- script: |
npm ci
npm run build
displayName: 'Build'
- task: CopyFiles@2
inputs:
contents: 'dist/**'
targetFolder: '$(Build.ArtifactStagingDirectory)/app'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'drop'
publishLocation: 'Container'
- job: Test
dependsOn: Build
steps:
# ... (tests comme avant, avec cache similaire)
- download: current
artifact: drop
- script: npm run test:e2e
displayName: 'E2E Tests sur artefact'Le cache npm utilise package-lock.json comme clé pour restaurer node_modules. Les artefacts persistent le build pour les jobs/tests suivants. Piège : sans restoreKeys, le cache échoue sur lockfile changes ; testez avec npm version pour valider.
Multi-Stages avec Variables et Templates
Passez au multi-environnements : dev auto-deploy, staging manuel, prod avec approbation. Utilisez des templates YAML pour la DRY principle (Don't Repeat Yourself), injectant des variables par stage.
Template de Déploiement Réutilisable
parameters:
environment: ''
appServiceName: ''
subscriptionId: ''
jobs:
- deployment: Deploy
displayName: 'Deploy to ${{ parameters.environment }}'
environment: ${{ parameters.environment }}
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: drop
- task: AzureWebApp@1
inputs:
azureSubscription: '${{ parameters.subscriptionId }}'
appType: 'webAppLinux'
appName: '${{ parameters.appServiceName }}'
package: '$(Pipeline.Workspace)/drop/app/**.zip'
deploymentMethod: 'zipDeploy'
- task: AzureCLI@2
inputs:
azureSubscription: '${{ parameters.subscriptionId }}'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az webapp config appsettings set --resource-group MyRG --name ${{ parameters.appServiceName }} --settings ENVIRONMENT=${{ parameters.environment }} HEALTH_CHECK_PATH=/health
displayName: 'Set App Settings'Ce template paramétré gère le déploiement zip sur App Service Linux, avec settings custom. L'environment Azure DevOps active les approbations. Piège : oubliez zipDeploy sans zipping l'artefact ; ajoutez un step ArchiveFiles@2 avant si needed.
Pipeline Multi-Stage avec Template et Approbations
stages:
- stage: BuildAndTest
# ... (comme avant)
- stage: DeployDev
displayName: 'Deploy Dev'
dependsOn: BuildAndTest
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/develop'))
jobs:
- template: templates/deploy-job.yml
parameters:
environment: 'dev'
appServiceName: 'myapp-dev'
subscriptionId: $(AZURE_SUBSCRIPTION_ID)
- stage: DeployStaging
displayName: 'Deploy Staging (Manual)'
dependsOn: DeployDev
jobs:
- deployment: gate
displayName: 'Approbation'
environment: 'staging-approval'
strategy:
runOnce:
deploy:
steps:
- script: echo 'Waiting for manual approval...'
- template: templates/deploy-job.yml
parameters:
environment: 'staging'
appServiceName: 'myapp-staging'
subscriptionId: $(AZURE_SUBSCRIPTION_ID)
- stage: DeployProd
displayName: 'Deploy Prod (Gates)'
dependsOn: DeployStaging
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- template: templates/deploy-job.yml
parameters:
environment: 'prod'
appServiceName: 'myapp-prod'
subscriptionId: $(AZURE_SUBSCRIPTION_ID)Les conditions branch spécifiques automatisent dev/staging/prod. Les gates via deployment: gate bloquent pour approbations manuelles. Piège : sans environment configuré dans Azure DevOps (avec checks), les deploys prod passent ; configurez approvers et retainers.
Sécurisation : Secrets et Key Vault
Intégrez Azure Key Vault pour injecter secrets dynamiquement, évitant les leaks en logs. Utilisez OIDC pour auth sans Service Principal secrets.
Intégration Key Vault et Variables Sécurisées
variables:
- group: kv-vars # Variable group lié à Key Vault
stages:
# ...
- stage: DeployDev
variables:
- name: dbConnection
value: $(DB_CONNECTION_DEV) # De Key Vault
jobs:
- template: templates/deploy-job.yml
parameters:
# ...
env:
DB_CONNECTION: $(dbConnection)
API_KEY: $(kv-api-key) # Direct de group
# Ajout OIDC pour tasks Azure
- task: AzureKeyVault@2
inputs:
azureSubscription: 'AzureRM'
keyVaultName: '$(KEY_VAULT_NAME)'
secretsFilter: 'db-password,api-key'Les variable groups liés à Key Vault injectent secrets au runtime. env les passe aux scripts sans logs. Piège : secrets en variables globaux persistent ; utilisez scope stage/job et AzureKeyVault@2 pour fetch on-demand.
Script de Health Check Post-Deploy (PowerShell)
param(
[string]$AppServiceName,
[string]$ResourceGroup,
[string]$HealthPath = '/health'
)
$subscriptionId = $env:AZURE_SUBSCRIPTION_ID
az account set --subscription $subscriptionId
$healthUrl = "https://$AppServiceName.azurewebsites.net$HealthPath"
try {
$response = Invoke-WebRequest -Uri $healthUrl -TimeoutSec 30 -UseBasicParsing
if ($response.StatusCode -eq 200) {
Write-Host "Health check OK: $($response.StatusCode)"
exit 0
} else {
Write-Error "Health check failed: $($response.StatusCode)"
exit 1
}
} catch {
Write-Error "Health check error: $($_.Exception.Message)"
exit 1
}Ce script PowerShell post-deploy vérifie l'endpoint /health via Azure CLI. Intégrez-le via task: AzurePowerShell@5. Piège : sans --slot pour blue-green, testez prod slot ; timeout 30s évite hangs.
Bonnes pratiques
- Templates partout : Factorisez 80% du code YAML pour scalabilité.
- Stratégies de déploiement : Utilisez
runOnce,rolling,canarypour zéro downtime. - Caching agressif : Node, Docker layers, .NET restore – ciblez 50%+ temps gagné.
- Gates avancés : Intégrez SonarQube, Snyk pour quality gates.
- Observabilité : Logs structurés avec
##vso[task.logissue type=warning]et metrics via Application Insights.
Erreurs courantes à éviter
- Pas de conditions branch : Déploys prod sur feature branches – utilisez
eq(variables['Build.SourceBranch'], 'refs/heads/main'). - Secrets en clair : Évitez
$(secret)sans Key Vault ; logs les capturent. - Pas de parallelism limits : Surcoûts Azure – set
demands: Agent.Name -equals myPool. - Artefacts manquants : Jobs suivants échouent – toujours
download: current.
Pour aller plus loin
Approfondissez avec Azure Pipelines docs, multi-stage stratégies. Découvrez nos formations DevOps Learni pour certification Azure DevOps Engineer Expert.