CI/CD avec Visual Studio et Azure

Je pars sur une application .NET (ASP.NET Core) déployée sur Azure App Service, avec Azure DevOps pour le pipeline. Si ta stack est différente (conteneurs, Functions, AKS), l’approche reste la même, seules les tâches de déploiement changent.

Architecture

Visual Studio → Azure Repos (ou GitHub) → Azure Pipelines
│
Build + Tests → Artefact → Staging (slot) → Approbation → Swap vers Production
│
Application Insights

Étape 1 : Préparer le projet dans Visual Studio

  1. Ouvre ta solution et ajoute un projet de tests (xUnit ou NUnit). Vérifie que tout passe dans Test Explorer.
  2. Ajoute un endpoint de santé, utile pour les smoke tests : builder.Services.AddHealthChecks();// ...app.MapHealthChecks("/health");
  3. Crée le dépôt Git : menu Git → Create Git Repository, puis choisis Azure DevOps ou GitHub et pousse le code.

Étape 2 : Créer les ressources Azure

Avec Azure CLI (ou le portail) :

az login
az group create -n rg-monapp -l canadaeast
az appservice plan create -n plan-monapp -g rg-monapp --sku S1
az webapp create -n monapp-web -g rg-monapp -p plan-monapp --runtime "dotnet:8"
az webapp deployment slot create -n monapp-web -g rg-monapp --slot staging

Points importants :

  • Les slots de déploiement demandent un plan Standard (S1) ou supérieur.
  • Adapte le nom monapp-web, qui doit être unique mondialement. Vérifie les runtimes disponibles avec az webapp list-runtimes.
  • Ajoute Application Insights (créé avec le Web App ou séparément) pour le monitoring.

Étape 3 : Configurer Azure DevOps

  1. Crée un projet sur dev.azure.com.
  2. Project Settings → Service connections → New → Azure Resource Manager. Choisis l’authentification Workload identity federation (recommandée, sans secret à gérer) et limite la portée au groupe de ressources rg-monapp. Nomme-la sc-azure-prod.
  3. Pipelines → Environments : crée staging et production.
  4. Sur production, ajoute une Approval check (toi ou ton équipe).

À noter : les agents hébergés par Microsoft peuvent nécessiter une demande de parallélisme gratuit sur les nouvelles organisations. Vérifie ce point dans les paramètres de ton organisation si le pipeline reste en attente.

Étape 4 : Le pipeline azure-pipelines.yml

À la racine du dépôt :

trigger:
branches:
include: [main]
pr:
branches:
include: [main]
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
azureSubscription: 'sc-azure-prod'
webAppName: 'monapp-web'
resourceGroup: 'rg-monapp'
stages:
# ---------- CI ----------
- stage: Build
jobs:
- job: BuildTest
steps:
- task: UseDotNet@2
inputs:
packageType: sdk
version: '8.x'
- task: DotNetCoreCLI@2
displayName: Restore
inputs:
command: restore
projects: '**/*.csproj'
- task: DotNetCoreCLI@2
displayName: Build
inputs:
command: build
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) --no-restore'
- task: DotNetCoreCLI@2
displayName: Tests unitaires
inputs:
command: test
projects: '**/*Tests.csproj'
arguments: '--configuration $(buildConfiguration) --no-build --collect:"XPlat Code Coverage"'
- task: DotNetCoreCLI@2
displayName: Publish
inputs:
command: publish
publishWebProjects: true
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: true
- publish: $(Build.ArtifactStagingDirectory)
artifact: drop
# ---------- CD : Staging ----------
- stage: DeployStaging
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: Staging
environment: staging
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: Déploiement dans le slot staging
inputs:
azureSubscription: $(azureSubscription)
appType: webApp
appName: $(webAppName)
deployToSlotOrASE: true
resourceGroupName: $(resourceGroup)
slotName: staging
package: $(Pipeline.Workspace)/drop/**/*.zip
- script: |
sleep 20
curl --fail --retry 5 --retry-delay 10 https://$(webAppName)-staging.azurewebsites.net/health
displayName: Smoke test
# ---------- CD : Production ----------
- stage: DeployProd
dependsOn: DeployStaging
jobs:
- deployment: Production
environment: production # déclenche l'approbation manuelle
strategy:
runOnce:
deploy:
steps:
- task: AzureAppServiceManage@0
displayName: Swap staging vers production
inputs:
azureSubscription: $(azureSubscription)
Action: 'Swap Slots'
WebAppName: $(webAppName)
ResourceGroupName: $(resourceGroup)
SourceSlot: staging
SwapWithProduction: true

Puis dans Azure DevOps : Pipelines → New pipeline → Azure Repos Git → Existing YAML file.

Étape 5 : Protéger la branche main

Repos → Branches → main → Branch policies :

  • Revue de code obligatoire (1 reviewer minimum).
  • Build validation : le pipeline doit réussir avant de pouvoir fusionner une PR.
  • Résolution des commentaires obligatoire.

Résultat : les PR lancent le build et les tests (étape CI uniquement), et seul un merge sur main déclenche le déploiement.

Étape 6 : Gérer les secrets et la configuration

  • Azure Key Vault pour les mots de passe, chaînes de connexion et clés d’API. Donne à l’App Service une Managed Identity et référence les secrets avec @Microsoft.KeyVault(SecretUri=...) dans les paramètres de l’application.
  • Utilise des paramètres de slot (« Deployment slot setting ») pour les valeurs propres à chaque environnement.
  • Ne mets jamais de secrets dans le dépôt ni dans le YAML.

Étape 7 : Monitoring et retour arrière

  • Application Insights pour les requêtes, erreurs, dépendances et performances. Configure des alertes (taux d’erreur, temps de réponse).
  • Rollback instantané : refais un swap des slots. L’ancienne version est toujours dans le slot staging après le swap.

Alternative : le raccourci depuis Visual Studio

Dans Visual Studio, clic droit sur le projet → Publish → Azure → Azure App Service. L’assistant peut proposer de générer un workflow GitHub Actions (CI/CD) avec les identifiants Azure déjà configurés. C’est très rapide pour démarrer, mais moins complet qu’un pipeline en plusieurs étapes avec approbations. Garde-le pour un prototype, et utilise le pipeline YAML ci-dessus pour un projet sérieux.

Évolutions possibles

  • Infrastructure as Code : décrire les ressources Azure avec Bicep ou Terraform plutôt qu’avec des commandes manuelles.
  • Scans de sécurité : Defender for DevOps, CodeQL, scan de dépendances.
  • Conteneurs : build d’image Docker, push vers Azure Container Registry, déploiement sur App Service for Containers ou Azure Container Apps.
  • Déploiement canary : le « Testing in production » des slots permet de router un pourcentage du trafic vers staging avant le swap.

Unknown's avatar

About Louis Dubois

Analyste programmeur principal DevSecOps, AIOps C#, T-SQL LouisDubois.com
This entry was posted in Informatique. Bookmark the permalink.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.