0
votes

I am trying to follow guidance that would allow azure functions (v2) to use app settings json files for each environment (dev, test, stage, prod).

Approach 1 - Using ExecutionContext Based on this article, I attempted the following code. The settings are never returned from the local.settings.json which is included in the published code.

    [FunctionName("ScheduleApp")]
    public async Task Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context)
    {
        log.LogInformation($"ScheduleApp triggered...");

        try
        {
            var configurationBuilder = new ConfigurationBuilder()
               .SetBasePath(context.FunctionAppDirectory)
               .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
               .AddEnvironmentVariables()
               .Build();
            var y = configurationBuilder.GetConnectionStringOrSetting("somesetting");
            var x = configurationBuilder["test"];
            log.LogInformation($"x = {x}, y = {y});
        }
        catch (Exception ex)
        {
            log.LogInformation($"exception = {ex.Message}");
        }

Approach 2 - Using startup.cs ConfigurationBuilder
FunctionOptions.cs is a class I wrote that is a set of properties that matches the configuration in the local.settings.json file Values collection. The IOptionsMonitor of this class is passed in the constructor of the azure function shown below. values from the json file are not returned in this approach either.

Startup.cs....

    public override void Configure(IFunctionsHostBuilder builder)
    {
        var configurationBuilder = new ConfigurationBuilder();

        var descriptor = builder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
        if (descriptor?.ImplementationInstance is IConfigurationRoot configuration)
        {
            configurationBuilder.AddConfiguration(configuration);
        }
        Configuration = configurationBuilder.Build();

        builder.Services.AddOptions<FunctionOptions>();
        builder.Services.Configure<FunctionOptions>(Configuration);
        builder.Services.Configure<FunctionOptions>(Configuration.GetSection("Values"));
    }

ScheduleApp.cs....

    public ScheduleApp(IConfiguration configuration, IOptionsMonitor<FunctionOptions> optionsAccessor)
    {
        _functionOptions = optionsAccessor.CurrentValue;
        _configuration = configuration;
    }

[FunctionName("ScheduleApp")]
    public async Task Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context)
    {
        log.LogInformation($"ScheduleApp triggered...");

      try
        {
            var z = "";
            if (_functionOptions != null)
                z = _functionOptions.somesetting;
            else
                log.LogInformation("_funcionsOption is null");

            log.LogInformation($"_funcionsOption = {z}");
        }
        catch (Exception ex)
        {
            log.LogInformation($"_funcionsOption exception, ex={ex.Message}");
        }

Neither of these approaches yield the configuration values. I tried adding the .json files as content in the azure function. I played around with the environment variable AZURE_FUNCTIONS_ENVIRONMENT as I expected this would control which set of settings were used (by environment). Nothing is working.

I must be missing something. Can someone point towards what I'm missing or a working c# sample of either approach? Ideally it would also show how to manage each environment setting as in

  • appsettings.dev.json
  • appsettings.test.json
  • appsettings.stage.json
  • appsettings.prod.json
2

2 Answers

2
votes

Override the ConfigureAppConfiguration method in your FunctionStartup class (https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources).

The following example takes the one provided in the documentation a step further by adding user secrets.

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

    builder.ConfigurationBuilder
        .SetBasePath(context.ApplicationRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
        .AddJsonFile($"appsettings.{context.EnvironmentName}.json", optional: true, reloadOnChange: false)
        .AddUserSecrets(Assembly.GetExecutingAssembly(), true, true)
        .AddEnvironmentVariables();
}

By default, configuration files such as appsettings.json are not automatically copied to the Azure Function output folder. Be sure to review the documentation (https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources) for modifications to your .csproj file. Also note that due to the way the method appends the existing providers it is necessary to always end with .AddEnvironmentVariables().

A deeper discussion on configuration in an Azure Function can be found at Using ConfigurationBuilder in FunctionsStartup

0
votes

You can achieve this easily by setting environment variable.

Refer to the following steps:

1.In function.cs:

public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ExecutionContext context, ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");
    var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
    Console.WriteLine($"the environment is: " + environment);
    var configurationBuilder = new ConfigurationBuilder()
        .SetBasePath(context.FunctionAppDirectory)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();
    var value = configurationBuilder.GetConnectionStringOrSetting("hi");
    log.LogInformation($"value = {value}");
    return (ActionResult)new OkObjectResult($"Hello, {value}");
}

2.Then add 2 .json files in the project: appsettings.dev.json and appsettings.json. The structure of the 2 .json files are the same, but have different storage connectiong strings.

In appsetings.json:

{
  "hi": "world"
}

In appsettings.dev.json:

{
  "hi":"i am dev appsettings"
}

Note that in visual studio, right click each of the json file -> properties -> set "Copy to Output Directory" to "Copy always".

3.Set environment variable via cmd or UI. The cmd command is setx ASPNETCORE_ENVIRONMENT "dev", then restart the visual studio if you want to use the environment variable.

enter image description here

If you deploy function to azure, set ASPNETCORE_ENVIRONMENT in Appsetting settings.

enter image description here

4.The output is as below:

enter image description here