Yes - the current version - that supports DI.
C#
In my asp.net core web apps I'm used to loading in appsettings.json and environment-specific appsettings.{environment}.json in on startup using the ConfigureAppConfiguration method on IWebHostBuilder. With the new FunctionsStartup & IFunctionsHostBuilder, how should one go about loading appsettings in, assuming they want to access the settings by having an IConfiguration injected into their Function using standard .net core DI functionality?
For example, what is the equivalent way of loading in appsettings.json now like I do here using IWebHostBuilder?
public static IWebHost BuildWebHost(string[] args)
{
var host = WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) =>
{
var tmpConfig = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var environmentName = tmpConfig["Environment"];
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>()
.Build();
return host;
}
FYI - here is what I ended up doing... I call this in my Startup.Configure() method:
static class IFunctionsHostBuilderConfigurationExtensions
{
public static IFunctionsHostBuilder AddAppSettingsToConfiguration(this IFunctionsHostBuilder builder)
{
var currentDirectory = "/home/site/wwwroot";
bool isLocal = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"));
if (isLocal)
{
currentDirectory = Environment.CurrentDirectory;
}
var tmpConfig = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json")
.Build();
var environmentName = tmpConfig["Environment"];
var configurationBuilder = new ConfigurationBuilder();
var descriptor = builder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
if (descriptor?.ImplementationInstance is IConfiguration configRoot)
{
configurationBuilder.AddConfiguration(configRoot);
}
var configuration = configurationBuilder.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
.AddAzureKeyVault()
.Build();
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));
return builder;
}
}
UPDATE: Had to add logic to access the actual appsettings.json location when deployed in Azure. It would run fine locally, but wasn't able to find the appsettings files when it was deployed.
I have the same issue as well. In the context of a Function, an ExecutionContextobject is injected from which I can access FunctionAppDirectory. It would be really helpful if a similar object is made available in the FunctionsStartup so that all configuration and DI can be done in the startup.
It's a great idea to bake this approach in FunctionsStartup as @miladghafoori said.
A Visual Studio project template similar to the asp.net core web/api app with the startup.cs and appsettings.json put together would be of great help than having to create these manually.
Another thought is that we could make a wrapper class of IConfiguration or IConfigurationRoot, in that way we could separate the logic of application settings and Azure function settings. In the dependency injection, we could only inject the wrapper class as a new type. We don't need to replace.
Sorry for the delayed response here. Currently, we don't have an accepted pattern/any guidance on accomplishing this. The workaround provided by @kemmis will work if needed. In the future, we do have plans to allow loading in configuration files out of the box without the need for custom code.
@gzuber Please provide an Issue# for us to follow and know when it is available
FYI - I updated my example above so that it should find the correct appsettings.json file when running in Azure. Using Environment.CurrentDirectory was only working when I ran the function locally, but not in Azure.
@kemmis nice update. I'd also add an extra explanation as to the _differences_ between localhost and azure settings/file locations (e.g. in the bin folder vs not in the bin folder but a level above [which is different to how netcore apps now work]).
Also, maybe a note about making sure the appsettings.json, etc files will need to be manually 'copy always' or 'copy when newer', to make sure they get pushed up.
nice code!
I have done something similar to get the correct directory based on environment. I make use of AZURE_FUNCTIONS_ENVIRONMENT, AzureWebJobsScriptRoot & HOME environment variables to achieve the same.
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// Get the path to the folder that has appsettings.json and other files.
// Note that there is a better way to get this path: ExecutionContext.FunctionAppDirectory when running inside a function. But we don't have access to the ExecutionContext here.
// Functions team should improve this in future. It will hopefully expose FunctionAppDirectory through some other way or env variable.
string basePath = IsDevelopmentEnvironment() ?
Environment.GetEnvironmentVariable("AzureWebJobsScriptRoot") :
$"{Environment.GetEnvironmentVariable("HOME")}\\site\\wwwroot";
var config = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) // common settings go here.
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT")}.json", optional: false, reloadOnChange: false) // environment specific settings go here
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: false) // secrets go here. This file is excluded from source control.
.AddEnvironmentVariables()
.Build();
builder.Services.AddSingleton<IConfiguration>(config);
}
public bool IsDevelopmentEnvironment()
{
return "Development".Equals(Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT"), StringComparison.OrdinalIgnoreCase);
}
}
Update: The above code creates a new IConfiguration and ends up replacing the originally registered IConfiguration. So, I would recommend follow kemmis's pattern which takes care of adding the originally registered IConfiguration into the newly created one. However, feel free to incorporate the environment variables suggested above.
I want to leave a warning here when using this approach. There's a reason this wasn't exposed with the startup. The work to make this happen is trivial in the host, but we can't perform that work in isolation.
While this works and the host will happily load configuration, if you add things like connection strings and some other trigger configuration to sources outside of the ones currently supported out of the box, key infrastructure components will not work as designed. O key example is the scale controller when running in consumption. If the controller is unable to locate configuration in the known sources, your Function App may not be activated and/or scaled. This is less of a concern in dedicated environments, but I wanted to make sure others were aware of potential problems with this and one of the key reasons why this is currently a runtime limitation.
like connection strings and some other trigger configuration to sources outside of the ones currently supported out of the box
@fabiocav can you please elaborate on this point with some examples. I don't fully understand, please 😊
Sure... some infrastructure pieces, particularly in the consumption model, need to monitor trigger sources and some configuration options that control the Function App behavior. One example of such component is the scale controller.
In order to activate and scale your application, the scale controller monitors the event source to understand when work is available and when demand increases or decreases. Using Service Bus as an example; the scale controller will monitor topics or queues used by Service Bus triggers, inspecting the queue/topic lengths and reacting based on that information. To accomplish this task, that component (which runs as part of the App Service infrastructure) needs the connection string for each Service Bus trigger setup and, today, it knows how to get that information from the sources we support. Any configuration coming from other providers/sources is not visible to the infrastructure outside of the runtime.
Hope the above is sufficient to clarify what I stated above, but let me know if you have any more questions.
OK - that does help me understand (and I hope, others also :) )
So to clarify ...
Azure Functions has some hardcoded conventions for configuration with respect to the built in 'stuff' like triggers. This convention is to use hosts.json (docs ref) for application settings (like queue settings or logging settings) and local.settings.json for app settings and connection strings that are used when running locally (live uses the portal to set these things).
But .. if we have our _own business logic settings_ we _could_ leverage our own custom settings files, like appsettings.json / appsettings.<environment>.json. If we choose to do this, then we need to understand that these files are for our own business logic and not for azure function settings stuff AND they'll require some custom code to be parsed/used in the azure functions c# code.
Finally, when using the portal to set settings (via the functions Application Settings section), this ends up adding theses key/values to Environmental Variables, which get used by the Functions infrastructure.
ref:

Okay .. was that about it?
To accomplish this task, that component (which runs as part of the App Service infrastructure) needs the connection string for each Service Bus trigger setup and, today, it knows how to get that information from the sources we support.
@fabiocav can you enumerate the list of sources supported? Is Azure Key Vault part of it?
Kv is working fine for me.
On Thu, Jul 25, 2019 at 10:28 AM mbusila notifications@github.com wrote:
To accomplish this task, that component (which runs as part of the App
Service infrastructure) needs the connection string for each Service Bus
trigger setup and, today, it knows how to get that information from the
sources we support.@fabiocav https://github.com/fabiocav can you enumerate the list of
sources supported? Is Azure Key Vault part of it?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Azure/azure-functions-host/issues/4464?email_source=notifications&email_token=AAE6C6MNIB7PHG2RCEGRYQ3QBG2A5A5CNFSM4HNU3YWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2ZUUEI#issuecomment-515066385,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAE6C6KB4ZAMHEDWOA3V3DLQBG2A5ANCNFSM4HNU3YWA
.
based on the example from @kemmis I've added some extension methods to a repo to approach this is a similar way
check out this repo
using extensions methods file like @kemmis in repo at IFunctionsHostBuilderConfigurationsExtensions.cs
If you look at Startup.cs you will see I'm calling an extension method and passing Func
Example:
builder.AddConfiguration((configBuilder) =>
{
var envName = Environment.GetEnvironmentVariable("ENVIRONMENT_NAME");
// use IConfigurationBuilder like you typically would
var configuration = configBuilder
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{envName}.json", true, true)
.TryAddAzureKeyVault(Environment.GetEnvironmentVariable("VAULT_NAME"))
.AddEnvironmentVariables()
.Build();
return configuration;
});
thanks @kemmis for the insights, if anyone has additional thoughts would love to hear any feedback. great to see DI in Azure Functions now, eager to see it continue to evolve.
Hi,
I had a similar issue with this, but I think passing the IConfiguration around as DI injected something is not a good idea in general. Rather than that, I would suggest to go for IOptions injected setting models.
You can then use this for example together with the Configuration in the Functions portal. Then you just build a local config that you do not register within the DI pipeline, but you can use it to configure your options.
public override void Configure(IFunctionsHostBuilder builder)
{
ConfigureServices(builder.Services);
}
private void ConfigureServices(IServiceCollection services)
{
var localConfig = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
services.Configure<ImageServiceSettings>(localConfig.GetSection("ImagesServiceSettings"));
services.AddTransient<ImageResizeService>();
}
You can surely also use an appsettings.json file, if you want to.
The service would then get something like:
public class ImageResizeService
{
private ILogger _logger;
private ImageServiceSettings _settings;
public ImageResizeService(ILogger<ImageResizeService> logger, IOptions<ImageServiceSettings> settings)
{
_logger = logger;
_settings = settings.Value;
}
// some code
}
Hope it helps someone. The important part is to not register the IConfiguration with the local configuration as also you host.json stuff (and everything @fabiocav mentioned) gets overwritten.
i tried this @lukasvosyka and in my application all value is null,
Startup

Function Trigger

Azure Portal

But it works fine in my local.
@jedmonsanto Maybe your BasePath is set wrong for Azure functions, when deployed. Try the suggested way as described above. Something like
var currentDirectory = "/home/site/wwwroot";
bool isLocal = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"));
if (isLocal)
{
currentDirectory = Environment.CurrentDirectory;
}
var tmpConfig = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json")
.Build();
@lukasvosyka no luck, what exactly the basepath in azure function app?
i've seen this below:

I've used the above approach to set a specific JSON file for configurations: appsettings.json and it works locally. In order to work on azure, I've set the base path using this:
private static string GetCurrentDirectory(IFunctionsHostBuilder builder)
{
ExecutionContextOptions executionContextOptions = builder.Services.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>().Value;
return executionContextOptions.AppDirectory;
}
But when I try to use the function on azure it says:
Error:
The function runtime is unable to start. Microsoft.Extensions.Configuration.FileExtensions: The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'D:homesitewwwrootappsettings.json'.
Session Id: fe4614558cdf459d94749746b8cfbc6b
Probably appsettings.json is not publishing to azure function app. The method that I'm using to deploy is just the "Publish" from VS2019. How can put the config file on: D:homesitewwwrootappsettings.json? Can I only do this by using ARM Templates?
Don't use appsettings.json , use Azure App Configuration instead. IMHO much better and easy to configure.
How is it easier to manually enter potentially dozens of different
configuration values in N1 * N2 number of app services’ configuration pages
via the azure portal blade, where N1 = the number of app services in each
environment and N2 is the number of environments. For us N1 is about 8 and
N2 is about 6. (Local, dev, qa, staging, prod, and support). That’s 48
different places to deploy settings if you place them in the portal. But if
we put them all in app settings json files, they all get saved in 1
solution that is stored and versioned in source control. The only other
settings we have are sensitive values (credentials), and those get saved in
key vaults. (That alone is enough trouble to make sure everything is wired
up with correct credentials. Hint: don’t use credentials when you can use
manages identities instead).
That’s why.
On Wed, Oct 2, 2019 at 4:24 AM Janus Knudsen notifications@github.com
wrote:
Don't use appsettings.json , use Azure App Configuration instead. IMHO
much better and easy to configure.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Azure/azure-functions-host/issues/4464?email_source=notifications&email_token=AAE6C6MJKWYGDNE266NP5BLQMO52HA5CNFSM4HNU3YWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEACZMQQ#issuecomment-537237058,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAE6C6NSNXNJKWN5P2M6LRDQMO52HANCNFSM4HNU3YWA
.
Also, if we could only communicate well enough with our devops team to come
up with naming conventions for settings values and environment names, then
we could essentially reduce N1*N2 down to N1 +1, if only N2 contains the
environment name used in your naming convention. But fml, you can only
fight so many battles, and for me, I’ve had to give up this one due to
numerous reasons within our company’s lack of competence.
On Wed, Oct 2, 2019 at 6:48 AM Rafe Kemmis rafe@kemmis.info wrote:
How is it easier to manually enter potentially dozens of different
configuration values in N1 * N2 number of app services’ configuration pages
via the azure portal blade, where N1 = the number of app services in each
environment and N2 is the number of environments. For us N1 is about 8 and
N2 is about 6. (Local, dev, qa, staging, prod, and support). That’s 48
different places to deploy settings if you place them in the portal. But if
we put them all in app settings json files, they all get saved in 1
solution that is stored and versioned in source control. The only other
settings we have are sensitive values (credentials), and those get saved in
key vaults. (That alone is enough trouble to make sure everything is wired
up with correct credentials. Hint: don’t use credentials when you can use
manages identities instead).That’s why.
On Wed, Oct 2, 2019 at 4:24 AM Janus Knudsen notifications@github.com
wrote:Don't use appsettings.json , use Azure App Configuration instead. IMHO
much better and easy to configure.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Azure/azure-functions-host/issues/4464?email_source=notifications&email_token=AAE6C6MJKWYGDNE266NP5BLQMO52HA5CNFSM4HNU3YWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEACZMQQ#issuecomment-537237058,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAE6C6NSNXNJKWN5P2M6LRDQMO52HANCNFSM4HNU3YWA
.
Yeah.. I know the battles as well. Sometimes things just are the way they are...
Ok regarding my issue on the top, I solved it by adding to .csproj where I was doing the publish, the following information:
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</None>
</ItemGroup>
Now everytime I publish the azure function it sends the appsettings.json also!
This is what I did, it works just fine, locally or on Azure. I actually set the "Copy to Output Directory" property on my local.settings.json in Visual Studio to "Copy if newer". The GetCustomSettingsPath() method ensure it's runs on azure or locally. This is a very easy approach that works fine.
public static partial class MyFunctionClass
{
static internal IConfigurationRoot Config = null;
static MyFunctionClass()
{
Config = new ConfigurationBuilder()
.SetBasePath(GetCustomSettingsPath())
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
// This is for Portal Support and requires no Auth token
[FunctionName("IsAlive")]
public static async Task<HttpResponseMessage> IsAlive()
{
// a function method
return null;
}
private static string GetCustomSettingsPath()
{
string path = "";
// This works on Azure or when running locally ...
string home = Environment.GetEnvironmentVariable("HOME");
if (home != null)
{
// We're on Azure
path = Path.Combine(home, "site", "wwwroot");
}
else
{
// Running locally
path = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
path = Path.GetDirectoryName(path);
DirectoryInfo parentDir = Directory.GetParent(path);
path = parentDir.FullName;
}
return path;
}
}
You can use this piece of code in your startup file.
I've just tested it today for my project and it works on both cloud and local
var executioncontextoptions = builder.Services.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
var config = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Another thought is that we could make a wrapper class of IConfiguration or IConfigurationRoot, in that way we could separate the logic of application settings and Azure function settings. In the dependency injection, we could only inject the wrapper class as a new type. We don't need to replace.
Is this an option or is the actual act of loading the appsettings file with the ConfigurationBuilder what causes the issues @fabiocav mentioned? Wasn't clear if that's the issue or it's adding the resulting IConfiguration to the service collection for DI. My assumption, is the latter, but can someone confirm?
Brilliant thinking @FabienColoignier! thanks for sharing that.
I know that this issue is closed, but it is the one that keeps hitting Google result 1 - So i will put it here!
@lukasvosyka - Not sure if you still have this problem?
I have been experiencing the same issue trying to add custom configuration for a functions v2 app via DI - As you reported, the host.json variable values get overwritten when registering a custom IConfiguration...
The below will ensure that all configurations are kept:
public void ConfigureServices(IServiceCollection services)
{
var providers = new List<IConfigurationProvider>();
foreach(var descriptor in services.Where(descriptor => descriptor.ServiceType == typeof(IConfiguration)).ToList())
{
var existingConfiguration = descriptor.ImplementationInstance as IConfigurationRoot;
if(existingConfiguration is null)
{
continue;
}
providers.AddRange(existingConfiguration.Providers);
services.Remove(descriptor);
}
var configuration = new ConfigurationBuilder();
// Add custom configuration to builder
providers.AddRange(configuration.Build().Providers);
services.AddSingleton<IConfiguration>(new ConfigurationRoot(providers));
}
@martinfletcher , Would you mind sharing your Startup.cs file. I am facing the same problem where after injecting IConfiguration all the host.json values are lost.
@kijujjav - Adding the above should be all you need. Can you post your startup file and I can take a look to see what might be your issue?
Its working with below code. Thanks @martinfletcher
public class Startup : FunctionsStartup
{
/// <summary>
/// This method is is for depedency injection of config files
/// </summary>
/// <param name="builder"></param>
public override void Configure(IFunctionsHostBuilder `builder)`
{
ConfigureServices(builder.Services);
}
public void ConfigureServices(IServiceCollection services)
{
var providers = new List<IConfigurationProvider>();
foreach (var descriptor in services.Where(descriptor => descriptor.ServiceType == typeof(IConfiguration)).ToList())
{
var existingConfiguration = descriptor.ImplementationInstance as IConfigurationRoot;
if (existingConfiguration is null)
{
continue;
}
providers.AddRange(existingConfiguration.Providers);
services.Remove(descriptor);
}
var executioncontextoptions = services.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
var config = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
providers.AddRange(config.Build().Providers);
services.AddSingleton<IConfiguration>(new ConfigurationRoot(providers));
}
}
@gzuber why is this closed if you're planning to implement it and there is no other issue open to track it?
Just came across this - hopefully this issue was resolved via this feature that allows loading in configuration sources: https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources
Feel free to open a new issue if there are capabilities this is missing (sometimes hard to have mega-issues that span a few stuff, so keeping closed to focus on still remaining work needed)
Most helpful comment
I have the same issue as well. In the context of a Function, an
ExecutionContextobject is injected from which I can accessFunctionAppDirectory. It would be really helpful if a similar object is made available in theFunctionsStartupso that all configuration and DI can be done in the startup.