Azure Functions As A Reusable Package

In this article, I will discuss about how to create reusable Azure Functions. Sometime when Function App is being adopted as a Service in Microservices architecture ( more on this some other time ), if there are many services and there is a requirement to create some common functions or cross cutting concern related function that can be used across multiple services then this will be helpful.

Scenario

Let's assume that we want to create basic Azure Functions that will provide ok response when function app is up and otherwise not. We can say that it is health related function. Also one more function in which it will return environment variable like AppVersion which help us to identify current deployed version of function app.

Tools

  • Visual Studio 2022
  • .net 6
  • Azure Functions V4 ( dotnet isolated mode)

Without Reusable Functions

  • AppVersion Provider Interface
public interface IAppVersionProvider
{
        string GetVersion();
}
  • Implementation of AppVersion Provider.
public class EnvironmentAppVersionProvider : IAppVersionProvider
    {
        public string GetVersion()
        {
            return Environment.GetEnvironmentVariable("AppVersion") ?? "Not Available";
        }
    }
  • Host looks like following.
var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddScoped<IAppVersionProvider,EnvironmentAppVersionProvider>();
    })
    .Build();

host.Run();
  • Health functions.
public class AppHealthFunctions
    {
        private readonly IAppVersionProvider appVersionProvider;
        public AppHealthFunctions(IAppVersionProvider appVersionProvider)
        {            
            this.appVersionProvider = appVersionProvider;
        }

        [Function("health")]
        public async Task<HttpResponseData> GetFunctionHealth(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "service/health")] HttpRequestData req)
        {
            var response = req.CreateResponse(HttpStatusCode.OK);            
            await response.WriteAsJsonAsync(new 
            { 
                Status = "running", 
                Machinename = Environment.MachineName,
                Version = this.appVersionProvider.GetVersion()
            });
            return response;
        }        
    }
  • Structure looks like following.

image.png

  • Here concern is that, if we have too many Function apps then each function app we might have repeat above function.

Is there any better solution for this ?

Yes. We can create package and import that package in every function app.

With Package

  • Here implementation is almost same as above but structure is different. We separate out HealthFunctions and related things into separate class library. ( Later it can publish as nuget package). Structure is bit clear now.

image.png

  • Publish the HealthAzureFunctions as nuget package. For this go to the directory where HealthAzureFunctions.csproj presents and execute following command.
dotnet pack
  • This will generate nuget package file in bid/debug ( As per configuration). Go to that directory and follow below step.

  • Publish it to source. Following is just for local.

nuget add HealthAzureFunctions.1.0.0.nupkg -Source C:\Packages
  • Add that source into Visual Studio Package Source.

image.png

  • Import into Azure Function Apps

image.png

  • It looks something like this.

image.png

  • Host configuration looks like following.
using HealthAzureFunctions;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services => services.AddHealthFunctionExtensions())
    .Build();

host.Run();
  • Now run. It will also display function endpoint from package as well.

image.png

Source Code

Both with and without package code available for download.

Summary

Reusable Azure Functions is specially useful when you have too many function apps and some of the function common across function app. By this way you can share common implementation.