Last active
May 14, 2025 11:54
-
-
Save cpoDesign/186138009619821fbe6846323666e58f to your computer and use it in GitHub Desktop.
Pipeline disabling slots
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public async Task RunAsync( | |
[ServiceBusTrigger("topic", "subscription", Connection = "ServiceBusConnection")] string message, | |
ILogger log) | |
{ | |
var enabled = Environment.GetEnvironmentVariable("SERVICEBUS_ENABLED"); | |
if (!bool.TryParse(enabled, out var isActive) || !isActive) | |
{ | |
log.LogInformation("Service Bus processing disabled in this app."); | |
return; | |
} | |
// Proceed with processing | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trigger: | |
branches: | |
include: | |
- main | |
variables: | |
azureSubscription: 'Your-Azure-RM-Service-Connection' | |
greenApp: 'your-func-green' | |
blueApp: 'your-func-blue' | |
resourceGroup: 'your-rg' | |
healthCheckEndpoint: '/api/health' | |
healthCheckRetries: 10 | |
healthCheckDelaySeconds: 15 | |
rollbackWindowMinutes: 60 | |
# This flag determines which app is live now | |
# Flip this manually in variable group or via automation (can also use tags or metadata for dynamic inference) | |
currentLiveApp: 'your-func-green' | |
stages: | |
# Determine target app | |
- stage: SelectTarget | |
displayName: 'Determine Blue or Green Target' | |
jobs: | |
- job: SetTarget | |
displayName: 'Set deployment target app' | |
variables: | |
targetApp: $[eq(variables['currentLiveApp'], variables['greenApp']) ? variables['blueApp'] : variables['greenApp']] | |
steps: | |
- script: echo "Deploying to $(targetApp)" | |
# Deploy to inactive app | |
- stage: DeployTarget | |
displayName: 'Deploy to target (Blue or Green)' | |
dependsOn: SelectTarget | |
jobs: | |
- job: Deploy | |
steps: | |
- task: AzureFunctionApp@2 | |
displayName: 'Deploy Function Code' | |
inputs: | |
azureSubscription: $(azureSubscription) | |
appType: functionApp | |
appName: $(targetApp) | |
package: '$(System.DefaultWorkingDirectory)/drop/function.zip' | |
- task: AzureAppServiceSettings@1 | |
displayName: 'Ensure triggers DISABLED on staging app' | |
inputs: | |
azureSubscription: $(azureSubscription) | |
appName: $(targetApp) | |
resourceGroupName: $(resourceGroup) | |
appSettings: | | |
[ | |
{ | |
"name": "SERVICEBUS_ENABLED", | |
"value": "false", | |
"slotSetting": false | |
} | |
] | |
# Health Check | |
- stage: HealthCheck | |
displayName: 'Health Check on Target' | |
dependsOn: DeployTarget | |
jobs: | |
- job: Check | |
steps: | |
- powershell: | | |
$app = "$(targetApp)" | |
$url = "https://${app}.azurewebsites.net$(healthCheckEndpoint)" | |
Write-Host "Health check at: $url" | |
$success = $false | |
for ($i = 0; $i -lt $(healthCheckRetries); $i++) { | |
try { | |
$resp = Invoke-WebRequest -Uri $url -UseBasicParsing | |
if ($resp.StatusCode -eq 200) { | |
Write-Host "Health check succeeded" | |
$success = $true | |
break | |
} | |
} catch { | |
Write-Host "Retry $i failed" | |
} | |
Start-Sleep -Seconds $(healthCheckDelaySeconds) | |
} | |
if (-not $success) { | |
Write-Error "Health check failed after retries" | |
exit 1 | |
} | |
# Switch Routing | |
- stage: ActivateNewVersion | |
displayName: 'Switch to new version (disable old, enable new)' | |
dependsOn: HealthCheck | |
jobs: | |
- job: SwitchApps | |
steps: | |
- powershell: | | |
Write-Host "Switching live app from $(currentLiveApp) to $(targetApp)" | |
az webapp config appsettings set --name $(targetApp) --resource-group $(resourceGroup) --settings SERVICEBUS_ENABLED=true | |
az webapp config appsettings set --name $(currentLiveApp) --resource-group $(resourceGroup) --settings SERVICEBUS_ENABLED=false | |
# Monitor Period for Auto-Rollback | |
- stage: MonitorAndRollback | |
displayName: 'Monitor and Auto-Rollback if Needed' | |
dependsOn: ActivateNewVersion | |
jobs: | |
- job: Monitor | |
timeoutInMinutes: ${{ variables.rollbackWindowMinutes }} | |
continueOnError: true | |
steps: | |
- powershell: | | |
$url = "https://${{ variables.targetApp }}.azurewebsites.net$(healthCheckEndpoint)" | |
Write-Host "Monitoring app health: $url" | |
$success = $true | |
for ($i = 0; $i -lt 12; $i++) { | |
try { | |
$resp = Invoke-WebRequest -Uri $url -UseBasicParsing | |
if ($resp.StatusCode -ne 200) { | |
Write-Host "Health check failed at minute $i" | |
$success = $false | |
break | |
} | |
} catch { | |
Write-Host "Exception checking health. Rolling back." | |
$success = $false | |
break | |
} | |
Start-Sleep -Seconds 300 # check every 5 mins | |
} | |
if (-not $success) { | |
Write-Host "Health failure detected, rolling back..." | |
az webapp config appsettings set --name ${{ variables.targetApp }} --resource-group $(resourceGroup) --settings SERVICEBUS_ENABLED=false | |
az webapp config appsettings set --name $(currentLiveApp) --resource-group $(resourceGroup) --settings SERVICEBUS_ENABLED=true | |
exit 1 | |
} | |
- script: echo "Deployment successful and stable after 1 hour" | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trigger: | |
branches: | |
include: | |
- main | |
variables: | |
azureSubscription: 'Your-Azure-RM-Service-Connection' | |
functionAppName: 'your-function-app' | |
resourceGroup: 'your-resource-group' | |
slotName: 'preview' | |
healthCheckUrl: 'https://your-function-preview.azurewebsites.net/api/health' | |
rollbackTimeoutMinutes: 60 | |
stages: | |
- stage: DeployPreview | |
displayName: 'Deploy to Preview Slot' | |
jobs: | |
- job: Deploy | |
steps: | |
- task: AzureFunctionApp@2 | |
inputs: | |
azureSubscription: $(azureSubscription) | |
appType: functionApp | |
appName: $(functionAppName) | |
slotName: $(slotName) | |
package: '$(System.DefaultWorkingDirectory)/drop/your-zip-file.zip' | |
- task: AzureAppServiceSettings@1 | |
displayName: 'Set Slot Settings - Disable Trigger' | |
inputs: | |
azureSubscription: $(azureSubscription) | |
appName: $(functionAppName) | |
resourceGroupName: $(resourceGroup) | |
slotName: $(slotName) | |
appSettings: | | |
[ | |
{ | |
"name": "IsActiveSlot", | |
"value": "false", | |
"slotSetting": true | |
} | |
] | |
- stage: HealthCheck | |
displayName: 'Health Check Before Swap' | |
dependsOn: DeployPreview | |
jobs: | |
- job: CheckHealth | |
steps: | |
- task: PowerShell@2 | |
displayName: 'Wait for health endpoint' | |
inputs: | |
targetType: 'inline' | |
script: | | |
$maxRetries = 10 | |
$retryCount = 0 | |
$delay = 15 | |
do { | |
try { | |
$response = Invoke-WebRequest -Uri "$(healthCheckUrl)" -UseBasicParsing | |
if ($response.StatusCode -eq 200) { | |
Write-Host "Health check passed" | |
exit 0 | |
} | |
} catch { | |
Write-Host "Health check failed, retrying..." | |
} | |
Start-Sleep -Seconds $delay | |
$retryCount++ | |
} while ($retryCount -lt $maxRetries) | |
Write-Error "Health check failed after $maxRetries attempts" | |
exit 1 | |
- stage: SwapSlots | |
displayName: 'Swap Slots with Rollback Timeout' | |
dependsOn: HealthCheck | |
jobs: | |
- job: Swap | |
steps: | |
- task: AzureAppServiceManage@0 | |
displayName: 'Swap Preview with Production' | |
inputs: | |
azureSubscription: $(azureSubscription) | |
WebAppName: $(functionAppName) | |
ResourceGroupName: $(resourceGroup) | |
SourceSlot: $(slotName) | |
SwapWithProduction: true | |
- task: AzureAppServiceSettings@1 | |
displayName: 'Enable Trigger on New Production Slot' | |
inputs: | |
azureSubscription: $(azureSubscription) | |
appName: $(functionAppName) | |
resourceGroupName: $(resourceGroup) | |
slotName: 'production' | |
appSettings: | | |
[ | |
{ | |
"name": "IsActiveSlot", | |
"value": "true", | |
"slotSetting": true | |
} | |
] | |
- stage: RollbackTimer | |
displayName: 'Rollback Option - Wait 1 Hour' | |
dependsOn: SwapSlots | |
jobs: | |
- job: Timer | |
timeoutInMinutes: ${{ variables.rollbackTimeoutMinutes }} | |
steps: | |
- task: ManualValidation@0 | |
timeoutInMinutes: ${{ variables.rollbackTimeoutMinutes }} | |
inputs: | |
instructions: 'Monitor system. If rollback is needed within 1 hour, manually trigger rollback pipeline.' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment