How to add an appsettings.json file to my Azure Function 3.0 configuration?

With this .NET Core 3.1 and Azure Function 3. Spent a hours days. Here is what I came up with.

[assembly: FunctionsStartup(typeof(Ugly.AzureFunctions.Startup))]

namespace Ugly.AzureFunctions
{
    class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            try
            {
                // On Azure, we need to get where the app is.
                // If you use Directory.GetCurrentDirectory(), you will get something like D:\Program Files (x86)\SiteExtensions\Functions\3.0.14785\32bit
                var basePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..");
                var environmentName = builder.GetContext().EnvironmentName;
                builder.ConfigurationBuilder
                    .SetBasePath(basePath)
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
                    .AddEnvironmentVariables();
            }
            catch (Exception ex)
            {
                // Handle exceptions about this. Which __should__ never ever happen.
                // The previous comment is sarcastic.
                throw;
            }
        }

        public override void Configure(IFunctionsHostBuilder builder)
        {
            try
            {
                // DO NOT add the configuration as Singleton.
                // If you need the IConfiguration:
                //var configuration = builder.GetContext().Configuration;

                builder.Services
                    .AddOptions<MachineLearningConfig>()
                    .Configure<IConfiguration>((settings, configuration) => {
                        configuration.GetSection("MachineLearningConfig").Bind(settings);
                });
            }
            catch (Exception ex)
            {
                // Handle or not handle? That's the question.
                throw;
            }
        }
    }
}

In Azure Functions v3 you can use the appsettings.json configuration pattern from ASP.NET-Core with the ConfigureAppConfiguration call below (reference).

Additionally, change the way you add your options by using the code within the Configure method below. You should not be passing IConfiguration to IServiceProvider.Configure<>(). This will allow you to use an injected IOptions<MachineLearningConfig> object.

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;

[assembly: FunctionsStartup(typeof(Startup))]

namespace MyAzureFunction
{
    public class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            if (builder == null) throw new ArgumentNullException(nameof(builder));

            var context = builder.GetContext();

            builder.ConfigurationBuilder
                .AddAppsettingsFile(context)
                .AddAppsettingsFile(context, useEnvironment: true)
                .AddEnvironmentVariables();
        }

        public override void Configure(IFunctionsHostBuilder builder)
        {
            if (builder == null) throw new ArgumentNullException(nameof(builder));

            var configuration = builder.GetContext().Configuration;

            builder.Services.Configure<MachineLearningConfig>(options =>
            {
                configuration.GetSection("MachineLearningConfig").bind(options);
            });
        }
    }

    public static class ConfigurationBuilderExtensions
    {
        public static IConfigurationBuilder AddAppsettingsFile(
            this IConfigurationBuilder configurationBuilder,
            FunctionsHostBuilderContext context,
            bool useEnvironment = false
        )
        {
            if (context == null) throw new ArgumentNullException(nameof(context));

            var environmentSection = string.Empty;

            if (useEnvironment)
            {
                environmentSection = $".{context.EnvironmentName}";
            }

            configurationBuilder.AddJsonFile(
                path: Path.Combine(context.ApplicationRootPath, $"appsettings{environmentSection}.json"),
                optional: true,
                reloadOnChange: false);

            return configurationBuilder;
        }
    }
}

In the startup class:

    IConfigurationRoot config = new ConfigurationBuilder()
              .SetBasePath(Environment.CurrentDirectory)
              .AddJsonFile("someSettings.json", optional: true, reloadOnChange: true)
              .AddEnvironmentVariables()
              .Build();

Add a json file to you project that holds the settings. Note that local.settings.json is ignored/removed during deployment. (Name the file something else.)


Nkosi's solution works pretty well, but it does update the way the azure function runtime loads settings for itself, by replacing IConfiguration singleton: services.AddSingleton<IConfiguration>.

I prefer having another IConfigurationRoot that is not injected. I just need to inject my settings IOption<MachineLearningSettings> that are linked to my own IConfigurationRoot.

I build another IConfigurationRoot that is member of the Startup class:

public class Startup : FunctionsStartup
{
    private IConfigurationRoot _functionConfig = null;

    private IConfigurationRoot FunctionConfig( string appDir ) => 
        _functionConfig ??= new ConfigurationBuilder()
            .AddJsonFile(Path.Combine(appDir, "appsettings.json"), optional: true, reloadOnChange: true)
            .Build();

    public override void Configure(IFunctionsHostBuilder builder)
    {
         builder.Services.AddOptions<MachineLearningSettings>()
             .Configure<IOptions<ExecutionContextOptions>>((mlSettings, exeContext) =>
                 FunctionConfig(exeContext.Value.AppDirectory).GetSection("MachineLearningSettings").Bind(mlSettings) );
    }
}

Note: connection strings must remain in the application settings, because it is required by triggers to create an instance of the the function app that is not started (in a consumption service plan).