- Pre-Deploy
- Log octopus parameters --
A - Transform package: Run service definition, service configuration, web/app config and file transformations/replacement bundled into the package. --
B
- Log octopus parameters --
- Post-Deploy
- Log deployment --
C - Ensure/Add diagnostics extensions --
D - Wait loop for
/healthcheckendpoint --E - Notify Slack/Skype --
F
- Log deployment --
- Deploy-failed
- Notify Slack/Skype --
F
- Notify Slack/Skype --
- Log deployment labels
- Sanity check staging deployment label against version number
- Wait loop for
runningstatus on staging deployment - Swap VIPs
- Sanity check production label against version number
- Wait loop for
runningstatus on production deployment. - Delete staging deployment
A
Write-Host ($OctopusParameters | Out-String)B
# This is an extensive list of conventions to allow multi-tenant deployment of packages
# to apply transformations bundled in the nuget package to the following files
#
# App.Config in worker roles
# Web.Config in web roles
# ServiceDefinition.csdef
# ServiceConfiguration.cscfg
# and file replacement throughout the web directoryC
function Get-Deployment()
{
Select-AzureSubscription -SubscriptionId $azureSubscriptionId
$deployment = Get-AzureDeployment -Slot $azureSlot -ServiceName $azureServiceName
$global:websiteRoot = $deployment | Select -ExpandProperty Url | Select -ExpandProperty OriginalString
Write-Host "Website Root: $websiteRoot"
return $deployment;
}
# main
Get-Deployment | ft -AutoSize | Out-HostD
Set-AzureSubscription -SubscriptionId $azureSubscriptionId -CurrentStorageAccount $OctopusAzureStorageAccountName
$existing = Get-AzureServiceDiagnosticsExtension -ServiceName $OctopusAzureServiceName -Slot $OctopusAzureSlot
if($existing)
{
Write-Host "Diagnostics extensions already configured."
}
else
{
Write-Host "Adding Diagnostics extensions..."
$storageCtx = New-AzureStorageContext -StorageAccountName $diagnosticsStorageAccount -StorageAccountKey $diagnosticsStorageAccountKey
Remove-AzureServiceDiagnosticsExtension -ServiceName $OctopusAzureServiceName -Slot $OctopusAzureSlot
Set-AzureServiceDiagnosticsExtension -ServiceName $OctopusAzureServiceName -Slot $OctopusAzureSlot -StorageContext $storageCtx -DiagnosticsConfigurationPath (Join-Path $root "diagnostics.wadcfgx")
Write-Host "Diagnostics extensions added."
}E
$endpoint = "$websiteRoot/healthcheck"
$startTime = [DateTime]::Now
$lapsed = 0
Write-Host "Healthcheck wait loop started. end-point: $endpoint"
while($true) {
try {
$res = Invoke-WebRequest -Uri $endpoint -TimeoutSec 30
$data = $res.Content | ConvertFrom-Json
if($data.Status -eq 0 ) { break }
}
catch [System.Net.WebException] { }
$lapsed = ([DateTime]::Now - $startTime).TotalMinutes
if($lapsed > 15) {
Write-Error "Healthcheck wait loop time-out after $lapsed mins"
return
}
Start-Sleep -s 1
}
Write-Host "Healthcheck completed after $lapsed mins"F
function Slack-Rich-Notification ($notification)
{
$payload = @{
channel = $NotificationSlackChannel
username = $NotificationSlackUsername
icon_url = $NotificationSlackIconUrl
attachments = @(@{
fallback = $notification.fallback
color = $notification.color
fields = @(@{
title = $notification.title
value = $notification.value
})
mrkdwn_in = @("fields")
});
}
Invoke-RestMethod -Method POST -Body ($payload | ConvertTo-Json -Depth 4) -Uri $NotificationSlackHookUrl -ContentType 'application/json'
}
if ($OctopusParameters['Octopus.Deployment.Error'] -eq $null){
Slack-Rich-Notification @{
title = "Success";
value = "Deployed *$OctopusProjectName* release ``$OctopusReleaseNumber`` to $OctopusEnvironmentName | <$websiteRoot/app | Browse>";
fallback = "Deployed $OctopusProjectName release $OctopusReleaseNumber to $OctopusEnvironmentName successfully";
color = "good";
};
} else {
Slack-Rich-Notification @{
title = "Failed";
value = "Failed to deploy $OctopusProjectName release $OctopusReleaseNumber to $OctopusEnvironmentName";
fallback = "Failed to deploy $OctopusProjectName release $OctopusReleaseNumber to $OctopusEnvironmentName";
color = "danger";
};
}G
$serviceName = $OctopusParameters["AzureSettings.ServiceName"]
$productionDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot "Production"
$stagingDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot "Staging"
$productionLabel = $productionDeployment.Label
$stagingLabel = $stagingDeployment.Label
Write-Host "Production deployment: $productionLabel, created $productionTime"
Write-Host "Staging deployment: $stagingLabel, created $stagingTime"
if(-not($stagingLabel -match "v$OctopusReleaseNumber"))
{
Write-Error "Staging label does not match version: v$OctopusReleaseNumber"
return
}
# Wait until it's all up
while (-not $($stagingDeployment.Status -eq "Running"))
{
Write-Host "Waiting 10 seconds for staging running status..."
Start-Sleep -s 10
$stagingDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot "Staging"
}
Write-Host "Swap Started" -ForegroundColor Green
$moveStatus = Move-AzureDeployment -ServiceName $serviceName
Write-Output ("Vip swap of {0} status: {1}" -f $serviceName, $moveStatus.OperationStatus)H
$serviceName = $OctopusParameters["AzureSettings.ServiceName"]
$productionDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot "Production"
$productionLabel = $productionDeployment.Label
if(-not($productionLabel -match "v$OctopusReleaseNumber"))
{
Write-Error "Production label does not match version: v$OctopusReleaseNumber"
return
}
# Wait until it's all up
while (-not $($productionDeployment.Status -eq "Running"))
{
Write-Host "Waiting 10 seconds for production running status..."
Start-Sleep -s 10
$productionDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot "Production"
}
Write-Host "Delete Staging started" -ForegroundColor Green
$removeStatus = Remove-AzureDeployment -ServiceName $serviceName -Slot "Staging" -Force
Write-Output ("Delete staging of {0} status: {1}" -f $serviceName, $removeStatus.OperationStatus)