Azure Functions in .NET 7: The Mandatory Isolated Process

Published by Antoine BEAUFRERE
Category : .Net / Azure / Azure Functions
23/06/2023

Azure Functions is a major component of the Serverless resources provided by Microsoft.

First seen in early 2022, the new .NET 7 framework brings some changes for Azure’s cloud. And notably a not-so-visible but significant change for Azure Functions appears: the Isolated Process will be the only possible hosting model.

 

Azure Functions

As Microsoft’s documentation aptly puts it:

Firstly, Azure Functions allow you to implement your system’s logic in readily available code blocks, which are called “functions”. Different functions can run whenever you need to respond to critical events.

Secondly, as requests increase, Azure Functions respond to the demand with as many resources and function instances as necessary, but only when needed. When demands decrease, additional resources and application instances automatically reduce.

The In Process vs Isolated Process

 

In Process and Isolated Process

 

In Process and Isolated Process are two different hosting models for Azure Functions. The first runs the Azure Functions runtime in the same process as the web application, API, or platform that hosts it. This can improve the performance and efficiency of functions but also means that any function-related issue can affect the overall hosting process.

Isolated Process hosting, on the other hand, runs the Azure Functions runtime in a separate process from the web application or hosting platform. This provides a higher degree of isolation and can prevent function-related issues from affecting the hosting process. However, this could also lead to a decrease in performance and efficiency compared to in-process hosting.

In .NET 6, the choice between In Process hosting and Isolated Process hosting depends on the specific requirements and needs of your Azure Functions and the hosting environment. However, starting from .NET 7, the choice has been made for you.

 

Code Changes

 

The csproj

To start, the .csproj is slightly different:

 

Comparison of the .csproj for an Azure Function project

 

The important things to remember are:

  • the <OutputType> set to Exe
  • the dependency changes (focus on that later).

Settings

A quick look at the local.settings.json file (in the app settings on Azure) where we specify that the function is indeed in dotnet isolated:

 

Comparison of local configurations for Azure Function

 

This will therefore imply the same change in the App Settings of the Function App.

 

The startup / program

 

Then we see a change in the function implementation. Dependency injection that is done in a Startup.cs file created in In Process is now done in the Program.cs file:

In Process (Startup.cs):

using Microsoft.Azure.Functions.Extensions.DependencyInjection; 
using Microsoft.Extensions.DependencyInjection; 

[assembly: FunctionsStartup(typeof(FAProjet.Startup))] 

namespace FAProjet 
{ 
    public class Startup : FunctionsStartup 
    { 
        public override void Configure(IFunctionsHostBuilder builder) 
        { 
            builder.Services.AddLogging(); 
            builder.Services.AddHttpClient(); 
            builder.Services.AddSingleton<IApiClientService, ApiClientService>(); 
        } 
    } 
}

 

Isolated Process (Program.cs):

using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection; 
using Microsoft.Extensions.Hosting; 

var host = new HostBuilder() 
    .ConfigureServices(services => 
    { 
        services 
            .AddLogging() 
            .AddHttpClient() 
            .AddSingleton<IApiClientService, ApiClientService>(); 
    }) 
    .ConfigureFunctionsWorkerDefaults() 
    .Build(); 

host.Run();

 

The Functions

Finally, we get to the heart of the matter with the changes in the functions themselves, which boil down to two things:

  • the [FunctionName(“…”)] attribute becomes (drum roll) simply [Function(“”)]
  • the triggers evolve in their uses

In Process:

using Microsoft.AspNetCore.Http; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.Azure.WebJobs; 
using Microsoft.Azure.WebJobs.Extensions.Http; 
using Microsoft.Extensions.Logging; 
using Newtonsoft.Json; 

namespace FunctionAppNet6 
{ 
    public static class Function1 
    { 
        [FunctionName("Function1")] 
        public static async Task<IActionResult> Run( 
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, 
            ILogger log) 
        { 
            log.LogInformation("C# HTTP trigger function processed a request."); 

            string name = req.Query["name"]; 

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 
            dynamic data = JsonConvert.DeserializeObject(requestBody); 
            name = name ?? data?.name; 

            string responseMessage = string.IsNullOrEmpty(name) 
                ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response." 
                : $"Hello, {name}. This HTTP triggered function executed successfully."; return new OkObjectResult(responseMessage); 
        } 
    } 
}

 

Isolated Process:

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

namespace FunctionAppNet7 
{ 
    public class Function1 
    { 
        [Function("Function1")] 
        public HttpResponseData Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req, 
            ILogger logger) 
        { 
            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; 
        } 
    } 
}

Regarding the changes at the trigger level, we have an HTTP trigger here that no longer provides an HttpRequest but instead an HttpRequestData. As a result, returning an OK status is no longer done in the same way (cf. above).

 

Dependencies

 

In Process and Isolated Process functions in Azure Functions have different dependencies because they run in different runtime environments.

In Process functions run in the same process as the host. Therefore, they have access to the host’s dependencies and any additional libraries or packages that are installed in the host’s environment. Isolated functions, on the other hand, run in their own separate process and therefore have their own dependencies and environment.

To ensure that the new model has all the necessary dependencies, you must include them in the function package or deploy them in the bin directory of the function application. You can do this using a deployment zip file or by using a continuous deployment method such as Azure DevOps or GitHub Actions.

 

Advantages of using the Isolated Process

 

The first advantage of switching to Isolated Process is improved stability and availability of functions. By running the functions in a separate process, errors will not be able to affect the web application and vice versa. This means that the functions will remain online even if the web application experiences problems. This will improve the application’s availability.

Secondly, this allows better resource isolation. Each function will have its own memory and CPU resource allocation, which will prevent conflicts between functions and ensure better resource usage. This will also better manage peak loads by allowing the simultaneous execution of multiple functions.

The third advantage is improved security. It will be more difficult for attackers to access the data and resources of the web application. This will increase the security of the application and protect sensitive user data.

In conclusion, the transition from Azure Functions in process to isolated process will bring several benefits for developers, including improved stability and availability, better resource isolation, and improved security. If you are using Azure Functions in your application, you should consider switching to isolated process mode to fully benefit from it.