'Why would my Azure Function not load my environment variables correctly in startup.cs?

We have a C# precompiled v4 Azure Function on .NET 6.0 on Windows App Service. We have the following code in Startup.cs:

public override void Configure(IFunctionsHostBuilder builder)
{
    IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile(Path.Combine(builder.GetContext().ApplicationRootPath, $"local.settings.json"), optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

    builder.Services.AddSingleton(config);

    builder.Services.AddSingleton(s =>
    {
        var connectionString = config["Azure:CosmosDB:ConnectionString"];
        if (string.IsNullOrEmpty(connectionString)) throw new InvalidOperationException(
            "Please specify a valid CosmosDBConnection in the local.settings.json file or your Azure Functions Settings.");

        return new CosmosClientBuilder(connectionString)
           .Build();
    });
    
    // Additional services configuration
}

And we have the necessary package references in csproj:

 <ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.20.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.0" />
 </ItemGroup>

This works well running locally in VS 2022 and loading from local.settings.json, but fails on the null connection string check, throwing the InvalidOperationException shown, when deployed to the cloud, despite the environment variable being correctly set:

enter image description here

What are we missing here?



Solution 1:[1]

The fix for us was to override ConfigureAppConfiguration to initialize Config, and (since we need to access it elsewhere in startup) to set it as a variable on the Startup.cs class.

    public IConfiguration Config { get; private set; }

    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton(s =>
        {
            var connectionString = Config["Azure:CosmosDB:ConnectionString"];
            if (string.IsNullOrEmpty(connectionString)) throw new InvalidOperationException(
                    "Please specify a valid CosmosDBConnection in the local.settings.json file or your Azure Functions Settings.");

            return new CosmosClientBuilder(connectionString)
                .Build();
        });

    // Etc.
    }

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        builder.ConfigurationBuilder
            .AddJsonFile(Path.Combine(builder.GetContext().ApplicationRootPath, $"local.settings.json"), optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();
        Config = builder.ConfigurationBuilder.Build();

    }

Since AddSingleton is passed an action, and that action isn't called until the service is required, then config in our original code is presumably out of scope. I'm not confused as to why this didn't work when we deployed it, but I am confused as to why it worked locally. Either way, the fix above works well in both, so the issue is resolved.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1