Migrate apps from Azure Functions version 1.x to version 4.x

Important

Java isn't supported by version 1.x of the Azure Functions runtime. Perhaps you're instead looking to migrate your Java app from version 3.x to version 4.x. If you're migrating a version 1.x function app, select either C# or JavaScript above.

Important

TypeScript isn't supported by version 1.x of the Azure Functions runtime. Perhaps you're instead looking to migrate your TypeScript app from version 3.x to version 4.x. If you're migrating a version 1.x function app, select either C# or JavaScript above.

Important

PowerShell isn't supported by version 1.x of the Azure Functions runtime. Perhaps you're instead looking to migrate your PowerShell app from version 3.x to version 4.x. If you're migrating a version 1.x function app, select either C# or JavaScript above.

Important

Python isn't supported by version 1.x of the Azure Functions runtime. Perhaps you're instead looking to migrate your Python app from version 3.x to version 4.x. If you're migrating a version 1.x function app, select either C# or JavaScript above.

If you're running on version 1.x of the Azure Functions runtime, it's likely because your C# app requires .NET Framework 2.1. Version 4.x of the runtime now lets you run .NET Framework 4.8 apps. At this point, you should consider migrating your version 1.x function apps to run on version 4.x. For more information about Functions runtime versions, see Azure Functions runtime versions overview.

Migrating a C# function app from version 1.x to version 4.x of the Functions runtime requires you to make changes to your project code. Many of these changes are a result of changes in the C# language and .NET APIs. JavaScript apps generally don't require code changes to migrate.

You can upgrade your C# project to one of the following versions of .NET, all of which can run on Functions version 4.x:

.NET version Process model*
.NET 7 Isolated worker process
.NET 6 Isolated worker process
.NET 6 In-process
.NET Framework 4.8 Isolated worker process

* In-process execution is only supported for Long Term Support (LTS) releases of .NET. Non-LTS releases and .NET Framework require you to run in an isolated worker process. For a feature and functionality comparison between the two process models, see Differences between in-process and isolate worker process .NET Azure Functions.

This article walks you through the process of safely migrating your function app to run on version 4.x of the Functions runtime.

Prepare for migration

Before you upgrade your app to version 4.x of the Functions runtime, you should do the following tasks:

  • Review Update your project files and decide which version of .NET you want to migrate to. Complete the steps to migrate your local project to your chosen version of .NET.
  • Complete the steps in update your project files to migrate your local project to run locally on a version 4.x and a supported version of Node.js.
  • After migrating your local project, fully test the app locally using version 4.x of the Azure Functions Core Tools.

  • Upgrade your function app in Azure to the new version. If you need to minimize downtime, consider using a staging slot to test and verify your migrated app in Azure on the new runtime version. You can then deploy your app with the updated version settings to the production slot. For more information, see Migrate using slots.

  • Republished your migrated project to the upgraded function app. When you use Visual Studio to publish a version 4.x project to an existing function app at a lower version, you're prompted to let Visual Studio upgrade the function app to version 4.x during deployment. This upgrade uses the same process defined in Migrate without slots.
  • Republished your migrated project to the upgraded function app.
  • Consider using a staging slot to test and verify your app in Azure on the new runtime version. You can then deploy your app with the updated version settings to the production slot. For more information, see Migrate using slots.

Update your project files

The following sections describes the updates you must make to your C# project files to be able to run on one of the supported versions of .NET in Functions version 4.x. The updates shown are ones common to most projects. Your project code may require updates not mentioned in this article, especially when using custom NuGet packages.

Choose the tab that matches your target version of .NET and the desired process model (in-process or isolated worker process).

.csproj file

The following example is a .csproj project file that runs on version 1.x:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
    <AzureFunctionsVersion>v1</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="Microsoft.CSharp" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

Use one of the following procedures to update this XML file to run in Functions version 4.x:

The following changes are required in the .csproj XML project file:

  1. Change the value of PropertyGroup.AzureFunctionsVersion to v4.

  2. Add the following OutputType element to the PropertyGroup:

    <OutputType>Exe</OutputType>
    
  3. Replace the existing ItemGroup.PackageReference with the following ItemGroup:

    <ItemGroup>
      <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.10.0" />
      <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" />
    </ItemGroup>
    
  4. Add the following new ItemGroup:

    <ItemGroup>
      <Folder Include="Properties\" />
    </ItemGroup>
    

After you make these changes, your updated project should look like the following example:


<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <RootNamespace>My.Namespace</RootNamespace>
    <OutputType>Exe</OutputType>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.8.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>
</Project>

program.cs file

In most cases, migrating requires you to add the following program.cs file to your project:

using Microsoft.Extensions.Hosting;
using Microsoft.Azure.Functions.Worker;

namespace Company.FunctionApp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            FunctionsDebugger.Enable();

            var host = new HostBuilder()
                .ConfigureFunctionsWorkerDefaults()
                .Build();

            host.Run();
        }
    }
}

host.json file

Settings in the host.json file apply at the function app level, both locally and in Azure. In version 1.x, your host.json file is either empty or it contains some settings that apply to all functions in the function app. For more information, see Host.json v1. If your host.json file has setting values, review the host.json v2 format for any changes.

To run on version 4.x, you must add "version": "2.0" to the host.json file. You should also consider adding logging to your configuration, as in the following examples:

{
    "version": "2.0",
    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            }
        }
    }
}

local.settings.json file

The local.settings.json file is only used when running locally. For information, see Local settings file. In version 1.x, the local.settings.json file has only two required values:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "AzureWebJobsStorageConnectionStringValue",
        "AzureWebJobsDashboard": "AzureWebJobsStorageConnectionStringValue"
    }
}

When you upgrade to version 4.x, make sure that your local.settings.json file has at least the following elements:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "AzureWebJobsStorageConnectionStringValue",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
    }
}

Namespace changes

C# functions that run in an isolated worker process uses libraries in a different namespace than those libraries used in version 1.x. In-process functions use libraries in the same namespace.

Version 1.x and in-process libraries are generally in the namespace Microsoft.Azure.WebJobs.*. Isolated worker process function apps use libraries in the namespace Microsoft.Azure.Functions.Worker.*. You can see the effect of these namespace changes on using statements in the HTTP trigger template examples that follow.

Class name changes

Some key classes changed names between version 1.x and version 4.x. These changes are a result either of changes in .NET APIs or in differences between in-process and isolated worker process. The following table indicates these key .NET classes used by Azure Functions that changed after version 1.x:

Version 1.x .NET Framework 4.8
FunctionName (attribute) Function (attribute)
TraceWriter ILogger
HttpRequestMessage HttpRequestData
HttpResonseMessage HttpResonseData

There might also be class name differences in bindings. For more information, see the reference articles for the specific bindings.

HTTP trigger template

Most of the code changes between version 1.x and version 4.x can be seen in HTTP triggered functions. The HTTP trigger template for version 1.x looks like the following example:

using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace Company.Function
{
    public static class HttpTriggerCSharp
    {
        [FunctionName("HttpTriggerCSharp")]
        public static async Task<HttpResponseMessage> 
            Run([HttpTrigger(AuthorizationLevel.AuthLevelValue, "get", "post", 
            Route = null)]HttpRequestMessage req, TraceWriter log)
        {
            log.Info("C# HTTP trigger function processed a request.");

            // parse query parameter
            string name = req.GetQueryNameValuePairs()
                .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
                .Value;

            if (name == null)
            {
                // Get request body
                dynamic data = await req.Content.ReadAsAsync<object>();
                name = data?.name;
            }

            return name == null
                ? req.CreateResponse(HttpStatusCode.BadRequest, 
                    "Please pass a name on the query string or in the request body")
                : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
        }
    }
}

In version 4.x, the HTTP trigger template looks like the following example:

using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;

namespace Company.Function
{
    public class HttpTriggerCSharp
    {
        private readonly ILogger _logger;

        public HttpTriggerCSharp(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<HttpTriggerCSharp>();
        }

        [Function("HttpTriggerCSharp")]
        public HttpResponseData Run([HttpTrigger(AuthorizationLevel.AuthLevelValue, "get", "post")] HttpRequestData req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");

            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Headers.Add("Content-Type", "text/plain; charset=utf-8");

            response.WriteString("Welcome to Azure Functions!");

            return response;
        }
    }
}

Update your project files

To update your project to Azure Functions 4.x:

  1. Update your local installation of Azure Functions Core Tools to version 4.x.

  2. Move to one of the Node.js versions supported on version 4.x.

  3. Add both version and extensionBundle elements to the host.json, so that it looks like the following example:

    {
        "version": "2.0",
        "extensionBundle": {
            "id": "Microsoft.Azure.Functions.ExtensionBundle",
            "version": "[3.3.0, 4.0.0)"
        }
    }
    

    The extensionBundle element is required because after version 1.x, bindings are maintained as external packages. For more information, see Extension bundles.

  4. Update your local.settings.json file so that it has at least the following elements:

    {
        "IsEncrypted": false,
        "Values": {
            "AzureWebJobsStorage": "UseDevelopmentStorage=true",
            "FUNCTIONS_WORKER_RUNTIME": "node"
        }
    }
    

    The AzureWebJobsStorage setting can be either the Azurite storage emulator or an actual Azure storage account. For more information, see Local storage emulator.

Upgrade your function app in Azure

You need to upgrade the runtime of the function app host in Azure to version 4.x before you publish your migrated project. The runtime version used by the Functions host is controlled by the FUNCTIONS_EXTENSION_VERSION application setting, but in some cases other settings must also be updated. Both code changes and changes to application settings require your function app to restart.

The easiest way is to upgrade without slots and then republish your app project. You can also minimize the downtime in your app and simplify rollback by upgrading using slots.

Upgrade without slots

The simplest way to upgrade to v4.x is to set the FUNCTIONS_EXTENSION_VERSION application setting to ~4 on your function app in Azure. You must follow a different procedure on a site with slots.

az functionapp config appsettings set --settings FUNCTIONS_EXTENSION_VERSION=~4 -g <RESOURCE_GROUP_NAME> -n <APP_NAME>

During upgrade, you must also set another setting, which differs between Windows and Linux.

When running on Windows, you also need to enable .NET 6.0, which is required by version 4.x of the runtime.

az functionapp config set --net-framework-version v6.0 -g <RESOURCE_GROUP_NAME> -n <APP_NAME>

.NET 6 is required for function apps in any language running on Windows.

In this example, replace <APP_NAME> with the name of your function app and <RESOURCE_GROUP_NAME> with the name of the resource group.

You can now republish your app project that has been migrated to run on version 4.x.

Upgrade using slots

Using deployment slots is a good way to upgrade your function app to the v4.x runtime from a previous version. By using a staging slot, you can run your app on the new runtime version in the staging slot and switch to production after verification. Slots also provide a way to minimize downtime during upgrade. If you need to minimize downtime, follow the steps in Minimum downtime upgrade.

After you've verified your app in the upgraded slot, you can swap the app and new version settings into production. This swap requires setting WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 in the production slot. How you add this setting affects the amount of downtime required for the upgrade.

Standard upgrade

If your slot-enabled function app can handle the downtime of a full restart, you can update the WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS setting directly in the production slot. Because changing this setting directly in the production slot causes a restart that impacts availability, consider doing this change at a time of reduced traffic. You can then swap in the upgraded version from the staging slot.

The Update-AzFunctionAppSetting PowerShell cmdlet doesn't currently support slots. You must use Azure CLI or the Azure portal.

  1. Use the following command to set WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 in the production slot:

    az functionapp config appsettings set --settings WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0  -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> 
    

    In this example, replace <APP_NAME> with the name of your function app and <RESOURCE_GROUP_NAME> with the name of the resource group. This command causes the app running in the production slot to restart.

  2. Use the following command to also set WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS in the staging slot:

    az functionapp config appsettings set --settings WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME>
    
  3. Use the following command to change FUNCTIONS_EXTENSION_VERSION and upgrade the staging slot to the new runtime version:

    az functionapp config appsettings set --settings FUNCTIONS_EXTENSION_VERSION=~4 -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME>
    
  4. Version 4.x of the Functions runtime requires .NET 6 in Windows. On Linux, .NET apps must also upgrade to .NET 6. Use the following command so that the runtime can run on .NET 6:

    When running on Windows, you also need to enable .NET 6.0, which is required by version 4.x of the runtime.

    az functionapp config set --net-framework-version v6.0 -g <RESOURCE_GROUP_NAME> -n <APP_NAME>
    

    .NET 6 is required for function apps in any language running on Windows.

    In this example, replace <APP_NAME> with the name of your function app and <RESOURCE_GROUP_NAME> with the name of the resource group.

  5. If your code project required any updates to run on version 4.x, deploy those updates to the staging slot now.

  6. Confirm that your function app runs correctly in the upgraded staging environment before swapping.

  7. Use the following command to swap the upgraded staging slot to production:

    az functionapp deployment slot swap -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME> --target-slot production
    

Minimum downtime upgrade

To minimize the downtime in your production app, you can swap the WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS setting from the staging slot into production. After that, you can swap in the upgraded version from a prewarmed staging slot.

  1. Use the following command to set WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 in the staging slot:

    az functionapp config appsettings set --settings WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME>
    
  2. Use the following commands to swap the slot with the new setting into production, and at the same time restore the version setting in the staging slot.

    az functionapp deployment slot swap -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME> --target-slot production
    az functionapp config appsettings set --settings FUNCTIONS_EXTENSION_VERSION=~3 -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME>
    

    You may see errors from the staging slot during the time between the swap and the runtime version being restored on staging. This error can happen because having WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 only in staging during a swap removes the FUNCTIONS_EXTENSION_VERSION setting in staging. Without the version setting, your slot is in a bad state. Updating the version in the staging slot right after the swap should put the slot back into a good state, and you call roll back your changes if needed. However, any rollback of the swap also requires you to directly remove WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 from production before the swap back to prevent the same errors in production seen in staging. This change in the production setting would then cause a restart.

  3. Use the following command to again set WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 in the staging slot:

    az functionapp config appsettings set --settings WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME>
    

    At this point, both slots have WEBSITE_OVERRIDE_STICKY_EXTENSION_VERSIONS=0 set.

  4. Use the following command to change FUNCTIONS_EXTENSION_VERSION and upgrade the staging slot to the new runtime version:

    az functionapp config appsettings set --settings FUNCTIONS_EXTENSION_VERSION=~4 -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME>
    
  5. Version 4.x of the Functions runtime requires .NET 6 in Windows. On Linux, .NET apps must also upgrade to .NET 6. Use the following command so that the runtime can run on .NET 6:

    When running on Windows, you also need to enable .NET 6.0, which is required by version 4.x of the runtime.

    az functionapp config set --net-framework-version v6.0 -g <RESOURCE_GROUP_NAME> -n <APP_NAME>
    

    .NET 6 is required for function apps in any language running on Windows.

    In this example, replace <APP_NAME> with the name of your function app and <RESOURCE_GROUP_NAME> with the name of the resource group.

  6. If your code project required any updates to run on version 4.x, deploy those updates to the staging slot now.

  7. Confirm that your function app runs correctly in the upgraded staging environment before swapping.

  8. Use the following command to swap the upgraded and prewarmed staging slot to production:

    az functionapp deployment slot swap -g <RESOURCE_GROUP_NAME>  -n <APP_NAME> --slot <SLOT_NAME> --target-slot production
    

Behavior changes after version 1.x

This section details changes made after version 1.x in both trigger and binding behaviors as well as in core Functions features and behaviors.

Changes in triggers and bindings

Starting with version 2.x, you must install the extensions for specific triggers and bindings used by the functions in your app. The only exception for this HTTP and timer triggers, which don't require an extension. For more information, see Register and install binding extensions.

There are also a few changes in the function.json or attributes of the function between versions. For example, the Event Hubs path property is now eventHubName. See the existing binding table for links to documentation for each binding.

Changes in features and functionality

A few features were removed, updated, or replaced after version 1.x. This section details the changes you see in later versions after having used version 1.x.

In version 2.x, the following changes were made:

  • Keys for calling HTTP endpoints are always stored encrypted in Azure Blob storage. In version 1.x, keys were stored in Azure Files by default. When you upgrade an app from version 1.x to version 2.x, existing secrets that are in Azure Files are reset.

  • The version 2.x runtime doesn't include built-in support for webhook providers. This change was made to improve performance. You can still use HTTP triggers as endpoints for webhooks.

  • To improve monitoring, the WebJobs dashboard in the portal, which used the AzureWebJobsDashboard setting is replaced with Azure Application Insights, which uses the APPINSIGHTS_INSTRUMENTATIONKEY setting. For more information, see Monitor Azure Functions.

  • All functions in a function app must share the same language. When you create a function app, you must choose a runtime stack for the app. The runtime stack is specified by the FUNCTIONS_WORKER_RUNTIME value in application settings. This requirement was added to improve footprint and startup time. When developing locally, you must also include this setting in the local.settings.json file.

  • The default timeout for functions in an App Service plan is changed to 30 minutes. You can manually change the timeout back to unlimited by using the functionTimeout setting in host.json.

  • HTTP concurrency throttles are implemented by default for Consumption plan functions, with a default of 100 concurrent requests per instance. You can change this behavior in the maxConcurrentRequests setting in the host.json file.

  • Because of .NET Core limitations, support for F# script (.fsx files) functions has been removed. Compiled F# functions (.fs) are still supported.

  • The URL format of Event Grid trigger webhooks has been changed to follow this pattern: https://{app}/runtime/webhooks/{triggerName}.

Next steps