.NET 9 Function App Secrets Management with Aspire - Seeking Best Practices

Lindsey Crowe 0 Reputation points
2025-04-15T23:54:25.9066667+00:00

Hi everyone,

We're working on a .NET 9 solution in C# with the following structure:

  • An API project
  • An Azure Function App project
  • An Aspire AppHost project
  • Several other supporting projects

We've successfully set up our local development environment using Aspire, which orchestrates everything beautifully, including a SQL Server database and Azurite for Azure blob and queue services.

Our current challenge lies in effectively managing secrets for our Function App when it's run through the Aspire AppHost.

Here's the situation:

  • When running the Function App project directly (as the startup project), it correctly loads configuration from local.settings.json.
  • However, when the Function App is run as part of the Aspire application, local.settings.json is not being picked up.
  • Aspire seems to naturally support an appsettings.json file within the Function App project.

This presents a couple of issues:

  1. Dual Configuration: We'd prefer to avoid maintaining two separate configuration files (appsettings.json and local.settings.json) for different execution scenarios.
  2. Development Secrets: Aspire doesn't appear to automatically load appsettings.Development.json in the Function App project. This is problematic because we need a way to manage development-specific secrets without committing them to our main appsettings.json.

Therefore, my questions to the community are:

  • What is the recommended best practice for handling Function App secrets when running within an Aspire application?
  • Are there established patterns or configurations we should be considering to avoid having both local.settings.json and appsettings.json?
  • How can we effectively manage development-specific secrets for the Function App in this Aspire context, given that appsettings.Development.json doesn't seem to be automatically loaded?

We're aware that .NET Aspire's Function App support is currently in preview, so we understand things might evolve. We're just looking for the most robust and maintainable approach for our current setup.

Any insights, suggestions, or experiences you can share would be greatly appreciated!

Thanks in advance for your help.Hi everyone,

We're working on a .NET 9 solution in C# with the following structure:

  • An API project
  • An Azure Function App project
  • An Aspire AppHost project
  • Several other supporting projects

We've successfully set up our local development environment using Aspire, which orchestrates everything beautifully, including a SQL Server database and Azurite for Azure blob and queue services.

Our current challenge lies in effectively managing secrets for our Function App when it's run through the Aspire AppHost.

Here's the situation:

  • When running the Function App project directly (as the startup project), it correctly loads configuration from local.settings.json.
  • However, when the Function App is run as part of the Aspire application, local.settings.json is not being picked up.
  • Aspire seems to naturally support an appsettings.json file within the Function App project.

This presents a couple of issues:

  1. Dual Configuration: We'd prefer to avoid maintaining two separate configuration files (appsettings.json and local.settings.json) for different execution scenarios.
  2. Development Secrets: Aspire doesn't appear to automatically load appsettings.Development.json in the Function App project. This is problematic because we need a way to manage development-specific secrets without committing them to our main appsettings.json.

Therefore, my questions to the community are:

  • What is the recommended best practice for handling Function App secrets when running within an Aspire application?
  • Are there established patterns or configurations we should be considering to avoid having both local.settings.json and appsettings.json?
  • How can we effectively manage development-specific secrets for the Function App in this Aspire context, given that appsettings.Development.json doesn't seem to be automatically loaded?

We're aware that .NET Aspire's Function App support is currently in preview, so we understand things might evolve. We're just looking for the most robust and maintainable approach for our current setup.

Lastly note that we have settings that must differ for every developer when running locally. This excludes solutions like using App Config and Key Vault (though we can and will use them for common settings).

Any insights, suggestions, or experiences you can share would be greatly appreciated!

Thanks in advance for your help.

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,911 questions
{count} votes

1 answer

Sort by: Most helpful
  1. RithwikBojja 3,055 Reputation points Microsoft External Staff Moderator
    2025-04-16T04:54:05.05+00:00

    Hi @Lindsey Crowe ,

    I use below approach to store the values and get those values in Portal too after deployment:

    Firstly, I store values in appsettings.json. Here I maintain all the values in this file regardless of local/deployment:

    
        {
    
          "RithOption": {
    
            "Name": "Rithwik"
    
          }
    
        }
    
    

    enter image description here

    In Function's Program.cs:

    
        using Microsoft.Azure.Functions.Worker.Builder;
    
        using Microsoft.Extensions.Hosting;
    
        using Microsoft.Extensions.Configuration;
    
        using Microsoft.Extensions.DependencyInjection;
    
        using FunctionApp9;
    
        
    
        var host = new HostBuilder()
    
                       .ConfigureFunctionsWebApplication()
    
                       .ConfigureAppConfiguration((context, builder) =>
    
                       {
    
                           builder.SetBasePath(context.HostingEnvironment.ContentRootPath)
    
                                  .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
    
                                  .AddJsonFile($"appsettings.Development.json", optional: true)
    
                                  .AddEnvironmentVariables();
    
                       })
    
                       .ConfigureServices((context, services) =>
    
                       {
    
                           var configuration = context.Configuration;
    
        
    
                           services.AddLogging();
    
                           services.Configure<RithOption>(configuration.GetSection(nameof(RithOption)));
    
                       })
    
                       .Build();
    
        
    
        host.Run();
    
    

    Function's csproj:

    
        <Project Sdk="Microsoft.NET.Sdk">
    
          <PropertyGroup>
    
            <TargetFramework>net9.0</TargetFramework>
    
            <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    
            <OutputType>Exe</OutputType>
    
            <ImplicitUsings>enable</ImplicitUsings>
    
            <Nullable>enable</Nullable>
    
          </PropertyGroup>
    
          <ItemGroup>
    
            <FrameworkReference Include="Microsoft.AspNetCore.App" />
    
            <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
    
            <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
    
            <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.0" />
    
            <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.0" />
    
          </ItemGroup>
    
          <ItemGroup>
    
            <ProjectReference Include="..\FunctionApp9.ServiceDefaults\FunctionApp9.ServiceDefaults.csproj" />
    
          </ItemGroup>
    
          <ItemGroup>
    
            <None Update="host.json">
    
              <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    
            </None>
    
            <None Update="local.settings.json">
    
              <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    
              <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    
            </None>
    
        	  <None Update="appsettings.json">
    
        		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    
        	  </None>
    
        	  <None Update="appsettings.Development.json">
    
        		  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    
        		  <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    
        	  </None>
    
          </ItemGroup>
    
          <ItemGroup>
    
            <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
    
          </ItemGroup>
    
        </Project>
    
    

    Added these 2 in above csproj:

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

    Function1.cs:

    
        using Microsoft.AspNetCore.Http;
    
        using Microsoft.AspNetCore.Mvc;
    
        using Microsoft.Azure.Functions.Worker;
    
        using Microsoft.Extensions.Logging;
    
        using Microsoft.Extensions.Options;
    
        using System.Text.Json;
    
        
    
        namespace FunctionApp9
    
        {
    
            public class Function1
    
            {
    
                private readonly ILogger<Function1> _logger;
    
                private readonly RithOption _options;
    
        
    
        
    
                public Function1(ILogger<Function1> logger, IOptions<RithOption> options)
    
                {
    
                    _logger = logger;
    
                    _options = options.Value;
    
        
    
                }
    
        
    
                [Function("Function1")]
    
                public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req)
    
                {
    
                    _logger.LogInformation("C# HTTP trigger function processed a request.");
    
                    var json = JsonSerializer.Serialize(_options);
    
                    return new OkObjectResult(json);
    
                }
    
            }
    
            public class RithOption
    
            {
    
                public string Name { get; set; }
    
            }
    
        }
    
    Program.cs in  functionapp9.apphost:
    
        var builder = DistributedApplication.CreateBuilder(args);
    
        builder.AddAzureFunctionsProject<Projects.FunctionApp9>("functionapp9"); 
    
        builder.Build().Run();
    
    

    Output:

    Locally:

    enter image description here

    After Deployment:

    enter image description here

    By this way, I am able to send values to Azure function after deploying. If you want to just use only local.settings.json file, then the values will be not loaded to portal, so I have used appsettings.json.

    Hope this helps.

    If the answer is helpful, please click Accept Answer and kindly upvote it. If you have any further questions about this answer, please click Comment.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.