A New Era of Azure Functions Development
Tatsuro Shibamura
Posted on September 17, 2020
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.
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
}
}));
}
}
}
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();
}
}
}
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");
}
}
There are also new extension points to load the configuration from JSON, User Secrets, etc.
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; }
}
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());
}
}
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
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.
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.
- ARM Template
- Bicep (ARM DSL)
- Terraform (Highly-recommended)
- Pulumi
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!
Posted on September 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.