Skip to main content

CI/CD for Angular Using Azure Web App Service

Continuous integration is a paradigm that may be new to some, but it has been a best practice in most of the leading technology teams for years. With the latest service offerings from Azure, setting up a deployment pipeline is easy, and there is little reason not to try it out. For anyone not using it because you don’t have the infrastructure, it’s now as easy as a few commands at the command line and a couple files in your project. For anyone not using it due to lack of knowledge, this article hopes to give you the direction you need to try it out on a sample project and see it working for yourself. Ideally, you can then extend your usage to include additional steps such as automated tests. This will allow you to construct a more complete deployment pipeline, realize the advantages this approach offers, and get it working across other projects you work with.

CI/CD for Angular Using Azure Web App Service

Before we get into details, the key components in this process are as follows:

  • an Azure account
  • deployment files in your project (only two, and mostly contain generated code)
  • kuduscript (a tool for generating the aforementioned deployment scripts)
  • azure command line (to set up the necessary Azure resources)

This article is going to assume you already have an Azure account. Most developers with a MSDN account should have monthly Azure credit which allows you to set up what we’ll discuss here without any out-of-pocket cost, but obviously, your miles may vary. Also, please note there are multiple ways to accomplish the tasks described below. My method exposes you to a command-line approach, which lends itself to the scripting of repeatable tasks and version-controlling these scripts. These steps comprise the bedrocks of the Continuous Delivery discipline.

Assuming you already have a working Angular project for which you plan to apply this approach, let’s jump right into setting up the Azure resources for deploying this project. The first step is to create a dedicated Resource Group for this application. Generally speaking, resource groups provide a way to aggregate the different Azure resources for a single application to facilitate management of those resources (in particular, making it convenient to delete everything if you want to clean up and try again!). To do this, we are going to use the Azure command line interface [Azure-CLI], which is available on all major platforms (Mac, Linux, Windows). Once you have installed the Azure CLI, you can create a new resource group using the following command:

az group create --name ${MyResourceGroup} --location ${MyLocation}

Note the use of two variables in the command above (MyResourceGroup, MyLocation). A list of available Azure regions can be found here [Azure Locations] or by running the command “az appservice list-locations”. Note, apparently Microsoft locations have both a short-name (lowercase, no spaces) and a long-name (initial capitals, with spaces), and either are acceptable, just make sure to wrap the value in quotes if it is the long-name and has spaces. Obviously, you can either use the actual values directly within the command, or assign values to these variables within the script or define environment variables to supply the values. Your strategy around Continuous Integration within your organization will likely drive what works best for you, given that you’ll have various scripts for different applications as well as different ways to set up the correct variables and invoke the correct commands to achieve the desired result programmatically (i.e. - repeatedly and verifiably). Additionally, some of these commands will involve data that should not be committed to source control as it is considered secret data. This article won’t go into detail about organizational strategy for managing secrecy for these application- or account-specific operational details (passwords, account credentials, etc.), but be aware that there are techniques and tools to deal with this issue. With .NET Core, Microsoft has created the Secret Manager Tool that addresses this exact concern. Perhaps in a future article, we can go into more detail with this.

Getting back to the setup of your Azure resources, having created the resource group using the command above, you can now use any of the following commands to confirm that it actually exists:

az group list

az group list --output table

az group list --query "[?contains(name,'${MyResourceGroup}')]"

az group list --query "[?name=='${MyResourceGroup}']"

As you’ll see when you execute, the first command lists all resource groups in the default output format (JSON). The second command provides slightly less detail, but in a more easily readable tabular format. The other two commands show some of the capabilities for querying for specific data which can be valuable when you have a lot of resource groups and want to only see data for specific scenarios. The exact options available seem to change occasionally as Microsoft improves their tooling in this area, so I suggest exploring the command line help info for details about what options are available and their exact syntax.

Now that our resource group exists for the application, we need to choose an app service plan. The App Service Plans in Azure define several things about our physical resources which our application will use. For the purposes of exploring the Continuous Integration setup here, you don’t really have to worry about these details. However, for serious applications you will want to be intentional about the settings here as the app service plan allows you to configure things like the region, instance size, scaling, etc. for your app. For now, just know that you need something defined here, but the defaults will be sufficient for us at the moment. You can create a simple app service plan with the following command:

az appservice plan create --name ${MyAppServicePlan} --resource-group ${MyResourceGroup} --sku FREE

Again, you should be able to confirm that it exits by running the following query to get the details about it:

az appservice plan list

Now that the group and service plans exist, you can create the app itself using the following command:

az webapp create --name ${MyAppName} --resource-group ${MyResourceGroup} --plan ${MyAppServicePlan}

Again, we want to confirm that it exists by running one of the following queries:

az webapp list

az webapp list --output table

At this point, hopefully you are getting somewhat familiar with the Azure CLI tools and the ability to query for data. If you look at the output for the web app details, you’ll notice a “DefaultHostName” property. This is the URL at which your application will be available in Azure. At this point, there is nothing deployed there, but we can fix that in the next few steps!

Note, the above commands are needed for every application you set up, but you must also declare the user account under which deployments will be done. You do so using the following command:

az webapp deployment user set --user-name ${UserName} --password ${Password}

Now that Azure is ready to host our application, we are ready to deploy our code to Azure and see it running! To do this, we need to point Azure to our project’s source code repository and also have deployment files to control what happens during a deployment. 

For the first issue, we can go to the Azure portal (portal.azure.com), login, and look at our list of resources and we should see the resources we configured in the steps above. Specifically, we want to look for our “App Service” resource. Once we click on that, we should see a ‘blade’ (Microsoft’s term for the panels of information which expand/appear to the right as we make selections), and on the blade for our application’s app service we should see a selection called “Deployment options” under the “Deployment” category. If you have never configured deployment options for this application, the blade should present a single option which prompts you to “Configure required settings”.  Select this option, then choose from the list source code repositories available. This feature is limited to the options here, but the list offers many popular repository options that are likely to be used. After choosing your repository and providing your authentication credentials, you should be presented a list of the projects available with your credentials. Go ahead and select the specific project for which you want to configure continuous integration. Once you are done configuring your project with Azure, click the “Okay” button, and in your repository, you will see a webhook registered by Azure in your source repository. For GitHub in particular, this is visible by going to your project repo, then under “Settings” > “Webhooks” you should see a URL registered with the repo, having a value like “***.scm.azurewebsites.net/deploy”; the webhook is how Azure knows whenever you commit changes to your repo. In this case, it is going to trigger a deployment of your latest code to Azure. Now let’s discuss how that happens on the Azure side.

Under the covers, Azure uses a cross-platform scripting tool called “kudu” and its associated kuduscript can perform custom deployment steps, which is what we will leverage to deploy our application. First, add the kuduscript package to your Angular project’s devDependencies by executing the following:

npm install --save-dev kuduscript

This installs the kuduscript tool to your project, and we use this tool to generate a template deployment script for node/npm-based projects using the following command:

kuduscript -y --node

You may notice after running the above command that two new files have been created: “.deployment” and either “deploy.sh” (on Mac or Linux) or “deploy.cmd” (on Windows).  The “.deployment” file simply indicates which command to run to perform a deployment which basically points to “deploy.sh/cmd”, and all of the steps during deployment will be in the “deploy.sh/cmd” file. The deploy.sh/cmd file may seem intimidating, but you can ignore most of it and simply add the steps which are needed to build and deploy your application. As our focus is on Angular apps, ideally, we should only need the following steps to do a simple build and deploy of our application:

            1. “npm install” to install all the npm packages our app needs to build

            2. “npm build” to build & package our app into a /dist/ folder

            3. copy files from /dist/ folder to where they will be served at runtime

Fortunately, kuduscript provides several environment variables to help accomplish this and they are already configured with appropriate values for our application on the Azure infrastructure, for example:

            DEPLOYMENT_SOURCE => D:\home\site\repository

            DEPLOYMENT_TARGET => D:\home\site\wwwroot

            NPM_CMD => “D:\Program Files (x86)\nodejs\6.9.1\node.exe" "D:\Program Files (x86)\npm\3.10.8\node_modules\npm\bin\npm-cli.js"

The $DEPLOYMENT_SOURCE variable points to the repository folder within the Azure infrastructure that holds a copy of our repository. When we commit a change to our repo, the Webhook is triggered, and Azure will pull the latest code from our repo to which we pointed in the Deployment options above. Once Azure has pulled down the latest code locally, it will run the deployment script which builds and deploys our code. The $DEPLOYMENT_TARGET variable points to the folder from which our application is served out to the world. So, our deployment script really just needs to execute the three steps we noted above. The steps in our script look like the following, but may differ in syntax depending on your platform:

if [ -e "$DEPLOYMENT_SOURCE/package.json" ]; then

  cd "$DEPLOYMENT_SOURCE"

  eval $NPM_CMD install

  exitWithMessageOnError "npm failed"

  cd - > /dev/null

fi

 

if [ -e "$DEPLOYMENT_SOURCE/package.json" ]; then

  cd "$DEPLOYMENT_SOURCE"

  eval ./node_modules/@angular/cli/bin/ng build eval $NPM_CMD install

  exitWithMessageOnError "npm build failed"

  cd - > /dev/null

fi

 

if [ "$IN_PLACE_DEPLOYMENT" -ne "1" ]; then

  "$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE/dist/" -t "$DEPLOYMENT_TARGET" \

                -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" \

                -i "e2e;node_modules;src;.angular-cli.json;.deployment;.gitignore;az.ps1;deploy.sh; \                              package.json;README.md;tsconfig.json;"

  exitWithMessageOnError "Kudu Sync failed"

  cd - > /dev/null

fi

 

The general effect of the scripts above should be clear:

            1. the first block executes an “npm install” command

            2. the second block executes the “npm build” command

            3. the third block copies the files from the repository’s dist/ folder to the wwwroot/ folder

Now when you commit changes to your project, you should see it get deployed and be available at “<project-name>.azurewebsites.net”! You should see the status of deployments when clicking on the “Deployment options” for the application within the Azure portal and if anything goes wrong, you can click on a specific deployment to see the deployment details, within which there is an option to View Log and see exactly what happened during the execution of the deployment script. Hopefully you don’t need that very often. In some cases, the log content will exceed what can be shown via the Azure portal, in which case you can access the full logs and more by going to the following URL:

                https://<azure-project-name>.scm.azurewebsites.net

From here, you can browse the files and use a Debug Console to determine the root cause of any issues. This site is effectively where the CI/CD management takes place, and this web portal gives you direct access to the files and console to see what is going on. At the very least, I recommend going here for your site just to get more familiar with what is happening under the hood.

There are certainly other topics to discuss around continuous delivery, especially Azure’s Deployment Slots feature. This tool allows you to configure a series of target deployment environments for successive validation prior to pushing to the actual production environment. But let’s save those details for another day! Hopefully at this point you can see (and put into practice) how Azure’s infrastructure allows you to exercise a best practice of continuous integration for your Angular applications. Now there is one less excuse for not doing so!

Resources:

Solution Category:
Speciality Category: