Continuous deployment to Azure Cloud Services

by Frans Lytzen | 12/11/2015

When you use Azure Websites you can do continuous deployment using Git, but with cloud services you can only do it from TFS. Luckily, it is quite easy to set up a Powershell script to deploy to Cloud Services. In this post I will share a sample script that can be used to deploy to multiple different environments, such as a UAT and a Live environment. I am assuming that you have set up corresponding UAT and Live Build and Azure profiles. The reason for this is that you can then easily use Web.Config transformation to change settings between UAT and Live and also use the two different Azure configurations to have different Azure settings. If you need to do an app.config transformation for your worker as part of your deploy, have a look at this Stackoverflow answer.
We are using this script to deploy manually as well as deploying automatically from our TeamCity server.

Pre-requisites

  • Before you start you need to create a Management Certificate for your Azure subscription, import it on your machine and add the thumb print to the script.
  • You will also need to install the Azure Powershell CmdLets on your machine (it’s usually installed as part of the SDK). When you look at the script, there are a couple of values you will need to provide.
  • Put the script and the configuration XML file in a folder in your solution called Deployment.
  • Assuming you want to enable remote access to your servers, you need to create a certificate and put it in the Deployment folder next to the script. You can do this by using Visual Studio to do a deployment first and enable remote access; This will configure the config files with the thumbprint and will generate a certificate for you. If you don’t want remote access, just remove that bit of the script.
Param(
[string]$deploymentType,
[string]$deployToAzure,
[string]$slot
)

$managementCertificateThumbprint="[YOURMANAGEMENTCERTIFICATETHUMBPRINT]"

function Build()
{
$dotNetVersion = "4.0"
$regKey = "HKLM:\software\Microsoft\MSBuild\ToolsVersions\$dotNetVersion"
$regProperty = "MSBuildToolsPath"
$msbuildExe = join-path -path (Get-ItemProperty $regKey).$regProperty -childpath "msbuild.exe"

Write-Host "Building..." -ForegroundColor Yellow
&$msbuildExe [YOURSOLUTIONNAME].sln /t:Publish /p:TargetProfile=$deploymentType /p:Configuration=$deploymentType /p:Platform="Any CPU"

if ($LASTEXITCODE -ne 0)
{
throw "The build failed"
}
}

function Deploy()
{
Import-Module Azure
write-host "`nAzure CmdLets loaded" -ForegroundColor Green
$configFile = $configFolder + "\AzureSettings.xml"
if (! (Test-Path $configFile))
{
Throw ("Unable to find " + $configFile)
}
[xml]$azureSettings = Get-Content ($configFile)
$subscriptionId = $azureSettings.AzureSettings.SubscriptionID
$friendlySubName = $azureSettings.AzureSettings.FriendlySubName
$serviceSettings = ($azureSettings.AzureSettings.Services.Service | Where-Object {$_.deploymentType -match $deploymentType })
$serviceName = $serviceSettings.ServiceName
$storageAccount = $serviceSettings.DeploymentStorageAccount

if (Test-Path cert:\CurrentUser\My\$managementCertificateThumbprint)
{
$myCert = Get-Item cert:\CurrentUser\My\$managementCertificateThumbprint
}
else {
if (Test-Path cert:\LocalMachine\My\$managementCertificateThumbprint) {
$myCert = Get-Item cert:\LocalMachine\My\$managementCertificateThumbprint
}
else {
Throw ("Unable to find the management certificate in either current user or local machine storage")
}
}
Write-Host ("Adding " + $friendlySubName + " subscription") -ForegroundColor Green
Set-AzureSubscription -SubscriptionName $friendlySubName -Certificate $myCert -SubscriptionID $subscriptionId

Select-AzureSubscription $friendlySubName

$deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot -ErrorVariable a -ErrorAction silentlycontinue
$deploymentLabel = $deploymentType + " " + $(Get-Date –f $timeStampFormat)

Set-AzureSubscription -SubscriptionName $friendlySubName -CurrentStorageAccount $storageAccount

#Make sure we have the remote access certificate in there
Add-AzureCertificate -ServiceName $serviceName -CertToDeploy ($configFolder + "\RemoteAccessCertificate.pfx") -Password powershellSucks

Write-Host ("About to deploy " + $deploymentType + " " + $service + " to " + $slot + " in " + $location) -ForegroundColor Green
Write-Host ("Subscription: " + $friendlySubName + ", Service: " + $serviceName + ", Storage: " + $storageAccount) -ForegroundColor Green

if ($a[0] -ne $null)
{
#Write-Host "No current deployment, creating a new deployment"
CreateNewDeployment
}
else
{
Write-Host "Existing deployment, upgrading..."
UpgradeDeployment
}
}

function CreateNewDeployment()
{
write-progress -id 3 -activity "Creating New Deployment" -Status "In progress"
Write-Output "$(Get-Date -f $timeStampFormat) - Creating New Deployment: In progress"

$opstat = New-AzureDeployment -Slot $slot -Package $packageLocation -Configuration $cloudConfigLocation -label $deploymentLabel -ServiceName $serviceName

$completeDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot
$completeDeploymentID = $completeDeployment.deploymentid

write-progress -id 3 -activity "Creating New Deployment" -completed -Status "Complete"
Write-Output "$(Get-Date -f $timeStampFormat) - Creating New Deployment: Complete, Deployment ID: $completeDeploymentID"

StartInstances
}

function UpgradeDeployment()
{
write-progress -id 3 -activity "Upgrading Deployment" -Status "In progress"
Write-Output "$(Get-Date -f $timeStampFormat) - Upgrading Deployment: In progress"

# perform Update-Deployment
$setdeployment = Set-AzureDeployment -Upgrade -Slot $slot -Package $packageLocation -Configuration $cloudConfigLocation -label $deploymentLabel -ServiceName $serviceName -Force

$completeDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot
$completeDeploymentID = $completeDeployment.deploymentid

write-progress -id 3 -activity "Upgrading Deployment" -completed -Status "Complete"
Write-Output "$(Get-Date -f $timeStampFormat) - Upgrading Deployment: Complete, Deployment ID: $completeDeploymentID"
}

function StartInstances()
{
write-progress -id 4 -activity "Starting Instances" -status "In progress"
Write-Output "$(Get-Date -f $timeStampFormat) - Starting Instances: In progress"

$deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot
$runstatus = $deployment.Status

if ($runstatus -ne 'Running')
{
$run = Set-AzureDeployment -Slot $slot -ServiceName $serviceName -Status Running
}
$deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot
$oldStatusStr = @("") * $deployment.RoleInstanceList.Count

while (-not(AllInstancesRunning($deployment.RoleInstanceList)))
{
$i = 1
foreach ($roleInstance in $deployment.RoleInstanceList)
{
$instanceName = $roleInstance.InstanceName
$instanceStatus = $roleInstance.InstanceStatus

if ($oldStatusStr[$i - 1] -ne $roleInstance.InstanceStatus)
{
$oldStatusStr[$i - 1] = $roleInstance.InstanceStatus
Write-Output "$(Get-Date -f $timeStampFormat) - Starting Instance '$instanceName': $instanceStatus"
}

write-progress -id (4 + $i) -activity "Starting Instance '$instanceName'" -status "$instanceStatus"
$i = $i + 1
}

sleep -Seconds 1

$deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot
}

$i = 1
foreach ($roleInstance in $deployment.RoleInstanceList)
{
$instanceName = $roleInstance.InstanceName
$instanceStatus = $roleInstance.InstanceStatus

if ($oldStatusStr[$i - 1] -ne $roleInstance.InstanceStatus)
{
$oldStatusStr[$i - 1] = $roleInstance.InstanceStatus
Write-Output "$(Get-Date -f $timeStampFormat) - Starting Instance '$instanceName': $instanceStatus"
}

$i = $i + 1
}

$deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot
$opstat = $deployment.Status

write-progress -id 4 -activity "Starting Instances" -completed -status $opstat
Write-Output "$(Get-Date -f $timeStampFormat) - Starting Instances: $opstat"
}

function AllInstancesRunning($roleInstanceList)
{
foreach ($roleInstance in $roleInstanceList)
{
if ($roleInstance.InstanceStatus -ne "ReadyRole")
{
return $false
}
}

return $true
}

#Get to the right place...
$scriptPath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptPath
cd $dir
cd..

$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (!$isAdmin)
{
throw "You need to run this script as an admin"
}

$configFolder = "Deployment"
if (!(Test-Path $configFolder))
{
Throw ("There is no '" + $configFolder + "' folder. Aborting.")
}

while ((!$deploymentType) -OR ($deploymentType -notin ("Live", "UAT", "Dev" )))
{
$deploymentType = Read-Host "Please specify deployment type Live or UAT (or Dev)"
}

while ((!$slot) -or ($slot -notin ("Production", "Staging")))
{
$slot = Read-Host "Deploy to Production or Staging?"
}

Build

while ((!$deployToAzure ) -or ($deployToAzure -notin ("Y", "N")))
{
$deployToAzure = Read-Host "Do you want to actually deploy this to Azure now (Y/N)?"
}

if ($deployToAzure -eq "Y")
{
$packageLocation = $dir + "\..\Cloud\bin\" + $deploymentType + "\app.publish\Cloud.cspkg"
$cloudConfigLocation = $dir + "\..\Cloud\bin\" + $deploymentType + "\app.publish\ServiceConfiguration." + $deploymentType + ".cscfg"
Deploy
}

Note that some of the above scripts have been found elsewhere on the internet, in particular the functions to actuall create/upgrade deployments.

You will also need a configuration file to hold some information about each of your environments:
<azuresettings>
<subscriptionid>[SUBSCRIPTIONID]</subscriptionid>
<friendlysubname>[WHATEVERYOUWANT]</friendlysubname>
<services>
<service deploymenttype="UAT">
<servicename>[CLOUDSERVICENAME eg myuatcloudservice]</servicename>
<deploymentstorageaccount>[STORAGEACCOUNTNAME eg myuatstorage]</deploymentstorageaccount>
</service>
</services>
<services>
<service deploymenttype="Live">
<servicename>[CLOUDSERVICENAME eg myuatcloudservice]</servicename>
<deploymentstorageaccount>[STORAGEACCOUNTNAME eg myuatstorage]</deploymentstorageaccount>
</service>
</services>
</azuresettings>


Share this article

You Might Also Like

Explore more articles that dive into similar topics. Whether you’re looking for fresh insights or practical advice, we’ve handpicked these just for you.

AI Isn’t Magic: Why Predictive Accuracy Can Be Misleading

by Frans Lytzen | 15/04/2025

One of the biggest misconceptions in AI today is how well it can actually predict things – especially things that are rare. This is most directly applicable to Machine Learning (as they are just statistical models) but the same principle applies to LLMs. The fundamental problem is the same and AI is not magic. In reality, AI’s predictive power is more complicated. One of the key challenges? False positives—incorrect detections that can significantly undermine the value of AI-driven decision-making. Let’s explore why this happens and how businesses can better understand AI’s limitations.

From Figma Slides to Svelte Page in Under an Hour – How I Accidentally Proved My Own Point

by Marcin Prystupa | 10/04/2025

A quick case study on how I went from a Figma presentation to a working Svelte page in less than an hour – with the help of AI and some clever tooling.

Embracing the European Accessibility Act: A NewOrbit Perspective

by George Elkington | 12/03/2025

As the European Accessibility Act (EAA) approaches its enforcement date on June 28, 2025, businesses must prioritise accessibility to ensure compliance and inclusivity. The EAA sets new standards for software, e-commerce, banking, digital devices, and more, aiming to make products and services accessible to all, including people with disabilities and the elderly. Non-compliance could lead to significant penalties across the EU. At NewOrbit, we believe that accessibility is not just a legal requirement—it’s good design. Take advantage of our free initial review to assess your compliance and stay ahead of the deadline.

Contact Us

NewOrbit Ltd.
Hampden House
Chalgrove
OX44 7RW


020 3757 9100

NewOrbit Logo

Copyright © NewOrbit Ltd.