Build and Deploy Web Applications With Azure DevOps

May 30, 2023
15 min read

In this article, I will be exploring one way to build and deploy web applications using Azure DevOps as a Continuous Integration and Continuous Delivery (CICD) service. I will cover some quick concepts about building and deploying with Azure DevOps and then proceed to build and deploy an ASP.NET Core Application using pipelines to an Azure web app, going step-by-step through the following outline:

  • Azure DevOps services
  • Creating an Azure web app
  • Connecting your Azure DevOps Pipeline to Azure
  • Setting up a build
  • Setting up a release

This article will assume you have the following:

If you do not, please get those in place before continuing.

Let us get started.

Azure DevOps Services

Azure DevOps is a suite of cloud services that enables organizations to build and deploy applications, plan and manage work, store and control source code access, test applications, and store application artifacts. Being a suite of services, Azure DevOps offers individuals and organizations the choice of tools where they can pick one, a couple, or all the Azure DevOps offerings for use in their application development process.

There are five main Azure DevOps service offerings:

  1. Boards: for managing work items like bugs, features, epics, and user stories.
  2. Repos: used to store code repository using either Git or Team Foundation Version Control.
  3. Pipelines: used for building and deploying code to cloud or on-premises servers.
  4. Test: helps to manage test plans and manual tests.
  5. Artifacts: enables developers to share code and packages.

You can extend these services by using extensions from the Azure DevOps Marketplace.

Connecting Your Azure DevOps Pipeline to Azure

Because Azure DevOps Pipelines is a paid service and free pipeline runs have mostly been disabled, configuring your build and release pipeline to use the Microsoft hosted agents will require some sort of billing. If you currently have Azure DevOps billing enabled in your organization, you can ignore this section.

To avoid having to pay the cost of the pipeline agents through Azure DevOps, you can follow these steps to connect your pipeline runs to your Azure account.

  1. In your Azure DevOps organization home, select Organization settings.
A screenshot of Azure DevOps Organisation home page, with the Organization settings button highlighted.
Figure 1: Organization Settings in Azure DevOps. | Used with permission from Microsoft. View Full Size

2. Then Billing > Set up billing.

Screenshot of the billing menu within Azure DevOps organization settings. The billing menu in the sidebar is highlighted as step one, and the set up billing button is highlighted as step two.
Figure 2: Set up billing for Azure DevOps. | Used with permission from Microsoft. View Full Size

3. Selecting the Set up billing button will load a pop-up to the side of your screen, where you will select your desired Azure subscription.

Screenshot of a sidebar loaded showing a list of Azure subscriptions.
Figure 3: Set up billing for Azure DevOps using your Azure account. | Used with permission from Microsoft. View Full Size

If you see the red warning section like above, follow this Microsoft documentation to fix that or go through these steps:

3a. Go to your Azure subscription from the Azure Portal and click on the spending limit prompt.

Screenshot of the Azure Portal notifying the user of a credit limit on their Azure subscription.
Figure 4: In the Azure subscription window, a spending limit is in place. | Used with permission from Microsoft. View Full Size

3b. Follow through the prompts and input your credit card information as needed.

Screenshot of the Azure portal prompt where the user is adding card details for Azure billing. The prompt comes after the user clicks the notification to remove spending limit on the Azure subscription.
Figure 5: Removing the spending limit. | Used with permission from Microsoft. View Full Size

3c. After a few minutes, you should be able to now activate the subscription for Azure DevOps

Screenshot of an Azure Portal notification informing the user that spending limit has been removed on their account.
Figure 6: The spending limit has been removed. | Used with permission from Microsoft. View Full Size

4. Select Save and you will be good to go after a couple of seconds.

Screenshot showing a valid Azure subscription for linking Azure DevOps billing. The Save button in the bottom-right corner is highlighted.
Figure 7: Billing setup with spending limit removed and a valid subscription notification. | Used with permission from Microsoft. View Full Size

Creating an Azure Web App

Although it is recommended to deploy infrastructure for apps as part of your CICD process, I will be using the Azure Command Line Interface with PowerShell to create an Azure web app for this article.

Run the following commands to create the Azure web app:

$resourceGroupName = “gwl-tekki-rg”
$location = “uksouth”
$webAppName = “gwltekki-webapp”
$appServicePlanName = “gwltekki-app-plan”
az group create --location $location --name $resourceGroupName.
az appservice plan create --name $appServicePlanName --resource-group $resourceGroupName --location $location --sku “B1” --is-linux
az webapp create --name $webAppName --resource-group $resourceGroupName --plan $appServicePlanName --runtime “DOTNETCORE:7.0”

Note: Remember to replace the Azure web app name with something globally unique.

There should now be two resources inside the resource group created.

Screenshot of the Azure Poral showing a newly created resource group containing an App Service plan and Azure web app.
Figure 8: Newly created resource group with an app service plan and web app. | Used with permission from Microsoft. View Full Size

Once this step is completed, we are ready for the next step.

Setting Up a Build

To set up a build pipeline in Azure DevOps, there are two options – the pipeline-as-code (YAML), and the visual editor method. I will be using the YAML method here, but feel free to explore the visual editor if you wish to.

With pipelines, you can connect to your code repository in Azure DevOps or from any code repository service like GitHub, Bitbucket or a local git server you own. In this article, I will be using repos in Azure DevOps.

I will use a sample ASP.NET Core application in this article. You can get the source with pipeline code and scripts here that I will be using to set up our build and release.

From your Azure DevOps homepage, navigate to your project (see here for instructions to set up an Azure DevOps project) and go to Pipelines > Pipelines.

Screenshot showing Azure DevOps steps for creating a build pipeline. Step 1 is selecting the Pipelines submenu, and step 2 is selecting the Pipelines option from the sub-menu.
Figure 9: Create a pipeline. | Used with permission from Microsoft. View Full Size

If this is your first time working with Azure Pipelines, you will see a screen that looks like the picture below.

Screenshot of a new pipeline page in Azure DevOps for someone creating a pipeline for the very first time.
Figure 10: Create your first pipeline. | Used with permission from Microsoft. View Full Size

Select the Create Pipeline button and you should get some options on how to get started with your pipeline.

Screenshot of the Azure Pipeline prompting the user to select where the code is stored to create a build. The GitHub option is highlighted.
Figure 11: Select GitHub YAML. | Used with permission from Microsoft. View Full Size

I will be using GitHub with the YAML option for this tutorial. This will require an authentication with GitHub for the first time. So, complete the GitHub prompts and sign-in flow and you should get a list of all the GitHub repositories you have access to. I will be using the gwl-tekki-build-and-deploy-with-azdo repository.

Screenshot showing the selected repository for creating the build pipeline.
Figure 12: Select repository. | Used with permission from Microsoft. 

In your case, you may need to fork the repository or just create a simple ASP.NET Web API using the dotnet new command on your computer and push to your GitHub repository.

After selecting the repository, you will get a list of options that looks like the image below. Select the Show more button to explore the diverse options available.

Screenshot of the Configure your pipeline step within the pipeline creation process. The Show more pipeline options button is highlighted.
Figure 13: Show more pipeline options. | Used with permission from Microsoft. 

Among the many options, you should see an ASP.NET item. This is what I will be building upon in this tutorial, but you can explore the options to see what YAML code is generated for you.

Screenshot of more options within the Configure your pipeline screen. The ASP.NET Core option is highlighted.
Figure 14: Select ASP.NET Core. | Used with permission from Microsoft. View Full Size

Some YAML code will be generated for you. Let’s look at the four main sections of that code and what they mean.

Screenshot of the autogenerated YAML code from selecting ASP.NET Core.
Figure 15: Generated YAML Code. | Used with permission from Microsoft. 
  • The first section is a trigger, which specifies that Azure DevOps should kickstart the build commands whenever there are changes in the main or any of the listed branches.
  • The second section declares the type of build agent that should be used to run the build commands. In this case, it is telling Azure DevOps to use the latest version of Ubuntu build server.
  • The third section helps you set up the key-value pairs of variables you will be using in your pipeline.
  • The fourth section is where most of the fun happens. This is where I will set up what actions should be performed on the build server.

Let’s Save and Run and see what happens first. I will use main branch for this demo, but best practice is to create a new branch for such activities.

Screenshot highlighting the Save and run button to build the autogenerated YAML code.
Figure 16: Modify generated YAML code. | Used with permission from Microsoft. View Full Size
Screenshot showing branch options for saving and running the YAML code changes.
Figure 17: Save and run. | Used with permission from Microsoft. View Full Size

The changes are committed, and a pipeline run is triggered immediately. At first, it will fail with this configuration, which is fine, because you have not yet set the right folder for where the pipeline should pick the ASP.NET Core code from.

Screenshot showing the build failure when the pipeline is run for the first time.
Figure 18: Build failure. | Used with permission from Microsoft. View Full Size

If you select the job, you will see the reason for failure.

Screenshot showing the cause of the build failure in the dotnet build step.
Figure 19: Reason for build failure. | Used with permission from Microsoft. View Full Size

Now, set the pipeline to build your code from the correct directory. Go back to Pipelines, select the ellipsis beside the failed pipeline run, then select Edit.

Screenshot showing the edit option when the ellipsis beside the failed pipeline run is selected.
Figure 20: Edit pipeline to fix build failure. | Used with permission from Microsoft. View Full Size

On line 16, add TekkiGurus.WebAppDemo so the line looks like the screenshot below, then save.

Screenshot highlighting the line to edit to fix the build pipeline failure.
Figure 21: Fix build failure. | Used with permission from Microsoft. View Full Size

If you then go back to Pipelines, you should see an active run (or a completed build).

Screenshot showing a successful build after setting the right directory.
Figure 22: Build succeeds. | Used with permission from Microsoft. View Full Size

Go into the most recent run and select Job to see the details of the build.

Screenshot showing the successful build steps.
Figure 23: Investigate build steps. | Used with permission from Microsoft. 

At this point, you have now completed the fundamental parts of the build steps. But, in order to completely build a .NET project in Azure DevOps, you also need to get the build output into an artifact so that you can publish it to Azure. Let’s start that now.

Go back to Edit Pipeline and add the following to the azure-pipelines.yml file to enable the pipeline to run copy and publish build artifacts.

- script: dotnet publish --no-build  --configuration $(buildConfiguration) --output $(build.artifactstagingdirectory) TekkiGurus.WebAppDemo
  displayName: 'dotnet publish $(buildConfiguration)'

The script should look like this:

Screenshot highlighting the YAML code section to add so the pipeline can publish the build.
Figure 24: Publish code. | Used with permission from Microsoft. View Full Size

After that, you will use the publish task in the Azure Pipelines Tasks library. Create two new lines after line 19, go the Tasks pane and search for publish. Then select the Publish build artifacts option.

Screenshot highlighting the Publish build artifacts option.
Figure 25: Publish build artifacts. | Used with permission from Microsoft. View Full Size

In the prompt that follows, just select Add, leaving all other fields as they are.

Screenshot highlighting the Add button.
Figure 26: Select Add. | Used with permission from Microsoft. 

Your pipeline code should then look like this:

Screenshot showing what the pipeline code should look like after adding the Publish build artifacts task.
Figure 27: After adding build artifact task. | Used with permission from Microsoft. View Full Size

Save and go back to Pipelines to see it run. The pipeline run should complete successfully with a message that looks like this:

Screenshot showing a successful build and published artifacts section.
Figure 28: Successful build. | Used with permission from Microsoft. View Full Size

And the job summary should look like this:

Screenshot highlighting the published artifact.
Figure 29: Build summary. | Used with permission from Microsoft. View Full Size

To deploy our generated code artifacts to Azure, I will modify our pipeline file to utilize stages so I can separate the build portion from release.

After line 7, create a new line and enter the following to set the pipeline run variables:

Screenshot showing the variables to add to the existing YAML code.
Figure 30: Add new variable. | Used with permission from Microsoft. 

Then enter the following to declare the two stages of your pipeline:

Screenshot showing the two stages to add to the existing YAML code.
Figure 31: Create stages in pipeline. | Used with permission from Microsoft. 

This splits your pipeline run into two separate sections – one for building and the other for deploying to Azure. You will remove some bits from this code in a short while.

You will now expand the build stage to contain the previous build code, then add some tasks to compress the folder and publish it.

Modify the first stage (build) by adding the following code after the dotnet publish task:

jobs:
	- job:
	displayName: 'Build code and publish artifacts'
	pool:
		vmImage: $(buildAgent)
	steps:
		- script: dotnet build --configuration $(buildConfiguration) TekkiGurus.WebAppDemo
		displayName: 'dotnet build $(buildConfiguration)'
		- script: dotnet publish --no-build --configuration $(buildConfiguration) --output $(build.artifactstagingdirectory) TekkiGurus.WebAppDemo
		displayName: 'dotnet publish $(buildConfiguration)'
		- task: ArchiveFiles@2
		inputs:
			rootFolderOrFile: '$(build.artifactstagingdirectory)'
			includeRootFolder: false
			archiveType: 'zip'
			archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
			replaceExistingArchive: true
		- task: PublishBuildArtifacts@1
		displayName: 'Publish build to artifacts directory'
		inputs:
			PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
			ArtifactName: 'drop'
			publishLocation: 'Container'

The whole build stage should look like this:

Screenshot showing the entire build stage code.
Figure 32: Full build stage. | Used with permission from Microsoft. View Full Size

This performs the same actions as your previous build, but now in this sectioned stage.

Let’s now add a dummy job to the release stage before putting in real code to assess if our stages are configured correctly. Modify the second stage (deploy) to look as below:

Screenshot showing the dummy stage added to the pipeline code release stage.
Figure 33: Create dummy release stage. | Used with permission from Microsoft. 

Save and go check out the pipeline run. The run should now contain two steps as below:

Screenshot of the successful pipeline run with dummy code added in the release stage.
Figure 34: Code stages succeeded. | Used with permission from Microsoft. View Full Size

Below is a summary of what you have done in the build stage:

  • Run a dotnet build command to compile your code.
  • Run dotnet publish to strip out any unnecessary code for deployment.
  • Compressed the published folder into a zip file.
  • Set it as an artifact ready for deployment to Azure.

With that, you are ready to create a release.

Setting up a release

The rest of your work is now on the release stage. Here, you will do the following:

  • Download the zip artifact from the build stage.
  • Deploy the zip folder to your created Azure web app.

To download the zip artifact, you need to add a download task to the release stage.

First, remove the dummy script to add the download task and in the Search task box, search for Download build artifacts and select.

Screenshot detailing how to add the download build artifacts step
Figure 35: Download build artifacts. | Used with permission from Microsoft. 

In the pop-up blade, set the download type to specific, and set the matching pattern as **/*.zip.

Screenshot highlighting the newly added task to download the published build artifacts.
Figure 36: Download build artifacts. | Used with permission from Microsoft. 

Save and check what happens. Feel free to set a displayName.

After the download stage, you can now add the task to deploy the application to Azure. Search for the Azure web app task and select it.

Screenshot showing how to add the Azure web app step to the release stage.
Figure 37: Add Azure web app step. | Used with permission from Microsoft. 

Select the target Azure subscription, app type and app name and change $(System.DefaultWorkingDirectory)/**/*.zip to $(System.ArtifactsDirectory)/**/*.zip , then set the runtime stack to DOTNETCORE|7.0 and save.

Screenshot showing the parameters to modify to configure the release stage to deploy to Azure
Figure 38: Set Azure web app parameters. | Used with permission from Microsoft. 

Save and check the pipeline run. For the first time, you will need to allow the pipeline to access Azure again.

Screenshot showing Azure permission needed for first release run.
Figure 39: Azure permission for first release run. | Used with permission from Microsoft. View Full Size

Follow the prompts and permit. The pipeline run should then continue normally. After the run, you should have a successful deployment that looks like this.

Screenshot showing successful deployment steps of the Azure web app from Azure DevOps
Figure 40: Successful deployment steps. | Used with permission from Microsoft. View Full Size

When you select the link to visit the web application from the Azure Portal, it should load up fine.

Screenshot showing the successfully deployed application running on a browser.
Figure 41: A successfully deployed application. View Full Size

Summary

In this article, I have shown you some Azure DevOps service offerings, how to connect your Azure DevOps pipelines and billing to Azure, how to create an Azure Web app, and how to set up a pipeline to build and release to your code. There is still a world of possibilities you can achieve with Azure DevOps and Azure, and I hope you enjoy working with them. Happy deployments!

Dara Oladapo

Dara Oladapo

Dara Oladapo is a technology lover with almost a decade of professional experience in Azure and DevOps technologies. He comes from a software development background and is a Microsoft Developer Technologies MVP and a certified trainer in Azure Fundamentals, Development, Administration, and DevOps. He frequently speaks to a wide variety of audiences, from beginners to industry professionals, and across different age groups. His core passion is to learn and teach technology, and his lifelong goal is to speak about awesome technology and innovation that drives developer productivity.