Custom Git Deployment to Azure

by Rob Richardson



About Me

Rob Richardson is a local software craftsman building web properties in ASP.NET and Node, Angular, and React. He’s a Microsoft MVP, published author, frequent speaker at conferences, user groups, and community events, and a diligent teacher and student of high quality software development. You can find this and other talks on https://robrich.org/presentations and follow him on twitter at @rob_rich.

Git deploy options in Azure

Azure Git Deploy Visual Studio Team Services
Free with Azure Web App (including free tier) Separate Azure purchase
Command-line tools and logs Web-based GUI including test result charts
Track work items

Azure Git Deploy

Companion Code: https://github.com/robrich/HelloKudu


2. Create Web App

source: azure.microsoft.com/en-us/documentation/articles/azure-web-sites-web-hosting-plans-in-depth-overview

3. Enable Git Deploy

source: azure.microsoft.com/en-us/documentation/articles/web-sites-publish-source-control

4. Grab Git URL

source: azure.microsoft.com/en-us/documentation/articles/web-sites-publish-source-control

5. Add azure git remote

From within your git working directory:

git remote add azure https://[email protected]:443/NeedsMoreGit.git

6. Send content to Azure

git push azure master

Azure Magic Deployments

We pushed code to Azure

git push azure master

Results on the command line

source: datascienceandprogramming.azurewebsites.net/wp-content/uploads/2013/11/Capture-132.png

Results in the Azure Portal

source: azure.microsoft.com/en-us/documentation/articles/web-sites-publish-source-control

Results in the Classic Azure Portal

source: erikschlegel.com/2015/06/20/azure-continuous-deployment-using-git-private-repos

We can control the magic

1. Get the Azure CLI Tools

Install node

Install Azure CLI Tools

npm install azure-cli -g

2. Generate deployment template (C#)

azure site deploymentscript --aspWAP path/to/Project.csproj -s SolutionFile.sln

Docs: github.com/projectkudu/kudu/wiki/Customizing-deployments

2. Generate deployment template (Static Content)

For Node, PHP, ASP.NET Core, etc

azure site deploymentscript --node path/to/deploy/folder

Docs: github.com/projectkudu/kudu/wiki/Customizing-deployments

Generated files:

  • .deployment: "where is the deployment script?"
    command = deploy.cmd
  • deploy.cmd: "the deployment script"

Docs: github.com/projectkudu/kudu/wiki/Customizing-deployments

Deployment script

Deployment script can be:

  • cmd
  • bat
  • bash
  • exe
  • ps1

Powershell is a little weird:

command = powershell -NoProfile -NoLogo -ExecutionPolicy Unrestricted -Command "& "$pwd\deploy.ps1" 2>&1 | echo"

Docs: github.com/projectkudu/kudu/wiki/Customizing-deployments


... snip ...

:: Deployment
:: ----------

echo Handling .NET Web Application deployment.

:: 1. Restore NuGet packages
IF /I "Hello.sln" NEQ "" (
  call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\Hello.sln"
  IF !ERRORLEVEL! NEQ 0 goto error

:: 2. Build to the temporary path
  call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\src\Web\Web.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS%
) ELSE (
  call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\src\Web\Web.csproj" /nologo /verbosity:m /t:Build /p:AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS%

IF !ERRORLEVEL! NEQ 0 goto error

:: 3. KuduSync
  call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error

... snip ...

3. Commit and push
deployment scripts

git add .deployment
git add deploy.cmd
git commit -m "deployment files"
git push azure master

Automate all the things

Run tests

Add more steps to deploy.cmd

:: 2. Build test project
echo Building test project
"%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\Tests\Tests.csproj" /property:Configuration=Release
IF !ERRORLEVEL! NEQ 0 goto error

:: 3. Run tests
echo Running tests
"%DEPLOYMENT_SOURCE%\packages\xunit.runner.console.2.1.0\tools\xunit.console.exe" "%DEPLOYMENT_SOURCE%\Tests\bin\Release\Tests.dll"
IF !ERRORLEVEL! NEQ 0 goto error

If tests fail, deployment is aborted, current site is unaffected

source: blog.amitapple.com/post/51576689501/testsduringazurewebsitesdeployment

JavaScript tasks

The easiest way is to add npm scripts to package.json

  "name": "my-site",
  "version": "1.0.0",

  "scripts": {
    "test": "eslint .",
    "build": "gulp"

  "devDependencies": {
    "eslint": "^2.12.0",
    "gulp": "^3.9.1",
    "gulp-minify-css": "^1.2.4",
    "gulp-uglify": "^1.5.3",
    "mocha": "^2.5.3",
    "chai": "^3.5.0"

If tasks fail, deployment is aborted, current site is unaffected

JavaScript tasks

Add more steps to deploy.cmd

:: 3. Run ESLint and Gulp
:: upgrade npm
call npm cache clean
call npm install -g npm
call npm install && npm test && npm run build
IF !ERRORLEVEL! NEQ 0 goto error

If tasks fail, deployment is aborted, current site is unaffected

Set App Version

Add to AssemblyInfo.cs

[assembly: AssemblyInformationalVersion("GITHASH")] // Set to git hash in build file

Change GITHASH in deploy.cmd

call :ExecuteCmd PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "(Get-Content Web\Properties\AssemblyInfo.cs).replace('GITHASH', (git rev-parse --short HEAD)) | Set-Content Web\Properties\AssemblyInfo.cs"

Read value in C#

Assembly assembly = Assembly.GetExecutingAssembly();
AssemblyInformationalVersionAttribute desc = (
	from a in assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)
	select a as AssemblyInformationalVersionAttribute
gitHash = desc.InformationalVersion;

Verify Site after Deploy

Add step after deploy in deploy.cmd

:: 9. Test website
:: https://www.amido.com/code/powershell-win32-internal-error-the-handle-is-invalid-0x6/
call :ExecuteCmd PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "$ProgressPreference = 'SilentlyContinue'; exit ((invoke-webrequest -method head -uri 'http://%WEBSITE_HOSTNAME%/' -UseBasicParsing).statuscode - 200)"
IF !ERRORLEVEL! NEQ 0 goto error

Database Deployments
with Red Gate

Add more steps to deploy.cmd

:: 6. Migrate database
:: TODO: set %server%, %db%, %user%, %password%
call tools\sqlcompare-\sqlcompare.exe /scripts1:sql /server2:%server% /db2:%db% /username2:%user% /password2:%password% /sync /Include:Identical /exclude:role /exclude:user
IF !ERRORLEVEL! NEQ 0 goto error
call tools\sqldatacompare-\sqldatacompare.exe /scripts1:sql /server2:%server% /db2:%db% /username2:%user% /password2:%password% /sync /Include:Identical /AbortOnWarnings
IF !ERRORLEVEL! NEQ 0 goto error
:: popd

If migrations fail, deployment is aborted, database may be in an unknown state

Automate all the things

User steps to run it:

git push azure