Reputation: 898
I currently have an azure function using the ServiceBusTrigger binding
[ServiceBusTrigger("%TopicName%", "%SubscripionName%", Connection = "MyConnection")]
string catclogueEventMsgs, ILogger log, ExecutionContext context)
which uses this local.settings.json file
"Values": {
…
"MyConnection": "Endpoint=sb://testxxxxxxxxxxxxxxxxxx
"SubscriptionName": "testsubscriptionName"
"TopicName": "testtopicName",
}
How do I represent this in the appsettings.json file. Will it be like the below?
"Values": {
"MyConnection": "Endpoint=sb://testxxxxxxxxxxxxxxxxxx
"SubscriptionName": "testsubscriptionName"
"TopicName": "testtopicName",
}
Instead of using a “Values” object can I use eg “MySubs” object like the below?
"MySubs": {
"MyConnection": "Endpoint=sb://testxxxxxxxxxxxxxxxxxx
"SubscriptionName": "testsubscriptionName"
"TopicName": "testtopicName",
}
If its possible to use the above settings, how do I represent this in the ServiceBusTrigger binding? would i change it to this?
[ServiceBusTrigger("%MySubs.TopicName%", "%MySubs.SubscripionName%", Connection = "MySubs.MyConnection")]
string catclogueEventMsgs, ILogger log, ExecutionContext context)
Upvotes: 16
Views: 76453
Reputation: 2958
Add your config to host.json and no need for AddJson just use:
public override void Configure(IFunctionsHostBuilder builder)
{
var apiConfig = builder.GetContext()
.Configuration
.GetSection("AzureFunctionsJobHost:WeatherApiConfig")
.Get<WeatherApiConfig>();
}
Override in local.settings.json using e.g.
"AzureFunctionsJobHost__WeatherApiConfig__WeatherApiKey": "yourkey"
Upvotes: 0
Reputation: 70184
This is how to do it for .NET 5, example with some overriding of secrets as well:
KotobeeSettings:
public class KotobeeSettings
{
public string BaseUrl { get; set; }
public string SerialNumber { get; set; }
}
Program.cs:
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration((hostContext, builder) =>
{
builder.AddJsonFile("local.settings.json");
//This will override any values added to local.settings.json if you wish to check in this file - Recommended approach for keeping secrets in dev is this though:
//https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0&tabs=windows#register-the-user-secrets-configuration-source
//builder.AddJsonFile("secrets.settings.json");
if (hostContext.HostingEnvironment.IsDevelopment())
{
builder.AddUserSecrets<Program>();
}
})
.ConfigureServices((hostContext, services) =>
{
var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:DefaultConnection");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
connectionString,
sqlServerOptions => sqlServerOptions.CommandTimeout(600)));
services.AddHttpClient();
var configuration = hostContext.Configuration;
var settings = new KotobeeSettings();
configuration.Bind("KotobeeSettings", settings);
services.AddSingleton(settings);
services.AddTransient<KotobeeClient>();
services.AddTransient<OrderRowService>();
})
.Build();
CreateDbAndApplyMigrations(host);
host.Run();
}
private static void CreateDbAndApplyMigrations(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
}
}
Example local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
},
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Project.Ebook;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"KotobeeSettings": {
"BaseUrl": "BaseURL",
"SerialNumber": "--SECRET--"
}
}
Example secrets.settings.json and secrets.json:
{
"KotobeeSettings": {
"SerialNumber": "123"
}
}
Settings injected like this:
public class TimerTrigger
{
private readonly OrderRowService _orderRowService;
private readonly KotobeeSettings _settings;
public TimerTrigger(OrderRowService orderRowService, KotobeeSettings settings)
{
_orderRowService = orderRowService;
_settings = settings;
}
[Function("TimerTrigger")]
public async Task RunAsync([TimerTrigger("0 */1 * * * *")] MyInfo myTimer, FunctionContext context)
{
var baseUrl = _settings.BaseUrl;
var logger = context.GetLogger("TimerTrigger");
logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
logger.LogInformation($"Next timer schedule at: {myTimer.ScheduleStatus?.Next}");
}
}
public class MyInfo
{
public MyScheduleStatus ScheduleStatus { get; set; }
public bool IsPastDue { get; set; }
}
public class MyScheduleStatus
{
public DateTime Last { get; set; }
public DateTime Next { get; set; }
public DateTime LastUpdated { get; set; }
}
Upvotes: 3
Reputation: 2793
It looks like a Hybrid of Jason and Oneil/Liam is correct.
From what I can tell, in the name/declaration/definition of the function it has to come from the local.settings.json when developing and referred to like "%TheQueueName%", and then entered in Function Configuration -> Application Settings for Azure.
In the actual function itself, you can leverage json file injection via Startup.cs and then include the values in the function itself.
This deserves a totally separate blog post (and there have been similar, yet not quite as sophisticated setups mentioned, but here is what I've done and figured out after a year plus and works fantastically.
Files setup:
Configuration Setup: Startup.cs
(even though it says /home/site/wwwroot - the files are based off of the root of the project, when published that will be the root)
public override void Configure(IFunctionsHostBuilder builder)
{
var currentDirectory = "/home/site/wwwroot";
var config = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("local.settings.json", optional: false, reloadOnChange: true)
.AddJsonFile("Config/xyz.settings.json", optional: false, reloadOnChange: true)
.AddJsonFile("Config/xyz.settings.dev.json", optional: true, reloadOnChange: true)
.AddJsonFile("Config/secret.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
builder.Services.Configure<XyzSettings>(config.GetSection("XyzSettings"));
What this does and how it works:
Speaking of Secrets:
Local Secrets can be stored in the secrets.json as long as they are not set to publish.
In Azure, it is recommended to store the values in Function App Settings which reach out to Azure Key Vault.
It is brilliant how it is configured. All you do in the Function App Settings is to name the variable what it is in settings, like XyzSettings:SettingName and then refer to the location in Key Vault like follows:
@Microsoft.KeyVault(SecretUri=https://yourvalutname.vault.azure.net/secrets/secret-name/auto-generated-keyvaultguid)
Function File: (using a Queues trigger as an example but operates the same way)
namespace Xyz.Functions
{
public class Azure_Queue_Add_Xyz
{
private readonly XyzSettings _xyzSettings = null;
public Azure_Queue_Add_Xyz(IOptions<AzureSettings> azure_settings)
{
_azureSettings = azure_settings.Value;
}
[FunctionName("Azure_Queue_Add_Xyz")]
public void Run(
[HttpTrigger(AuthorizationLevel.Function, "post",
Route = "HttpTrigger/Azure_Queue_Add_Xyz")] xyzConfig input,
[Queue("%TheQueueName%"), StorageAccount("StorageAccount")] ICollector<string> msgOutput,
ILogger logger,
ExecutionContext context)
{
logger.LogError("{0} is processing a request", context.FunctionName);
logger.LogError("{0} - Queue: {1}", context.FunctionName, _xyzSettings.TheQueueName);
logger.LogError("{0} - CloudQueueMessage: {1}", context.FunctionName, JsonConvert.SerializeObject(input));
msgOutput.Add(JsonConvert.SerializeObject(input));
}
}
}
Upvotes: 1
Reputation: 2672
You CAN indeed read settings outside the Values
array as follows:
public class WeatherApiConfig
{
public string WeatherApiUrl { get; set; }
public string WeatherApiKey { get; set; }
}
New for Azure Functions V2, we have an appropriate way to handle DI as shown below:
[assembly: FunctionsStartup(typeof(BlazingDemo.Api.Startup))]
namespace BlazingDemo.Api
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var apiConfig = new WeatherApiConfig();
config.Bind(nameof(WeatherApiConfig), apiConfig);
builder.Services.AddSingleton(apiConfig);
builder.Services.AddHttpClient();
}
}
}
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
},
"WeatherApiConfig": {
"WeatherApiUrl": "http://api.openweathermap.org/data/2.5/weather",
"WeatherApiKey": "**removed**"
}
}
Note: The key for me was to add
.SetBasePath(Directory.GetCurrentDirectory())
inStartup.cs
since it couldn't find the file without it.
In production I use the function app's Application Settings
section to configure these two properties as follows:
Upvotes: 34