DEV Community

Tatsuro Shibamura
Tatsuro Shibamura

Posted on • Edited on

A New Era of Azure Functions Development

There were many exciting announcements at August's Azure Functions Live, which was streamed on YouTube.

There are additional features that will change the future of Azure Functions development. This is truly a new era.

You can't just benefit from a template project generated by Visual Studio. Take this opportunity to learn about the new era of Azure Functions development.

A sample project that uses the features presented here is available. I hope it will help you.

GitHub logo shibayan / azure-functions-boilerplate

A boilerplate project for getting started with Azure Functions v4

Dependency Injection is everything

The days of implementing Azure Functions with static classes and static methods are over.

Dependency Injection will become the norm for dependency resolution, instance lifecycle management, and testability.

Advanced Dependency Injection extensions are provided by the package Microsoft.Azure.Functions.Extensions.

https://www.nuget.org/packages/Microsoft.Azure.Functions.Extensions/

Many of the Azure SDKs recommend handling client instances as a singleton. Other than that, it is well known that we need to handle the HttpClient as a singleton.

With Dependency Injection, you can easily implement instance lifecycle management.

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

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

namespace FunctionApp1
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            var context = builder.GetContext();

            // Initialize IHttpClientFactory
            builder.Services.AddHttpClient();

            // Initialize CosmosClient
            builder.Services.AddSingleton(provider =>
                new CosmosClient(context.Configuration.GetConnectionString("CosmosConnection"), new CosmosClientOptions
                {
                    SerializerOptions = new CosmosSerializationOptions
                    {
                        PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
                    }
                }));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The Function implementation uses instance methods to perform constructor injection.

If you've ever used ASP.NET Core, this code will be familiar to you.

using System.Net.Http;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;

namespace FunctionApp1
{
    public class Function1
    {
        // Injected by Function Runtime.
        public Function1(IHttpClientFactory httpClientFactory, CosmosClient cosmosClient)
        {
            // Create new HttpClient
            _httpClient = httpClientFactory.CreateClient();
            _cosmosClient = cosmosClient;
        }

        private readonly HttpClient _httpClient;
        private readonly CosmosClient _cosmosClient;

        [FunctionName("Function1")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
            ILogger log)
        {
            // Use HttpClient
            var httpResponse = await _httpClient.GetAsync("...");

            // Use singleton CosmosClient
            var container = _cosmosClient.GetContainer("Todo", "Items");

            var cosmosResponse = await container.ReadItemStreamAsync("...", new PartitionKey("..."));

            return new OkResult();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Optimal lifecycle management could be achieved with simple code.

With PaaS, you need to be aware of issues such as SNAT port exhaustion, which can be mitigated by lifecycle management with Dependency Injection.

Integrated configuration

What I thought was the biggest weakness of Azure Functions is the difficulty in referencing the configuration from the application code.

Previously, you had to use Environment.GetEnvironmentVariable to load the connection string and so on. Yes, we had to read everything from environment variables.

This annoying problem has finally been solved.

The package I used for DI now provides an official way to refer to the configuration.

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // Get FunctionHostBuilderContext. it's very useful!
        var context = builder.GetContext();

        // Get config section
        var section = context.Configuration.GetSection("Sample");

        // Get storage connection string
        var connectionString = context.Configuration.GetConnectionString("Storage");
    }
}
Enter fullscreen mode Exit fullscreen mode

There are also new extension points to load the configuration from JSON, User Secrets, etc.

https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources

Seamless options pattern

Previous versions of Azure Functions also made use of the Options pattern commonly used in ASP.NET Core.

However, it requires a lot of code and is more complex than ASP.NET Core, but now it is simple to use.

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var context = builder.GetContext();

        builder.Services.Configure<SampleOptions>(context.Configuration.GetSection("Sample"));
    }
}

public class SampleOptions
{
    public string Value { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Now that the pre-configured IConfiguration is provided, you can use the Options pattern with clean code.

Of course, the option is type-safe.

Secrets from vault

There are other ways to load the secret other than from a file or from App Settings in Azure Functions.

You can also use the Key Vault to securely load them.

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // Secrets loaded!
    }

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        var builtConfig = builder.ConfigurationBuilder.Build();

        var tokenProvider = new AzureServiceTokenProvider();
        var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(tokenProvider.KeyVaultTokenCallback));

        builder.ConfigurationBuilder.AddAzureKeyVault(builtConfig["KeyVaultEndpoint"], keyVaultClient, new DefaultKeyVaultSecretManager());
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Vault Reference feature is simple to use, but the connection to the Key Vault cannot be protected by Service Endpoint / Private Endpoint.

Are you ready to run?

Great AOT compilation features introduced in .NET Core 3.0 are now available in Azure Functions.

The assembly contains native code, which will improve cold starts.

https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library#readytorun

I tried it and it worked for win-x64, but I couldn't build it for win-x86.

dotnet publish -c Release -o ./publish -r win-x64 -p:PublishReadyToRun=true
Enter fullscreen mode Exit fullscreen mode

It will be especially effective in combination with the Consumption Plan.

VNET Integration is the best

It is not necessary to limit the source of connections in SQL Database or Cosmos DB by using the App Service's Outbound IP.

Regional VNET Integration can be enabled to limit on a per-subnet basis. This is my favorite feature.

https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet

For those with higher security requirements, Private Link can be used to provide a private connection to Azure PaaS.

A great example is available on GitHub.

GitHub logo Azure-Samples / Azure-Functions-Private-Endpoints

Sample showing how to use Azure Functions with private endpoints for triggers and output bindings.

This update allows you to protect your Azure Functions applications and resources with VNET and Private Link. Awesome!

Combined Regional VNET Integration and Private Link architectures will become more common in the future.

Security, Security, Security

Security is more important than anything else. But security is difficult.

Don't worry, Azure and Azure Functions have all the features you need for security.

https://docs.microsoft.com/en-us/azure/azure-functions/security-baseline

If you follow Azure Functions best practices, you can make your applications secure.

Eventually the IaC will be needed

Azure Functions and the underlying App Service have as many configuration items as there are features. I think this is the fate of PaaS.

Adoption of Infrastructure as a Code will be essential for the reproducibility and consistency of the infrastructure configuration.

We've all experienced problems at one time or another due to improper configuration changes made without our knowledge.

Adoption of IaC can be expected to improve security through IAM configuration while preventing direct configuration changes through Azure Portal.

If a 4th Azure Portal rebuild occurs, it will not be affected if you have adopted IaC. 😄

Enjoy your Azure Serverless life!

Top comments (0)