Building and Deploying a .NET 6 Azure Function with Gitlab CI/CD

Date:
2022-02-13

I use Gitlab for my source code for most of my personal projects. There is a lot that I like about Gitlab. There are unlimited repositories, and I can choose to make any of them private if I'd like. One of the features that I like most about Gitlab is Gitlab CI/CD.

Gitlab CI/CD is a DevOps tool that allows me to create pipelines that automatically build and deploy the code in the git repository. Pipelines are defined as YAML files with the name .gitlab-ci.yml, and saved in the root of the repository. There is a defined syntax to build these pipelines, and they get versioned in the same way that the rest of the codebase in the git repository does.

I'm working on a .NET 6 Azure Function for a side project right now. My git repository for this is hosted in Gitlab, and I'm looking for easy ways to deploy the code out to Azure. Visual Studio Code has an Azure plugin that allows for deployment to Azure. While this works, I must remember to deploy every time I commit a change to the master branch of my git repository, and I want to eliminate mistakes. I've created a Gitlab CI/CD pipeline that allows me to build, test, and deploy on every commit.

Just show me the code!

Here's the code for my .gitlab-ci.yml file for my project:

image: mcr.microsoft.com/dotnet/sdk:6.0

stages:
    - build
    - test
    - deploy

build:
    stage: build
    script:
        - "dotnet build"
    artifacts:
      paths:
        - bin/

test:
    stage: test
    script: 
        - "dotnet test"

deploy:
  stage: deploy
  script:
    - curl -sL https://aka.ms/InstallAzureCLIDeb | bash
    - apt-get install curl && curl -sL https://deb.nodesource.com/setup_12.x | bash -
    - apt-get install nodejs
    - npm install -g azure-functions-core-tools@4 --unsafe-perm true
    - az login --service-principal -u $APPLICATION_ID -p $APPLICATION_SECRET --tenant $TENANT_ID
    - func azure functionapp publish $FUNCTION_APP --csharp
  only:
    - master

You may be asking yourself: What does this all mean? Let's break it down, section by section.

The image property

Gitlab CI/CD pipelines execute in a Docker container. This helps enable repeatability of the pipeline execution. image: mcr.microsoft.com/dotnet/sdk:6.0 defines that this pipeline will run in the official .NET 6 Docker Image. This image is needed as my project is a .NET 6 project.

The stages property

Gitlab CI/CD has a feature called stages. Stages contain groups of jobs that execute. My pipeline defines three stages that will run in sequence - build, test, and deploy.

build and test stages

These stages use the .NET CLI to build and test the code for my Azure Function. I can use the .NET CLI in this pipeline because it is included with the official .NET 6 Docker Image. The features of the .NET CLI could be a completely different series of blog posts. It's an essential tool for modern .NET development, with many powerful features.

Other than the .NET CLI commands, the artifacts portion of the build stage is key to allowing us to deploy the Azure Function. The artifacts section states that the bin folder, which is where the built code resides, will be consumable in future stages of the Gitlab CI/CD pipeline.

The deploy stage

This is where the real fun begins! The official .NET 6 Docker Container defined in the image property is executing using Gitlab CI/CD's shared runners on Linux. Due to this, all of the commands in this stage are compatible with a Linux-based host. All of the magic in this stage is defined under script.

This script does the following:

  1. Uses curl to download a Microsoft script to install the Azure CLI.
  2. Install Node.js, which is a prerequisite for the next step
  3. Instal Azure Functions Core Tools using the Node Package manager. I'm using v4 of the Azure Functions Core Tools as my function uses the version 4.x runtime.
  4. Login to Azure using the Azure CLI (az) with a Service Principal. I created a Service Principal independent of my DevOps pipeline, and I'm using Gitlab CI/CD variables to pass in sensitive values.
  5. Use the Azure Function Core Tools that was installed in the previous step to publish my function to Azure.

Wrapping up

This solution has allowed me to make the development of my Azure Functions faster and less error prone. There's a lot of cool technology to dig into further here. I hope this post gives you some inspiration on what you're working on.

P.S.: Credit goes to this post on dev.to and this post on Stack Overflow for providing me with information to learn how to set up my pipeline.

© 2023 Phil Busch. Crafted in Milwaukee, Wisconsin, USA.