Migrating from Azure Functions version 1 to 2 to 3 and beyond

What a title! Being an author of many books, articles and posts, I really think I could write a book about this and what is happening behind the scenes, so don’t think your going to get all the ins and outs from this post.  It all comes down to what value you place in the FUNCTIONS_EXTENSION_VERSION application setting.  Be for certain that migrating to a new runtime most often requires more than changing the value of FUNCTIONS_EXTENSION_VERSION. At the moment you have a few options, the Description is the most probable scenario, which I’ll write to later.

Version Value Description
3.x ~3 You are targeting .NET Core 3.1
2.x ~2 You are targeting .NET Core 3.1
2.x ~2.0 You are targeting .NET Core 2.1
1.x ~1 You are targeting .NET Framework 4.8

There is also a concept of Pinning, which you can use to temporarily target your Function App to a specific Azure Functions Runtime version.  For example, if you are running with ~3, it means you will always get bumped to the newest version of the runtime when released.    You can see the announcement of a new version here.  If the next release of the Azure Functions Runtime causes you some problems, then you can place value of 3.0.15417 into the FUNCTIONS_EXTENSION_VERSION application setting which will get to back to a version you know that works.  This is only a temporary solution, because at some point that version can be removed.  That’s pinning, but this isn’t really the purpose of this post.

The purpose is, using the table shown above, to explain how to move from a FUNCTIONS_EXTENSION_VERSION value of ~2.0 to ~2 which means you would be targeting a new version of .NET Core?  There are numerous complexities with this because it has to do with whether or not the code you wrote works the same when you target .NET Core 2.1 and .NET Core 3.1.  You can read about some Breaking changes in .NET Core 3.1 to see if your application has any features which will not work when you attempt to compile against this new version of the library.  Also, by upgrading to these updated versions of the runtime and therefore updated library, you also get new features see here What’s new in .NET Core 3.1.  Finally, when you upgrade to a new version of .NET, you get new language enhancements as well C# 8.0 new features are available in .NET Core 3.x, they are not available with .NET Core 2.x and the C# 9.0 features are only available in with .NET 5.0.  Here are two links which talk about .NET 5.0 Azure Functions.
NOTE: When FUNCTIONS_WORKER_RUNTIME = dotnet-isolated and FUNCTIONS_EXTENSION_VERSION = ~3, then you are targeting .NET 5.

Let’s get down to business now and show you how to do this.  Once you have performed a review of your code and are confident you are not impacted by any known breaking changes, open your Azure Function project in Visual Studio which supports the .NET version you want to target.  I wrote a few posts about “How to install .NET 5” and “How to install ASP.NET Core 2.1” and in both cases what got me was that I needed to install an updated version of Visual Studio which supports that specific .NET version.  Therefore, in order to update from .NET Core 2.x to .NET Core 3.1 you must be running at least Visual Studio 2019 version 16.4 or later and for .NET 5.0 you need at least version 16.7.3.

Once you get your project opened in the IDE, you will likely see something like this, Figure 1.


Figure 1, How to migrate an Azure Function to a new runtime version

If you navigate to the project Properties, you will notice that the selected Target framework is .NET Core 2.1.  Also, expand the Dependencies folder and you will see that the Microsoft.NETCore.App assembly is (2.1.0).  Figure 2 shows what a Timer Triggered Azure Function template looks like when created targeting .NET Core 2.1.0.


Figure 2, How to migrate an Azure Function to a new runtime version

To target this Azure Function to a new version of .NET so that I can change the FUNCTIONS_EXTENSION_VERSION from 2.0 to either ~2 or ~3, I need to select .NET Core 3.1 from the Target framework list, Figure 3.


Figure 3, How to migrate an Azure Function to a new runtime version

When you make the change, you will notice that the IDE downloads some new Dependencies and adds a Frameworks subdirectory that now contains the Microsoft.NETCore.Apps assembly.  Those are shown in Figure 4.


Figure 4, How to migrate an Azure Function to a new runtime version

Here is where the fun starts, try to compile it.  When I compiled my simple example I received the following exceptions.  The first one is the infamous:

System.IO.FileNotFoundException: Could not load file or assembly ‘System.Runtime, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’. The system cannot find the file specified.

The second was related to the first, both are shown in Figure 5.

Metadata generation failed. :\Users\benperk\.nuget\packages\microsoft.net.sdk.functions\1.0.28\build\netstandard1.0\Microsoft.NET.Sdk.Functions.Build.targets


Figure 5, How to migrate an Azure Function to a new runtime version

The file from which the exception was coming from lead me to the solution which was to update the Microsoft.NET.Sdk.Functions SDK.  If you look in Figure 5 and Figure 1, you notice the version did not change when I selected the new Target framework.  As you can see in Figure 6, the IDE did call out that I needed or that an update was recommended.


Figure 6, How to migrate an Azure Function to a new runtime version

Once installed, I was able to successfully compile my Azure Function against .NET Core 3.1 which allowed me to then change my FUNCTIONS_EXTENSION_VERSION application setting from ~2.0 to ~3.

Simple Example

I recognize this example is a simple one.  If your code depends on a specific version of .NET, then you will need to install a version of that dependency and all the assemblies which that depends on as well.  It is complicated, but the complexity is highly dependent on the complexity of your application and its dependencies.

This post is intended to help you make the jump, because as I read here is that the end of life for .NET Core 2.1 is approaching rapidly.  You will need to make take this jump in the near term to continue getting uninhibited support.

You might also find that you may need to upgrade your Function Trigger extensions in order to target new frameworks.

You need to make that jump, then work through the exceptions one by one.  There are some migration tools which can help you find the pain points, but as some point you must take the leap, install the new Visual Studio, update the libraries, compile and simply work it out.

.NET 5.0

My initial review of Azure Functions which target .NET 5.0 carries some new concepts which I am learning how to manage, for example isolated.  Also, it seems that the instructions are targeting the Azure CLI or Visual Studio Code for this.  Keep an eye on this area, and make sure that you take backups of your source, dependencies and projects before you start any migrations.  You should work towards getting on the .NET 5 migration path as soon as possible.  When running in the cloud, you need, or your IT solutions need to move along with it, and the cloud moves fast.

Tags: How to migrate an Azure Function to a new runtime?