31

Is it possible to use an appsettings.json file in Azure Functions?

There is documentation for environment variables here..

https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#environment-variables

..however we use Octopus for deployments and would really like to have appsettings version controlled.

We have tried using

{
  "frameworks": {
    "net46": {
      "dependencies": {
        "Microsoft.Extensions.Configuration": "1.0.0",
        "Microsoft.Extensions.Configuration.Json": "1.0.0"
      }
    }
  }
}

but constantly get

2016-11-23T15:27:03.811 (12,16): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0

Even being able to supply/update environment variables via Octopus would be sufficient for our needs.

Please advise.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Nosmadas
  • 632
  • 3
  • 9
  • 17

8 Answers8

26

For your needs the answer is YES! Azure Functions can use appsettings.json for your configurations. But there are some ordering sequence that Azure will do when a Function is requested.

1º) Azure will look for that KEYS that you used on .GetEnvironmentVariables("[KEY]") method, through Keys that were configured on Application Settings blade in Azure Functions settings

2º) If Azure wasn't able to find out that configurations through Application Settings keys, then Azure will try looking for after appsettings.json file into your root folder of the Function that you working on.

3º) Finally, if Azure wasn't able to find out this keys either on Application Settings neither on appsettings.json file, then Azure will do the last attempt to find out web.config for looking for into this file appsettings section keys.

For your appreciation, you'll able to find out these configurations by the sample on my github repo: here and here

I hope that these information help you.

  • That sounds great, but is it only single key/value pairs? Can I have a complex object like I get from `ConfigurationBuilder`? – Bill Noel Jun 17 '17 at 22:12
  • Yes, You Can! Just put your configuration like this: **SectionA**:SubSectionA1:SubSectionA11:"YOUR VALUE GOES HERE". On Application Settings Key field. This "BIG" string is a JSON convention. Use appsettings.json is only one way to create your own configuration, not last. You can use **Application Settings** on your Functions Portal. Another way that you can try is Azure Key Vault. – Jose Roberto Araujo Jun 22 '17 at 19:29
  • I'm not sure exactly how that works, but I'll fiddle with it. Thanks... – Bill Noel Jun 23 '17 at 15:05
  • Thanks. Looks like just what I needed. Seems like a hack compared to the full-fledged capabilities elsewhere with web/service apps, but given the lightweight approach for functions is great that it looks like it fits the bill. about to test it out. – drewid Jul 02 '17 at 06:31
  • According to [Azure-Functions: Can you use web.config restrictions (e.g. IP restriction or basic Auth)](//stackoverflow.com/q/43241431) Web.config files do not apply to Function Apps and will be ignored. Where did you source that web.config is used? – Michael Freidgeim Jul 12 '17 at 02:40
  • @MichaelFreidgeim Who said anything about the Web.config? – Victorio Berra Nov 14 '18 at 16:41
  • @JoséRobertoAraújo "root folder of the Function that you working on" does that mean it sits next to the host.json or inside the function folder(s)? See structure here: https://learn.microsoft.com/en-us/azure/azure-functions/deployment-zip-push#deployment-zip-file-requirements – Victorio Berra Nov 14 '18 at 16:42
  • @VictorioBerra asked *"Who said anything about the Web.config?"* The answer mentions Web.config in the "3o" bullet point; although I'm struggling to comprehend what the wording means: *"Azure will do the last attempt to find out web.config for looking for into this file appsettings section keys"*. – Richardissimo Sep 17 '21 at 12:51
13

According to the changes made to the configuration files, you should only use local.settings.json since the appsettings.json was renamed to local.settings.json

Reference to the change: azure-functions-cli

armadillo.mx
  • 934
  • 1
  • 11
  • 17
9

Only environment variables are supported for app settings and connection strings. appsettings.json is not supported.

However, you can use Azure Resource Manager (ARM) Templates to configure the settings for your Function App. Here's a blog post that describe this in more detail.

mathewc
  • 13,312
  • 2
  • 45
  • 53
  • @mathewc - in the new preview tooling for Azure Functions, it auto-creates an appsettings.json file when I create a Functions project. Has something changed since your answer here? – brandon-barnett Dec 05 '16 at 18:37
  • 2
    That settings file is for local development only and is used by our CLI. There is a blog post [here](https://blogs.msdn.microsoft.com/appserviceteam/2016/12/01/running-azure-functions-locally-with-the-cli/) describing that in more detail. – mathewc Dec 05 '16 at 19:30
  • @mathewc It would be fantastic if the tooling would auto-ignore that local.settings.json file from the source control provider, if possible. In addition, when you right-click -> publish (to azure) it gets copied up to the file system. Presumably this local file is not actually needed at runtime, if the Environment Variables get injected from AppSettings? – JoeBrockhaus Jun 08 '17 at 02:22
9

Configuration source customization is available beginning in Azure Functions host versions 2.0.14192.0 and 3.0.14191.0.

To specify additional configuration sources, override the ConfigureAppConfiguration method in your function app's StartUp class.

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
    public class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder 
builder)
        {
            FunctionsHostBuilderContext context = builder.GetContext();

            builder.ConfigurationBuilder
                .AddJsonFile(Path.Combine(context.ApplicationRootPath, 
"appsettings.json"), optional: true, reloadOnChange: false)
                .AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings. 
{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
                .AddEnvironmentVariables();
        }
    }
}

// update configuration in csproject

<None Update="appsettings.json">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>      
</None>

<None Update="appsettings">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>

Instead of everytime using configuration, you can inject option class whenever required as below. From inside the Startup.Configure method, you can extract values from the IConfiguration instance into your custom type using the following code:

builder.Services.AddOptions<MyOptions>()
    .Configure<IConfiguration>((settings, configuration) =>
    {
        configuration.GetSection("MyOptions").Bind(settings);
    });

Function class:

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

    public HttpTrigger(IOptions<MyOptions> options)
    {
        _settings = options.Value;
    }
}

Refer: https://learn.microsoft.com/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Nilesh Sawant
  • 1,406
  • 14
  • 8
  • 1
    What is the second "appsettings" added to the .csproj file? – zane Feb 21 '22 at 10:44
  • Your contextApplicationRootPath did the trick for me after quite sometime the app only loading settings from azure but ignoring my appsettings.son thank you :) – Egli Becerra Oct 28 '22 at 14:00
3

In Azure Functions, setting are stored in local.settings.json (create this file if doesn't exist in your solution/ name should be exact as mentioned).

once you add setting file, you have to configure it under your Run() method as mentioned bellow,

enter image description here

When Accessing setting, use below

IConfigurationRoot config;
config["fromEmail"];

use below command to publish settings

func azure functionapp publish *YourAppName* --publish-local-settings -i

enter image description here

2

Tried and test approach where we can:

  1. Create an appsettings.json within your function app project.
  2. You would need add a Startup.cs class within your project inheriting from FunctionsStartup class. This would expose a method Configure(IFunctionsHostBuilder builder) for overriding.
  3. For control on level of customisation, I would say to extend IFunctionsHostBuilder class lets say it is (IFunctionsHostBuilderExtensions.cs) and add an extension method to add a new configuration builder to the same. This gives us the benefit of controlling how we want to configure the appsettings setup at runtime.
  4. Once, done, you can call the newly created extension method by either passing the entire file path for the appsettings or just the name.( You can configure base path for you appsettings in the extension method to avoid hastiness within the code).
  5. Once you build the extension method, you will get a service ready for injection i.e. IConfiguration, which can be used as anywhere within the codebase.
  6. You can also add multiple providers for the appsettings, like Azure Key Vault, AWS Secret Manager etc. For the same , all you need to do is add an extension method within the IFunctionsHostBuilderExtensions class & call them within your startup class.
  7. If you want to keep things more neat, you can implement a wrapper around the IConfiguration service to expose a single GetSettings(string key) method which would return the settings that you want from a central collection of providers within IConfiguration.

Some code snips below:

/// <summary>
///     Represents the startup class of the function app.
/// </summary>
public class Startup : FunctionsStartup
{
    private const string LocalSettingFileGenericName = "appsettings";
    private const string LocalSettingFileExtension = "json";

    /// <summary>
    ///     Configures the host builder for the function app.
    /// </summary>
    /// <param name="builder">The function app host builder.</param>
    public override void Configure(IFunctionsHostBuilder builder)
    {
        try
        {
            builder.AddConfiguration((configurationBuilder) =>
            {
                var configuration = typeof(Startup).Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>().Configuration;

                var configurationFileName = !string.Equals(configuration, "Release")
                    ? $"{LocalSettingFileGenericName}.{configuration.ToLowerInvariant()}.{LocalSettingFileExtension}"
                    : $"{LocalSettingFileGenericName}.{LocalSettingFileExtension}";

                var configurationSource = configurationBuilder
                    .AddJsonFile(configurationFileName, false, true)
                    .AddEnvironmentVariables();

                var partialConfigurationBuilder = configurationSource.Build();

                var keyVaultName = partialConfigurationBuilder.GetSection(ConfigurationKeys.KeyvaultName)?.Value;

                return configurationSource
                       .AddKeyVaultWithManagedIdentity(keyVaultName)
                       .Build();
            });

IFunctionBuilderExtensions.cs

 internal static class IFunctionsHostBuilderConfigurationsExtensions
{
    private const string keyVaultGenericUri = "https://{0}.vault.azure.net/";

    /// <summary>
    ///     Provides an extension method to add configuration provider.
    /// </summary>
    /// <param name="builder">The function app host builder.</param>
    /// <param name="configBuilderFunc">The delegate to pointing to configuration builder.</param>
    /// <returns>The function app host builder</returns>
    public static IFunctionsHostBuilder AddConfiguration(
        this IFunctionsHostBuilder builder,
        Func<IConfigurationBuilder, IConfiguration> configBuilderFunc)
    {
        var configurationBuilder = builder.GetBaseConfigurationBuilder();

        var configuration = configBuilderFunc(configurationBuilder);

        builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

        return builder;
    }

    /// <summary>
    ///     Provides an extension method to add Azure Key Vault as a configuration provider.
    /// </summary>
    /// <param name="builder">The configuration builder.</param>
    /// <param name="keyvaultName">The azure key vault name.</param>
    /// <param name="authenticationClientId">The AAD application clientId.</param>
    /// <param name="authenticationClientSecret">The AAD application clientSecret.</param>
    /// <returns>The configuration builder.</returns>
    public static IConfigurationBuilder AddKeyVaultWithManagedIdentity(
        this IConfigurationBuilder builder,
        string keyvaultName)
    {
        if (string.IsNullOrWhiteSpace(keyvaultName))
        {
            return builder;
        }

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

        var keyVaultUri = string.Format(keyVaultGenericUri, keyvaultName);

        builder.AddAzureKeyVault(
            keyVaultUri,
            keyVaultClient,
            new DefaultKeyVaultSecretManager());

        return builder;
    }

    private static IConfigurationBuilder GetBaseConfigurationBuilder(this IFunctionsHostBuilder builder)
    {
        var configurationBuilder = new ConfigurationBuilder();

        var descriptor = builder.Services.FirstOrDefault(
            service => service.ServiceType == typeof(IConfiguration));

        if (descriptor?.ImplementationInstance is IConfiguration configRoot)
        {
            configurationBuilder.AddConfiguration(configRoot);
        }

        var rootConfigurationBuilder = configurationBuilder.SetBasePath(GetCurrentDirectory());

        return rootConfigurationBuilder;
    }

    private static string GetCurrentDirectory()
    {
        var currentDirectory = Path.GetDirectoryName(
            Assembly.GetExecutingAssembly().Location);

        return currentDirectory.Replace("bin", "{Your settings directory}");
    }

Example for wrapper implementation:

  /// <summary>
///     Represents the configuration settings provider class.
/// </summary>
public class ConfigurationSettings : IConfigurationSettings
{
    private readonly IConfiguration configurationSource;

    /// <summary>
    ///     Initializes the class of type <see cref="ConfigurationSettings"/>.
    /// </summary>
    /// <param name="configurationSource">The configuration source.</param>
    public ConfigurationSettings(
        IConfiguration configurationSource)
    {
        this.configurationSource = configurationSource;
    }

    ///<inheritdoc/>
    public T GetSetting<T>(string key)
    {
        try
        {
            if (!configurationSource.GetSection(key).Exists())
            {
                throw new ConfigurationDoesNotExistException(
                    $"The configuration with key {key} does not exist in appsetting or key vault.");
            }

            return (T)Convert.ChangeType(configurationSource.GetSection(key)?.Value, typeof(T));
        }
        catch (InvalidCastException)
        {
            throw;
        }
        catch (Exception)
        {
            throw;
        }
    }
}
  • Do you get the appsettings.json loaded while testing the app locally? In my case the appsettigs.json doesn't get copied into the bin/Debug/netcoreapp3.1 folder. – Neits May 27 '21 at 10:44
1

Maybe this simple solution would be useful:

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

namespace AzureFunctions;

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var context = builder.GetContext();
        var config = context.Configuration; //Here you go!
    }

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        var context = builder.GetContext();

        builder.ConfigurationBuilder
                .AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
                .AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false); //For example enviroment name can be: Development
    }

}
Yoro
  • 805
  • 8
  • 17
-3

For dependencies you should use/create the project.json inside your function. There you can specify your dependencies. Please check: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#package-management

For example:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.ProjectOxford.Face": "1.1.0"
      }
    }
   }
}
  • 1
    the question is about appsettings.json, not the project.json file, where you specify nuget dependencies – Thiago Silva Feb 20 '17 at 23:40
  • You do not specify dependencies in appsettings.json, that is the reason you have project.json. Check: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#package-management – Aureliano Buendia Feb 21 '17 at 17:00
  • 2
    I understand that, but the original question was about appsettings.json, not about package dependencies - the OP was trying to have his environment settings and connections in a file, not in the Web App Settings in Azure. – Thiago Silva Mar 01 '17 at 18:50