CI/CD Of Azure Web Apps With GitHub Actions

Introduction

GitHub Actions enables automating processes like integration and deployment through workflows in GitHub. A workflow is a collection of one or more jobs that are executed in response to some trigger (automated or manual) or on scheduled intervals. In this article, we will create a simple workflow to build, test and deploy ASP.NET Core web app to Azure App Service.

Typically, whenever code is merged into a master (or main) branch in GitHub, we would like to ensure that it builds without any error and all the unit tests pass. These steps will constitute Continuous Integration (CI) part of the workflow. After build and tests are successful, we would like to deploy the code to Azure App Service so that it is available for QA and eventually to end users. This comprises our Continuous Deployment (CD) part of the workflow.

CI/CD

Setup

Create a new repository in GitHub and note the remote URL of the repo. We will use this URL to push our code from local git repo to GitHub.

Create a new folder called github-actions-demo where the app and the test project will be stored on your local machine. Create a new solution file inside this folder by executing the following dotnet CLI command

dotnet new sln

Create a new ASP.NET MVC Core Web App by executing the following dotnet CLI command in the context of the folder created in the previous step.

dotnet new mvc -o github-actions-demo-app

Add the project to the solution by executing the following dotnet CLI command

dotnet sln add github-actions-demo-app

Switch to the project directory (github-actions-demo-app) and build the project

dotnet build

Run the app using the following CLI command

dotnet run

It should display the application home page in the browser as shown in the screenshot below:-

GitHub setup

Switch back to the root project folder and add a new MSTests Project by running the following dotnet CLI command

dotnet new mstest -o github-actions-demo-app-tests

Add the test project to the solution

dotnet sln add github-actions-demo-app-tests

Add reference of the ASP.NET project to the test project by switching to the test project directory in console and executing the following dotnet CLI command

dotnet add reference ../github-actions-demo-app/github-actions-demo-app.csproj

Open the test project in VS Code. For the purpose of this article we will add a dummy test as follows in UnitTest1.cs file

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        Assert.IsTrue(true);
    }
}

Build the test project and execute tests using the following CLI command. Note that the following command also works in context of the root project folder that contains the solution file

dotnet test


Adding projects to local git repo

Add gitignore file to the root project directory (github-actions-demo) by executing the following CLI command

dotnet new gitignore

Initialize local git repo

git init

Stage the changes

git add .

Commit changes to local git repo

git commit -m "Initial commit"

Add URL of the remote GitHub repository. Replace <repo_url> with the URL of the repository that you created in one of the previous steps

git remote add origin <repo_url>

Push code to GitHub

git push origin master

Navigate to the GitHub repo in your browser to validate the code is pushed successfully.

Adding projects to local git repo

Continuous Integration

You can create a GitHub action workflow by navigating to the Action tab on your repository page in the browser. Alternatively, you can also write workflow declaratively inside your code editor. We will go with the second approach.

Add a new folder inside the root project directory and name it .github. Create a subfolder inside .github folder called workflows. Inside the workflows folder add a new file named app-build-deploy-workflow.yml. The folder structure looks like the screenshot below (from VS Code):-

Adding projects to local git repo

Add following code to app-build-deploy-workflow.yml

name: build-deploy-azure-app
on:
  push:
    branches:
      - master
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout branch
        uses: actions/checkout@v2
      - name: Install dotnet
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.x
      - name: Restore Nuget packages
        run: dotnet restore
      - name: Build
        run: dotnet build --no-restore
      - name: Test
        run: dotnet test --no-build
      - name: Publish
        run: dotnet publish github-actions-demo-app/github-actions-demo-app.csproj -c Release -o website
      - name: Upload Artifact
        uses: actions/upload-artifact@v2
        with:
          name: app
          path: website/**
          if-no-files-found: error
  • name – Name of the workflow
  • on - Defines the trigger condition – only run the workflow when the code is pushed to the master branch
  • runs-on – The GitHub managed VM image on which the CI job would be executed
  • Checkout branch – Checks out the master branch to enable the job to read the code
  • Install dotnet – Installs dotnet CLI that will be used to build, test and publish the projects. Since my projects target .NET 6, I have mentioned the same as the .NET SDK version under dotnet-verison. Please change the value as per the version that your project targets.
  • Restore Nuget packages – Restore nuget package dependencies
  • Build – Build the project. Since the dependencies have already been installed in the previous step, we need not attempt to restore them again
  • Test – Execute tests in all the test projects present in the solution
  • Publish - Publish the web app in Release mode to directory named "website"
  • Upload Artifact - Store the output published in the previous step so that it can be used in subsequent job while deployment. If there is no output from the previous step, throw an error.

Stage and commit the changes and push them to GitHub using the git commands mentioned in the previous steps. As soon as you perform push to master branch, the workflow is triggered. You can watch the execution logs of the workflow under the Actions tab on your repository page. After successful execution of the workflow, an artifact is generated that contains the deployable package that can be consumed by CD process to deploy the app to Azure App Service.

Adding projects to local git repo

Azure Setup

Create a new resource group and an Azure Web App targeting appropriate .NET version and OS.

Create new Azure service principal by navigating to Azure Active Directory -> App Registrations -> New registration and filling in the required details. Once the service principal is created, note the application id. On the left menu, click on Certificates & Secrets and generate a new secret. Note the application secret. We will use the id and secret while configuring the deployment job for workflow.

Navigate to the Azure web app, click on the Access Control (IAM) menu on the left. Click on Add role assignment and select Contributor role and click Next. Clik on Select Members, search and add the service principal name created in the previous step and click Review + assign. This will assign Contribute permission to the service principal to the Azure Web app to be able to perform deployments.

Azure Setup

Also note the tenant ID and subscription ID of your Azure tenant and subscription as they would be needed along with the service principal credentials to establish connection from our workflow to Azure.

Continuous Deployment

Let’s first store the credentials and Azure ids as repository secrets. Navigate to the GitHub repository settings and click on Secrets on the left menu. Add a new secret called AZURE_CREDENTIALS. The value of the secret is a JSON object in the following format (Replace <GUID> with corresponding values from your Azure subscription).

Azure Setup

Update the app-build-deploy-workflow.yml file in the code editor. Add a "deploy" job after the "build" job as follows (below snippet just shows a particular section of the workflow file. The entire yml file text is mentioned towards the end of this article).

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download Artifact
        uses: actions/download-artifact@v2
        with:
          name: app
          path: app
      - name: Login to Azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
      - name: Deploy to Azure
        uses: azure/webapps-deploy@v2
        with:
          app-name: github-actions-demo-1
          package: app
      - name: Logout from Azure
        run: az logout
  • needs – Specifies that the deploy job is dependant on successful execution of the build job
  • runs-on – The GitHub managed VM image on which the CD job would be executed
  • Download Artifact – Downloads the artifact that was uploaded by the the build job
  • Login to Azure – Log in to Azure with the credentials stored in the repository secret. Note that repository secrets can be accessed by following syntax - 
    • ${{ secrets.<secret_name> }}
  • Deploy to Azure – Deploy the app artifact contents to Azure web app. github-actions-demo-1 is the name of my Azure web app. Replace this with the name you have chosen for your Azure web app.
  • Logout from Azure –Logs out from Azure

Complete Workflow YAML File

name: build-deploy-azure-app
on:
  push:
    branches:
      - master
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout branch
        uses: actions/checkout@v2
      - name: Install dotnet
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.x
      - name: Restore Nuget packages
        run: dotnet restore
      - name: Build
        run: dotnet build --no-restore
      - name: Test
        run: dotnet test --no-build
      - name: Publish
        run: dotnet publish github-actions-demo-app/github-actions-demo-app.csproj -c Release -o website
      - name: Upload Artifact
        uses: actions/upload-artifact@v2
        with:
          name: app
          path: website/**
          if-no-files-found: error
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download Artifact
        uses: actions/download-artifact@v2
        with:
          name: app
          path: app
      - name: Login to Azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
      - name: Deploy to Azure
        uses: azure/webapps-deploy@v2
        with:
          app-name: github-actions-demo-1
          package: app
      - name: Logout from Azure
        run: az logout

Final Commit and Test

Stage and commit the changes and push them to GitHub using the git commands mentioned in the previous steps. As soon as you perform push to master branch, the workflow is triggered.

Azure Setup

After successful execution of the workflow, you should be able to see the app under Azure web app URL

Azure Setup

Let's make a small change to the Welcome text. Open the web app in code editor and update "Welcome" in Views -> Home -> Index.cshtml to "Welcome to DevOps !!"

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome to DevOps !!</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

Save the file, stage, commit and push the changes to GitHub repo's master branch. After successful workflow execution the code changes are visible on the deployed Azure web app. Refresh the browser where you have the Azure web app open and you should be able to see the changes.

Azure Setup

References


Similar Articles