Skip to content

Instantly share code, notes, and snippets.

@cpoDesign
Last active May 14, 2025 11:54
Show Gist options
  • Save cpoDesign/186138009619821fbe6846323666e58f to your computer and use it in GitHub Desktop.
Save cpoDesign/186138009619821fbe6846323666e58f to your computer and use it in GitHub Desktop.
Pipeline disabling slots
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
}
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"
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