.NET 6 Azure Web App Deployment using Azure DevOps Pipelines

This is the Summer of 2021, and Summer means a ton of new announcements and public previews of cool stuff from Microsoft to play with. This summer a few things are on my radar and, I’m really excited about the possibility.

First and foremost, .NET 6 with C# 10 (I can see the future lies here). Visual Studio 2022 and finally Windows 11. I got my personal development machine updated last weekend. To put everything into a test, I wanted to write a piece of code in .NET 6 (a Minimal API) with C# 10 in Visual Studio 2022 IDE on Windows 11, test it on Linux (WSL2). And, finally, deploy the app in Azure App Service using Azure DevOps pipeline.

Many in the Developer community including me think that GitHub is becoming the first-class citizen for Microsoft and Azure DevOps, the second. Azure DevOps is being considered in the “Others” category in most of the places. But I think many organizations are still on Azure DevOps and will be there for quite some time.

This blog post is for those, who are using Azure DevOps and wants to Code, Build and Continuously deploy their Modern .NET 6, C#10 apps to Azure using the Azure DevOps pipeline.

Alright, let’s get started with the .NET 6, C# 10 coding in Visual Studio 2022.

First thing first — code the .NET 6 API

With Visual Studio 2022, .NET 6 comes preinstalled. To start with, I fired up the new Windows Terminal (this comes pre-installed with Windows 11). I ran the command dotnet new web -o <your app name> to create the barebone API project.

Next, I opened this project in Visual Studio 2022. It created a single file API project with only Program.cs file, unlike any other older .NET core versions. Yes, it doesn’t have a startup.cs.

This gave me a plain vanilla .NET 6 minimal API code base. The complete sample project is available here to clone.

NOTE: If you are only interested in deploying a .NET 6 app in Azure App Service using Azure DevOps pipeline. Then you can directly jump into Build the home for the API in Azure App Service section and skip the next two sections.

Giving a touch of C# 10

I wanted to use some C#10 features in this API. At the time of writing of this blog C# 10 is in preview and the default C# version for Visual Studio 2022 is C# 9. 

You can check the default version of Visual Studio C# language version by running csc -languageversion:? command in Visual Studio Developer Command Line

So, to give this code a little bit of C# 10 touch, I changed the C# runtime to preview. To do so, I have edited the .csproj file from Visual Studio Solution Explorer (right-click on the project >> Edit project file) and added LangVersionas preview

<PropertyGroup>
<LangVersion>preview</LangVersion>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

This enabled C# 10 as the language runtime of my API project. 

I wanted to use the Global Namespace feature of C#10. The global namespace feature allows us to have all the import using into one single file as global import.

Observe the program.cs file. At the beginning of the file, there are no using imports. I have moved all of them into a new file Globalnamespace.cs and used global using as below —

global using System;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.Extensions.Hosting;

There are many new cool features coming to .NET 6 & C# 10. You can learn more from here.

Dev testing in Linux (WSL2)

Now that my code is ready. I wanted to check how it behaves in a Linux environment as I’m planning to use Linux OS for the Azure App Service. To test it out I ran this app in WSL2 from Visual Studio.

NOTE: If the .NET 6 is not installed on your WSL, then it will prompt you to install and it may take a while. Once installed you can select WSL 2 from the dropdown and run the API.

After a basic round of testing, I have committed the code to my Azure DevOps project’s Git Repo. 

Next step is to create a home for this API app in Azure.

Build the home for the API in Azure App Service

To host the API, I have created an Azure app service and selected Runtime Stack as .NET 6 (Early Access), Operating System as Linux, and Publish type as Code

NOTE: While writing this blog .NET 6 runtime for Azure App Service is in Early Access .

Since this is just for my testing, I have chosen the Free Tier as SKU and Size.

This App Service will be the home for my .NET 6 Minimal API. Next, I’m going to set up the Azure DevOps pipeline for continuous deployment.

Setup Continuous Deployment with Azure DevOps Pipeline

I have committed my code in the Azure DevOps Git repo in the previous step. Now, my objective is to create a pipeline and deploy the API in the Azure app service. 

NOTE: To deploy the API from the Azure DevOps to Azure App Service, a Service Principal Name (SPN) is needed. 

Next, I started creating the pipeline using the Set up build button. This button is available in the code repository page itself.

After clicking the Set up build button, I choose the Starter Pipeline 

This will create a yml file with a few basic steps. I have replaced the entire code with the below code snippet –

#This is a Build Deploy Pipeline for .NET 6 Web App to Azure App Service using Azure DevOps Pipeline

trigger:
- master

pool:
  vmImage: ubuntu-latest

variables:
  buildConfiguration: 'Release'
  dotNetFramework: 'net6.0'
  dotNetVersion: '6.0.x'
  targetRuntime: 'linux-x64'
  azureSPNName: 'YOUR-SPN-NAME' #get it from your AzureDevOps portal
  azureAppServiceName: 'Your-Azure-AppService-Name' #get it from your Azure portal

# Build the app for .NET 6 framework
steps:
- task: [email protected]
  inputs:
    version: $(dotNetVersion)
    includePreviewVersions: true
- script: dotnet build --configuration $(buildConfiguration)
  displayName: 'Build .NET 6 Application'

# Publish it as .NET 6 self-contained application for linux runtime
- task: [email protected]
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(BuildConfiguration) --framework $(dotNetFramework) --runtime $(targetRuntime) --self-contained --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: True

# Package the file and uploads them as an artifact of the build
- task: [email protected]
  inputs:
    targetPath: '$(Build.ArtifactStagingDirectory)' 
    artifactName: 'MinimalAPI'

#Publish it to the Azure App Service
- task: [email protected]
  inputs:
    appType: webAppLinux
    azureSubscription: $(azureSPNName) #this is the name of the SPN
    appName: $(azureAppServiceName) #App Service's unique name
    package: $(Build.ArtifactStagingDirectory)/**/*.zip

The above Azure Pipeline Code snippet does the following 

  1. Gets the code from the Git repo and build it targeted for.NET 6 
  2. Publishes the code as a self-contained application for Linux runtime 
  3. Packages the published output, Zips, and uploads to the artifact location
  4. Finally, the Azure Web App build task pushes the Zip file to the Azure App Service 

Since it is a continuous deployment pipeline, upon every code commit to the Git repo this pipeline deploys the .NET 6 API Code to the Azure app service. 

Testing the Deployed Minimal API

The DevOps pipeline ran smoothly for me and it deployed the code to the Azure App Service.

In my Minimal API, I have two endpoints

app.MapGet("/", (Func<string>)(() => "Welcome to .NET 6 Minimal API with C# 10!"));
app.MapGet("/counter", (Func<int>)(() => ++counter ));

The default (/) one shows a greeting message and the other one (/counter) increases the counter by 1 on every call. 

I opened both the URL in the Windows 11 vertical tabbed Edge browser (you can use any other modern browser of your choice). I got the desired result from both endpoints without any issue. 

This completes the entire journey of Code, Build and Deployment of a .NET 6 C# 10 API in Azure using Azure DevOps pipeline. YAY!!!

Wrap up

In this blog post, I wanted to cover the readiness of the Azure DevOps pipeline and Azure App Service for .NET 6 and C# 10. And, so far everything looks green.

After running the entire test, I’m happy with the result. I was able to Code, Build and Deploy a .NET 6 API with C# 10, and I was able to deploy in Azure App Service using Azure DevOps pipeline without any hiccups. 

As I conclude my blog here, I am waiting to see how the developer community is building and deploying their modern apps with Azure and Azure DevOps. Cheers!!!