I host my Azure Functions sandbox project on GitHub. The project doesn’t do much other than give me a safe place to experiment with functions, but nevertheless, I’d like to update it to the latest functions runtime, targeting .NET Core 3.1. But before I do so, I want a reliable way to validate that the upgrade hasn’t broken anything.
That’s when I got the idea to leverage Azure DevOps’s free tier to create a CI/CD pipeline to build, test, and deploy my functions.
Configuring Azure DevOps
Creating an Azure DevOps account is a few clicks at most, requiring nothing more than an email address. The free account comes with 2,000 hours of free hosted agent time and 2GB of artifact storage space. Perfect for small projects like mine.
Pipelines that run on the hosted agents seem to get queued for a bit before execution. An easy way to avoid the line-up is to configure your own computer as a self-hosted agent. You’ll find the instructions on how to do this under Project Settings → Agent Pools → Default → New Agent.
The agent’s installer gives you the option to run it on demand or as a Windows service. I chose on demand since I don’t need the agent all that often. I added an external tool to Visual Studio so that it’s never more than two clicks away to launch it:


Creating The Pipeline
Build and deployment pipelines can be created in a few ways within Azure DevOps:
- Using the visual designer to lay out separate build and release pipelines.
- Creating a YAML-based build pipeline that feeds a visually designed release pipeline.
- Enabling the multi-stage pipeline preview to build a YAML document that describes a pipeline across individual stages (build app, deploy to dev, etc).
I chose the multi-stage pipeline, even though I didn’t need its features per se. Once enabled, the New Pipeline button prompted me to choose where the code is housed. In this case, I chose GitHub, and all I needed to do is authorize the app on GitHub to read my code.
Building a YAML file from scratch is a pain. Luckily, the Pipeline assistant is packed with templates for building every kind of application under the sun. I chose the .NET Core Function App To Windows On Azure template, which comes with a build, archive, and deploy step, but no test step.
To add unit tests to the pipeline, I first used the VsTest@2 task. I quickly found myself fighting an error that I couldn’t shake no matter what I tried:
Tests that target the .NET core framework can be executed by specifying the appropriate target framework value
Instead of fiddling with VsTest, I switched to the DotNetCoreCli@2 task with the test parameter. It was much easier to configure, and once saved, my unit test projects run as expected:
- task: DotNetCoreCLI@2
displayName: Unit Test
inputs:
command: 'test'
projects: |
**/*.Tests.csproj
Side Note: A pipeline automatically gets the same name as the repository in which it’s committed, regardless of the filename you give it. In some scenarios, it can be useful to give the pipeline a more descriptive name. You can rename the pipeline by hovering over it, then clicking on the three dots that appear to choose Rename/Move.

Thoughts on YAML Pipelines
The pipeline assistant helps you find and implement the steps you need to perform a build and deploy. Once saved, the pipeline becomes source code like any other, but without the assistant, it’s difficult to make anything but the smallest changes to the pipeline. For that reason, you’re better off editing the pipeline in Azure DevOps since clicking on a task’s Settings link brings up the pipeline assistant.
Even with the assistant, I was constantly referring to the documentation to configure task parameters properly. On top of that, it’s difficult to visualize what the pipeline does from reading the YAML. The ideal experience, at least for me, would be to have the visual designers work alongside a living YAML document.
Closing Thoughts
Overall, I like the YAML experience, even with the issues I experienced. I’m especially fond of how it binds the application code to the pipeline code that’s needed to build and deploy the app at any given moment in time.
What has your experience been with YAML pipelines? Feel free to chime in below or on Twitter.