# Azure DevOps Pipeline - Build, Push and Deploy with Deployment Slots # Advanced version with zero-downtime deployment using staging slots trigger: branches: include: - main tags: include: - 'v*' pool: vmImage: 'ubuntu-latest' variables: # Azure Container Registry configuratie acrName: 'zdlas' # Pas aan naar jouw ACR naam repositoryName: 'cmdb-insight' dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection' # Azure App Service configuratie resourceGroup: 'rg-cmdb-insight-prod' backendAppName: 'cmdb-backend-prod' frontendAppName: 'cmdb-frontend-prod' azureSubscription: 'zuyderland-cmdb-subscription' # Deployment configuratie imageTag: '$(Build.BuildId)' deployToProduction: true useDeploymentSlots: true # Enable deployment slots stagingSlotName: 'staging' stages: - stage: Build displayName: 'Build and Push Docker Images' jobs: - job: BuildImages displayName: 'Build Docker Images' steps: - task: Docker@2 displayName: 'Build and Push Backend Image' inputs: command: buildAndPush repository: '$(repositoryName)/backend' dockerfile: 'backend/Dockerfile.prod' containerRegistry: '$(dockerRegistryServiceConnection)' tags: | $(imageTag) latest - task: Docker@2 displayName: 'Build and Push Frontend Image' inputs: command: buildAndPush repository: '$(repositoryName)/frontend' dockerfile: 'frontend/Dockerfile.prod' containerRegistry: '$(dockerRegistryServiceConnection)' tags: | $(imageTag) latest - task: PowerShell@2 displayName: 'Output Image URLs' inputs: targetType: 'inline' script: | $backendImage = "$(acrName).azurecr.io/$(repositoryName)/backend:$(imageTag)" $frontendImage = "$(acrName).azurecr.io/$(repositoryName)/frontend:$(imageTag)" Write-Host "##vso[task.setvariable variable=backendImage;isOutput=true]$backendImage" Write-Host "##vso[task.setvariable variable=frontendImage;isOutput=true]$frontendImage" Write-Host "Backend Image: $backendImage" Write-Host "Frontend Image: $frontendImage" - stage: DeployToStaging displayName: 'Deploy to Staging Slot' dependsOn: Build condition: and(succeeded(), eq(variables['deployToProduction'], true), eq(variables['useDeploymentSlots'], true)) jobs: - deployment: DeployBackendStaging displayName: 'Deploy Backend to Staging' environment: 'staging' strategy: runOnce: deploy: steps: - task: AzureWebAppContainer@1 displayName: 'Deploy Backend to Staging Slot' inputs: azureSubscription: '$(azureSubscription)' appName: '$(backendAppName)' deployToSlotOrASE: true resourceGroupName: '$(resourceGroup)' slotName: '$(stagingSlotName)' containers: '$(acrName).azurecr.io/$(repositoryName)/backend:latest' - task: AzureCLI@2 displayName: 'Wait for Backend Staging to be Ready' inputs: azureSubscription: '$(azureSubscription)' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | echo "Waiting for backend staging to be ready..." sleep 30 STAGING_URL="https://$(backendAppName)-$(stagingSlotName).azurewebsites.net/api/health" for i in {1..10}; do STATUS=$(curl -s -o /dev/null -w "%{http_code}" $STAGING_URL || echo "000") if [ "$STATUS" = "200" ]; then echo "✅ Backend staging is ready" exit 0 fi echo "Waiting... ($i/10)" sleep 10 done echo "⚠️ Backend staging health check timeout" - deployment: DeployFrontendStaging displayName: 'Deploy Frontend to Staging' environment: 'staging' strategy: runOnce: deploy: steps: - task: AzureWebAppContainer@1 displayName: 'Deploy Frontend to Staging Slot' inputs: azureSubscription: '$(azureSubscription)' appName: '$(frontendAppName)' deployToSlotOrASE: true resourceGroupName: '$(resourceGroup)' slotName: '$(stagingSlotName)' containers: '$(acrName).azurecr.io/$(repositoryName)/frontend:latest' - task: AzureCLI@2 displayName: 'Wait for Frontend Staging to be Ready' inputs: azureSubscription: '$(azureSubscription)' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | echo "Waiting for frontend staging to be ready..." sleep 20 STAGING_URL="https://$(frontendAppName)-$(stagingSlotName).azurewebsites.net" for i in {1..10}; do STATUS=$(curl -s -o /dev/null -w "%{http_code}" $STAGING_URL || echo "000") if [ "$STATUS" = "200" ]; then echo "✅ Frontend staging is ready" exit 0 fi echo "Waiting... ($i/10)" sleep 10 done echo "⚠️ Frontend staging health check timeout" - stage: SwapToProduction displayName: 'Swap Staging to Production' dependsOn: DeployToStaging condition: and(succeeded(), eq(variables['deployToProduction'], true), eq(variables['useDeploymentSlots'], true)) jobs: - deployment: SwapBackend displayName: 'Swap Backend to Production' environment: 'production' strategy: runOnce: deploy: steps: - task: AzureCLI@2 displayName: 'Swap Backend Staging to Production' inputs: azureSubscription: '$(azureSubscription)' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | echo "Swapping backend staging to production..." az webapp deployment slot swap \ --name $(backendAppName) \ --resource-group $(resourceGroup) \ --slot $(stagingSlotName) \ --target-slot production echo "✅ Backend swapped to production" - deployment: SwapFrontend displayName: 'Swap Frontend to Production' environment: 'production' strategy: runOnce: deploy: steps: - task: AzureCLI@2 displayName: 'Swap Frontend Staging to Production' inputs: azureSubscription: '$(azureSubscription)' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | echo "Swapping frontend staging to production..." az webapp deployment slot swap \ --name $(frontendAppName) \ --resource-group $(resourceGroup) \ --slot $(stagingSlotName) \ --target-slot production echo "✅ Frontend swapped to production" - stage: VerifyProduction displayName: 'Verify Production Deployment' dependsOn: SwapToProduction condition: and(succeeded(), eq(variables['deployToProduction'], true), eq(variables['useDeploymentSlots'], true)) jobs: - job: VerifyDeployment displayName: 'Verify Production' steps: - task: AzureCLI@2 displayName: 'Production Health Check' inputs: azureSubscription: '$(azureSubscription)' scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | echo "Verifying production deployment..." BACKEND_URL="https://$(backendAppName).azurewebsites.net/api/health" FRONTEND_URL="https://$(frontendAppName).azurewebsites.net" echo "Backend URL: $BACKEND_URL" echo "Frontend URL: $FRONTEND_URL" # Wait for swap to complete sleep 15 # Check backend health BACKEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $BACKEND_URL || echo "000") if [ "$BACKEND_STATUS" = "200" ]; then echo "✅ Backend production health check passed" else echo "❌ Backend production health check failed: $BACKEND_STATUS" exit 1 fi # Check frontend FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $FRONTEND_URL || echo "000") if [ "$FRONTEND_STATUS" = "200" ]; then echo "✅ Frontend production is accessible" else echo "❌ Frontend production check failed: $FRONTEND_STATUS" exit 1 fi echo "🎉 Production deployment verified successfully!"