Function App 未读取 Azure Functions v4 中包含用户机密的 local.settings.json 文件

chr*_*oli 1 .net c# visual-studio azure-functions .net-6.0

我环顾四周,很惊讶没有人问过这个问题,但我正在尝试将用户机密与我的 Azure Function 应用程序(.NET 6,v4)一起使用。该值永远不会填充“我从 Visual Studio 本地运行”。

Function1.cs

public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            var secret = Environment.GetEnvironmentVariable("mysecret");

            return new OkObjectResult($"Secret is: {secret}");
        }
    }
Run Code Online (Sandbox Code Playgroud)

local.settings.json

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet"
  }
}
Run Code Online (Sandbox Code Playgroud)

secrets.json

{
  "mysecret": "test1",
  "Values:mysecret": "test2"
}
Run Code Online (Sandbox Code Playgroud)

FunctionApp1.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <UserSecretsId>24c7ab90-4094-49a9-94ef-c511ac530526</UserSecretsId>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.1" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>
Run Code Online (Sandbox Code Playgroud)

端点返回:“秘密是:”,我希望它返回“秘密是:test1”或“秘密是:test2”

但是,当我将其更改local.settings.json为它时,它会起作用:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "mysecret": "test3"
  }
}
Run Code Online (Sandbox Code Playgroud)

如何将用户机密正确传递到 local.settings.json?我猜它们不会显示为环境变量。

Ogg*_*las 5

总而言之:

可能不是最好的办法,但可以这样解决。当有我想要签入的设置时,我也这样做过几次。但是,如果您确实有可以签入的设置,您可能会重新考虑将它们添加到自己的类中,除非它们应该在部署期间被覆盖。

在我看来,您应该保留local.settings.json而不是尝试将其替换为appsettings.json,因为通常需要一些值才能使其运行如下:

"Values": {
  "AzureWebJobsStorage": "UseDevelopmentStorage=true",
  "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
},
Run Code Online (Sandbox Code Playgroud)

安装 NuGetMicrosoft.Extensions.Configuration.UserSecrets

设置。local.settings.json Copy to Output DirectoryCopy if newer

对于 .NET 5 < ,修改Program.cs为如下所示。已测试至 .NET 7:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureAppConfiguration((hostContext, config) =>
     {
         if (hostContext.HostingEnvironment.IsDevelopment())
         {
             config.AddJsonFile("local.settings.json");
             config.AddUserSecrets<Program>();
         }
     })
    .ConfigureServices((hostContext, services) =>
    {
        var connectionString = hostContext.Configuration.GetConnectionString("DefaultConnection");
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(connectionString));

        var configuration = hostContext.Configuration;
        
        var settings = new Settings();
        configuration.Bind(settings);
        services.AddSingleton(settings);
    }
    )
    .Build();

host.Run();

public class Settings
{
    public ConnectionStrings ConnectionStrings { get; set; } = new ConnectionStrings();
}

public class ConnectionStrings
{
    public string DefaultConnection { get; set; } = "";
    
}
Run Code Online (Sandbox Code Playgroud)

唯一真正需要的是下面的内容,但我喜欢展示现实世界中更完整的示例:

.ConfigureAppConfiguration((hostContext, config) =>
 {
     if (hostContext.HostingEnvironment.IsDevelopment())
     {
         config.AddJsonFile("local.settings.json");
         config.AddUserSecrets<Program>();
     }
 })
Run Code Online (Sandbox Code Playgroud)

您现在可以注入Settings构造函数中的任何内容,如果存在秘密,它们将被覆盖。

public class TimerTrigger
    {
        private readonly ILogger _logger;
        private readonly ApplicationDbContext _dbContext;
        private readonly Settings _settings;

        public TimerTrigger(ILoggerFactory loggerFactory, ApplicationDbContext dbContext, Settings settings)
        {
            _logger = loggerFactory.CreateLogger<TimerTrigger>();
            _dbContext = dbContext;
            _settings=settings;
        }

        [Function("TimerTrigger")]
        public void Run([TimerTrigger("0 */1 * * * *")] MyInfo myTimer)
        {
            if (myTimer is not null && myTimer.ScheduleStatus is not null)
            {
                _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
                _logger.LogInformation($"Next timer schedule at: {myTimer.ScheduleStatus.Next}");
                var defaultConnection = _settings.ConnectionStrings.DefaultConnection;
Run Code Online (Sandbox Code Playgroud)

长答案:

使用 Visual Studio 创建的 ASP.NET Core Web 应用程序dotnet new会为 .NET 7 生成与此类似的代码:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();
Run Code Online (Sandbox Code Playgroud)

WebApplication.CreateBuilder使用预配置的默认值初始化WebApplicationBuilder类的新实例。初始化WebApplicationBuilder (builder)提供默认配置,并在 EnvironmentName 为Development时调用AddUserSecrets

https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-7.0&tabs=windows#register-the-user-secrets-configuration-source

该方法AddUserSecrets位于Microsoft.Extensions.Configuration包的命名空间中Microsoft.Extensions.Configuration.UserSecrets

https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.usersecretsconfigurationextensions.addusersecrets?view=dotnet-plat-ext-7.0

https://www.nuget.org/packages/Microsoft.Extensions.Configuration.UserSecrets/

如果我们看看它是如何Environment variables工作的:

环境变量用于避免将应用程序机密存储在代码或本地配置文件中。环境变量会覆盖所有先前指定的配置源的配置值。

https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-7.0&tabs=windows#environment-variables

鉴于这local.settings.json意味着是本地的,并且.gitignore添加 Azure Functions 项目时创建的默认值会显式忽略它,因此就像秘密一样工作:

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# Azure Functions localsettings file
local.settings.json
Run Code Online (Sandbox Code Playgroud)

应用程序机密不会签入源代码管理。

https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-7.0&tabs=windows#secret-manager

但是,如果您仍然想使用机密管理器工具和机密存储,请阅读我的 TLDR。

示例值:

本地.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "Movies:ServiceApiKey": "--SECRET--"
  },
  "ConnectionStrings": {
    "DefaultConnection": "--SECRET--"
  }
}
Run Code Online (Sandbox Code Playgroud)

秘密.json:

{
  "Movies": {
    "ServiceApiKey": "12345"
  }
}
Run Code Online (Sandbox Code Playgroud)

读数值:

var moviesApiKeyConfig = _config["Movies:ServiceApiKey"];
var moviesApiKeySettings = _settings.Movies.ServiceApiKey;
var moviesApiKeyEnvironmentVariable = Environment.GetEnvironmentVariable("Movies:ServiceApiKey");
Run Code Online (Sandbox Code Playgroud)

正如您所看到的IConfigurationMicrosoft.Extensions.Configuration按预期拾取秘密,但Environment.GetEnvironmentVariable没有。也符合文档的预期。

在此输入图像描述