AzureFunction.Isolated.HostConfigurator
1.0.3
dotnet add package AzureFunction.Isolated.HostConfigurator --version 1.0.3
NuGet\Install-Package AzureFunction.Isolated.HostConfigurator -Version 1.0.3
<PackageReference Include="AzureFunction.Isolated.HostConfigurator" Version="1.0.3" />
paket add AzureFunction.Isolated.HostConfigurator --version 1.0.3
#r "nuget: AzureFunction.Isolated.HostConfigurator, 1.0.3"
// Install AzureFunction.Isolated.HostConfigurator as a Cake Addin #addin nuget:?package=AzureFunction.Isolated.HostConfigurator&version=1.0.3 // Install AzureFunction.Isolated.HostConfigurator as a Cake Tool #tool nuget:?package=AzureFunction.Isolated.HostConfigurator&version=1.0.3
🪲 Problem
The new dotnet-isolated hosting model of Azure Functions doesn't allow you to configure the connection for triggers or binding the same way it does in the In-Process model. This is a problem when you want to use a connection string from Key Vault, for example.
The problem is that the host process needs the connection before it calls into your code (i.e. before your Program.cs
is called) so you only have a couple of ways to configure it:
- Use an environment variable
- Use a
local.settings.json
file
Both of these options may not be sufficient to cover your scenario so this package helps you work around this limitation that will hopefully be resolved soon on the Azure Functions side. See this issue and this issue for context.
⚒️ Workaround
Azure Functions using the dotnet-isolated hosting model will load the extensions that your project references and will initialize the extensions before trying to resolve the connection strings.
This step happens inside the AddScriptHost
method of the ScriptHostBuilderExtensions
when the host calls the HasExternalConfigurationStartups()
method. (the code I'm talking about here is in the azure-function-host project on GitHub here.
So this package essentially hooks into that functionality to allow you to customize the host configuration before it's used.
This package implements a WebJob extension that will delegate the configuration to a type that you have to implement and specify in your project using the assembly attribute [HostConfiguratorAttribute(typeof(TheTypeToBeInvoked))]
.
You need to specify a type that implements the interface IWebJobsConfigurationStartup
from the package Microsoft.Azure.WebJobs
in the attribute, this type will then be invoked by this extension allowing you to add additional configuration sources to the host configuration.
🚀 Usage
- Install the package
AzureFunction.Isolated.HostConfigurator
into your Azure Functions dotnet-isolated project or in a separate class library project that targets net6.0 if your function project targets net7.0.
dotnet add package AzureFunction.Isolated.HostConfigurator
- Create a class and implement the interface
IWebJobsConfigurationStartup
from the packageMicrosoft.Azure.WebJobs
in your project. This is where you will add your custom configuration sources.
internal class StartupExtension : IWebJobsConfigurationStartup
{
public void Configure(WebJobsBuilderContext context, IWebJobsConfigurationBuilder builder)
{
// Please note the usage of the context.ApplicationRootPath to get the path to the application root, where we can find our configuration files
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: false, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: false, reloadOnChange: false)
.AddEnvironmentVariables();
}
}
- Add the attribute
[HostConfiguratorAttribute(typeof(TheTypeToBeInvoked))]
to your project.
[assembly: HostConfiguratorAttribute(typeof(StartupExtension))]
- Set the
ConfiguratorAssemblyName
configuration with the name of the assembly that contains the[assembly: HostConfiguratorAttribute]
attribute. Such configuration can be added to either thehost.json
, theappsettings.json
or as an environment variable
host.json
example
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"ConfiguratorAssemblyName": "FunctionApp1.dll"
}
⚠️ Caveats
This package provides a workaround for an unsupported functionality loading an assembly that you specify into the function host process, this comes with a set of limitations and constraints as you can see below.
If you need to call
AddJsonFile('appsettings.json')
, make sure to callPath.Combine(context.ApplicationRootPath, "appsettings.json")
because the host will not be running from the same directory your code is running from.If you use a single assembly approach, you're forced to target
net6.0
and your assembly will be loaded twice, once in the host process and once by the Azure Functions host. If you have any static initialization code, it may potentially be called twice. To prevent this issue you should define a standalone assembly that contains the configuration code instead of adding the type in your functions code directly.If you need an assembly reference in your configuration class, you have to make sure it can be loaded in the host process (the function host) that targets .NET 6.0. Keep the code in the host configurator class as small as possible, and if your function needs to target net7.0, place your custom configuration class in a separate assembly and target net6.0.
Since
local.settings.json
is not published during deployment, I decided not to include that file in the assembly name lookup here. If you add theappsettings.json
file, make sure to set theCopy to Output Directory
property toCopy always
orCopy if newer
so that the file is copied to the output directory during the build process. Please note that you're also constrained about the version of the packages that you're using, for KeyVault and App Configuration, you can copy that from the relative samples.You may get a warning telling you that:
The Functions scale controller may not scale the following functions correctly because some configuration values were modified in an external startup class.
This is caused by a validation that checks if the configuration values have been modified by an external startup class, which is the case here since the original configuration has no means to read from the additional configuration sources we want to add. This warning can be safely ignored since it's the problem this library tries to solve. If you want to dig deeper, this behavior is implemented in the ExternalConfigurationStartupValidator in the azure-functions-host project.
Note that if you use managed identities with e.g. Azure Service Bus you won't get the warning because the configuration value is null but not the nested configuration called
fullyQualifiedNamespace
and the service only checks for top level ones.
- You can't set a breakpoint in the code loaded by the host configurator and if you do so, you will realize that the breakpoint won't be hit. If you need to debug the code, you can use the
Debugger.Launch()
method to attach a debugger to the process.
💡 Samples
Check the /samples folder for some sample projects.
This has been tested on Visual Studio 17.6.5 and via the func cli 4.0.5085 both of which can successfully run a project as well as deployed in Azure Function.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Microsoft.Azure.Functions.Worker (>= 1.18.0)
- Microsoft.Azure.WebJobs (>= 3.0.37)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.