azure-functions-core-tools: Loading settings from User Secrets doesn't work

User Secrets are supposed to be working but they don’t.

When trying to move settings from local.settings.json to User Secrets, the following error is happening:

image

Azure Functions Core Tools Core Tools Version: 3.0.3233 Commit hash: d1772f733802122a326fa696dd4c086292ec0171 Function Runtime Version: 3.0.15193.0

[2021-01-29T01:13:59.792Z] Found C:\github\djpool\mdc\Cloud\src\Endpoints\Integration\Integration.csproj. Using for user secrets file configuration. Missing value for AzureWebJobsStorage in local.settings.json and User Secrets. This is required for all triggers other than httptrigger, kafkatrigger. You can run ‘func azure functionapp fetch-app-settings <functionAppName>’ or specify a connection string in local.settings.json or User Secrets.

Context: https://twitter.com/sfeldman/status/1354567460532285442

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 31 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Just to also iterate, kind of what’s already been said… the problem is also present running isolated processes in .NET 6

public async Task Run(
  [BlobTrigger($"{ContainerName}/{{blobName}}", Connection = "MySpecialAppSettingConnectionString")]
...

Having MySpecialAppSettingConnectionString set correctly in local.settings.json will work just fine.

But it will not get picked up correctly from user secrets.

Not sure if that helps anyone, but I to get a message saying Using for user secrets file configuration..

  1. <UserSecretsId>898cd92a-e4a0-46ff-9296-2fef4b6b9a14</UserSecretsId>
  2. dotnet user-secrets list gives:
ServiceBusConnection = Endpoint=sb://xxx
AppConfigConnectionString = Endpoint=https://xxxx

And app fails because none of those values are there.

I’ve modified startup.cs and solved the issue by adding:

public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
            if (System.Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") == "Development")
            {
                builder.ConfigurationBuilder.AddUserSecrets<Startup>();
            }

but I was expecting default builder to do it…

The solution of PureKrome appears to be working great.

However, it looks like the configuration is too late for the Bindings/Triggers. I can see the secrets are being added to the IConfiguration, but these values aren’t used in the Connection parameter of some triggers.

When trying to set the Configuration of the IFunctionsWorkerapplicationBuilder an error occurs. Any suggestions for these triggers?

Unfortunately, the suggestion from @NorthHighlandNicole is unavailable in .NET 5, I am trying to upgrade all my projects to .NET 5, and I can’t do it with Azure Functions, because of this very problem concerning User Secrets and AzureWebJobsStorage.

The problem is not just with AzureWebJobsStorage. Whatever value I put for the “Connection” field next to my trigger path, if there is a corresponding value in User Secrets but none in local.settings.json, the trigger will not work.

image

In the image above, if “MyConnection” is a key in User Secrets with a valid sotrage connection string but there is no such key in local.settings.json, then I get the following error:

“Microsoft.Azure.WebJobs.Host: Error indexing method ‘Functions.BlobFunction1’. Microsoft.Azure.WebJobs.Extensions.Storage: Storage account connection string ‘AzureWebJobsMyConnection’ does not exist. Make sure that it is a defined App Setting.”

If I do put “MyConnection” in local.settings.json, then the trigger works as expected… but that defeats the purpose of User Secrets!

Is there a way around this problem, since it prevents upgrade to .NET 5 (and Isolated Process Functions)?

Thanks.

The reason the user-secrets are not getting loaded is because the default implementation doesn’t support them. To enable user-secrets, there’s a few steps required.

(NOTE: i’ve ommited any user-secrets nuget packages, init steps, etc… they are required but assumed you know about that, already)

A Story in 3 Acts …

Act 1: The Setup

Here’s an example of what the current code looks like, using the default templates, etc… with some changes … but -no user-secret- changes. aka. “the before” code 😃

  • I have a SQL database class. (aka. some repository). It requires a connectionString
  • The connectionString is located in the user-secrets. 🌠

image

(and the code for copy-pasting):

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices((appBuilder, services) =>
    {
        // Use Serilog for logging.
        // Serilog is configured via Env Vars .. which is
        //   => Localhost: local.settings.json
        //   => Azure Functions on Azure: the 'Configuration' tab/section/page
        var logger = new LoggerConfiguration()
            .ReadFrom.Configuration(appBuilder.Configuration) // <-- This is the magic, here!
            .Enrich.FromLogContext()
            .CreateLogger();
        services.AddLogging(configure => configure.AddSerilog(logger, true));

        // Setup our configuration settings.
        // Just a strongly typed class.
        services
            .AddOptions<ConnectionStringOptions>()
            .Configure<IConfiguration>((settings, configuration) =>
            {
                configuration
                    .GetSection(ConnectionStringOptions.ConfigurationSectionKey)
                    .Bind(settings);
            });

        // DapperRepository requires an instance of an IOptions<ConnectionStringOptions> class
        // in the ctor .. which is why that is registered, above.
        services.AddSingleton<IRepository, DapperRepository>(); 
    })
    .Build();

host.Run();

Act 2: The Magic

Ok, fine. so now lets tell our program that we -also- need to wire up user secrets!

image



var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureAppConfiguration(builder =>
    {
        builder.AddConfiguration(GetConfigurationBuilder());
    })
    .ConfigureServices((appBuilder, services) =>
    {
        var logger = new LoggerConfiguration()
            .ReadFrom.Configuration(appBuilder.Configuration)
            .Enrich.FromLogContext()
            .CreateLogger();
        services.AddLogging(configure => configure.AddSerilog(logger, true));

        // Setup our configuration settings.
        services
            .AddOptions<ConnectionStringOptions>()
            .Configure<IConfiguration>((settings, configuration) =>
            {
                configuration
                    .GetSection(ConnectionStringOptions.ConfigurationSectionKey)
                    .Bind(settings);
            });

        services.AddSingleton<IRepository, DapperRepository>();
    })
    .Build();


host.Run();


// Strongly based off/from: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder?view=dotnet-plat-ext-5.0
static IConfiguration GetConfigurationBuilder() =>
    new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddUserSecrets<Program>()
        .AddEnvironmentVariables()
        .Build();

So here we can see that we need to say “please also use my extra configuration information”.

so builder.AddConfiguration(GetConfigurationBuilder()); will ‘add’ anything extra to the existing default configuration settings.

Here I have said:

  • Add user secrets
  • Add env vars (I actually don’t need to have this, because the default is already doing this). So I’ve now removed it from my code … but left it here for prosperity.

And viola! you can now access your user secrets.

Act 3: The Future

So … where does this leave us?

  1. We can just do the above and stuff works.
  2. We can update the ConfigureFunctionsWorkerDefaults to also include UserSecrets, but this means Azure Functions has yet another dependency … which might not be wanted/a good thing?

Anyhow … hope this helps.

Source of error message just for reference in the discussion: https://github.com/Azure/azure-functions-core-tools/blob/5e114c97ed391449fefcae1a93767f6e9fd8f49a/src/Azure.Functions.Cli/Actions/HostActions/StartHostAction.cs#L404-L410

This is also hit when attempting to use identity for the AzureWebJobsStorage connection (still a preview feature), where I have AzureWebJobsStorage__accountName instead of AzureWebJobsStorage in local.settings.json

As an aside, I’d also argue that the phrasing of the error implies a managed identity in a location where func is being run, which is not really a scenario. Managed identities only exist in Azure. When developing locally, identity can be used, but it will instead use the developer identity or other options that can be configured.

Core Tools doesn’t have access to the dotnet toolchain needed to resolve the secrets automatically, so we had to write custom logic to locate the csproj file and extract the secrets id. This means it’s not trivial for us to resolve those variables.

That said, the initial issue is worth investigating, where an empty AzureWebJobsStorage in local.settings.json hides the user secrets value. Perhaps this is working as intended, but we should see if we can provide a message when this happens. We already have some warnings when environment variables are overriding local.settings.json values. @gzuber could you please investigate?