Azure functions - getting configuration recommended practices - c#

I have created an Azure Function v2. In most articles I people suggest following:
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Does a function hosted on azure also have a local.settings.json file? Will this code snippet get the app settings defined in Azure? Or do you need to change local.settings.json before publish? I thought local.settings.json was a local developer thing?
Is there any recommended practices using above snippet vs
Environment.GetEnvironmentVariable("xx")?

The first way you posted works just fine, both locally and in the cloud.
Note: In Azure, there is no local.settings.json (or should never be). This is only being used when debugging locally. There it is being picked up by the Functions runtime the same way it picks up AppSettings when running in Azure - and the settings are injected into your app as env variables. I have been using this for most of the time.
However, I recently started to switch to the second way, using Environment variables (Environment.GetEnvironmentVariable("xx")). Why? Simply because it is shorter and requires not extra usings etc. Apart from that there is no difference that I am aware of in using either of those ways.

Related

Fallback appsettings.json in asp.net core application

I've an Asp.Net Core application.
When we first launch it, the code check for some variable in it, and if missing, it displays a wizard, which allow our users to configure some stuff. Those configured element are then stored in this appsettings.json(could be SMTP settings, mongo db settings, ...) and the app is restarted to take those into account.
This part work fine. The issue is that this file gets commited by every developer on every change, and some things are referencing things that are local(could be the schema version of our database, or some folder). We tried to put this file in the git ignore list, but since there are some settings(like log level) that are present(and should stay present), the file needs to be presents and its updates are tracked.
So I'm wondering: Is there a way to have some sort of "template" of appsettings.json, like initial.appsettings.json, and when the Asp.Net Core app starts, check if the file is missing, copy it and restart if needed?
Like this we can have this initial.appsettings.json being tracked, and ignore the appsettings.json in our git repository?
After test, the coon value will be overriden by the settings in connectionstrings.json file. You can find it below.
Splitting the files in appsettings.json is a good option for your current situation.
You can divide the configuration in the appsettings.json file into connectionstrings.json, and logging.json according to your requirements.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("connectionstrings.json", optional: true, reloadOnChange: true)
.AddJsonFile("logging.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Load it at startup with the following code. Then you are ignoring which specific files according to your needs.

How can I use EF Core without appsettings.json

I need to use the dotnet ef tools utility, but I need to set a connection string in appsettings.json.
I don't want to store it in appsettings.json, because project is stored in the git repository.
At the moment, I keep launchSettings.json in the EnvironmentVariables section, but this does not help me because dotnet ef tools simply does not see these values. I tried to implement IDesignTimeDbContextFactory but everything is the same. He just doesn't see these values.
I found a solution that requires you to explicitly enter the value of the connection string, but this option also does not work.
The thing is that you should not store any secret value in git/repository at all.
Ideally this connnection string should be in a vault/Secret place which you will have access using another password, so eventually you need to "store" a password somewhere.
There is differnt approaches for this, as mention the most common one will be something like _secret.GetConnectionString() -> this calls to another service which will return the connectionstring.
Other solution you can do (probably better in your case) is to "setup" a default/plain appsettings.json just store the value like connection: xxxxxx and then use environment variables to replace that value. The configuration builder is already built with this in mind.
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables() //This line here
.Build();
Then you can just set up the value in your own machine.
For deploying to production it will depends which software you use to deploy the app, but it will have some way to allow you to add environment variables.
Finally, as an extension of the previous code you can add your "own" json
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.private.json")
.Build();
So doing this, in appsettings.private.json you will only need to write the connectionstring property. You will not need to write the entire document.
and then you can just add a rule on .gitignore to ignore it.

IDataProtector unable to decrypt when application runs as a service

I'm using ASP.NET Core data protection with default DI behavior. That works fine when my ASP.NET application is hosted on IIS. Now I have an application that needs to run as a service. So I'm using Microsoft.Extensions.Hosting.WindowsServices to do the windows service part with our standard
Host.CreateDefaultBuilder(args)
.UseWindowsService()
The BackgroundService then hosts ASP.NET Core with your standard
var builder = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("secrets.json", optional: true, reloadOnChange: true);
}
.ConfigureWebHostDefaults(....)
inside the background service, I can then resolve an instance of IDataProtectionProvider, create a protector, and use it to unprotect my secrets
var dataProtectionProvider = Container.Resolve<Microsoft.AspNetCore.DataProtection.IDataProtectionProvider>();
var protector = dataProtectionProvider.CreateProtector(appName);
var decryptedSecret = protector.Unprocect(some secret)
Now that all works fine as long as I run my application from the CLI. But running it as a service (same file, same location, and of course under the same account), I get an 'invalid payload' exception when I call Unprotect.
I know same path and same account is important, so that's taken care of. I also know that the application can find secrets.json as I wrote some probing code that checks if the file is present and can be read before I even try to unprotect. I'm even checking if the string I'm trying to unprotect is null/empty (which it isn't).
I finally installed a debug build as a service and attached the debugger, and when I look at IDataProtectionProvider, it has a Purpose.. and when running as a service, that's c:\windows\system32. When my app runs from the CLI, it's the path to the exe. So, is there a way to specify the purpose on my own so things behave the same regardless of CLI/Service?
So how can I control the purpose?
So having noted the difference in purpose of the IDataProtectionProvider, I was well on my way of solving this. The solution was to set a static purpose as explained here

How to update a single value in Application settings at runtime in Azure Function App

I'm building my first Function App so if I'm asking a silly question, please keep that in mind :)
I have a need to store some settings at runtime.
In standard desktop apps I can easily update app.config
by using:
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings["UserId"].Value = "myUserId";
config.Save(ConfigurationSaveMode.Modified);
but in Function App I load config using ConfigurationBuilder:
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
and there is no way to update that config object.
When developing locally I can update local.settings.json, but when running on Azure everything is loaded from Application settings (Environment variables).
My question is: how can I update a single value in Application settings that are associated with my Function App?
I found a similar question but the solution there requires registering app in AAD and it is updating all settings, not just one value.
Take a look at the appsettings.json based approach.
Note that in-built support for appsettings.json files is not supported in Azure functions (yet). There is a github issue that tracks this.
There are few ways to do things at present. An example answer here:
Azure Functions, how to have multiple .json config files
There is also a similar solution described at github:
https://github.com/Azure/azure-functions-host/issues/4464#issuecomment-494367524

Azure Function Environment Variables

I'm trying to create a super basic Azure Function, but am having trouble with environment variables. Following various tutorials online,
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
log.Info(config["AzureWebJobsStorage"]);
My local.settings.json looks like this:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "<language worker>",
"AzureWebJobsStorage": "abc123",
"AzureWebJobsDashboard": "abc123",
"MyBindingConnection": "abc123"
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "*"
}
}
When I run this locally, that
log.Info(config["AzureWebJobsStorage"]);
line returns nothing... but when I deploy to Azure I see "abc123" in the console.
If, however, I alter that line to
log.Info(config["Values:AzureWebJobsStorage"]);
Then when I run locally, I see "abc123" but when I deploy to Azure I see nothing.
Is there something I'm missing to be able to reach the environment variables the same way locally vs deployed?
EDIT: To clarify, these settings are configured in the app settings for the function:
Assuming you are using targeting the ~2 runtime for your Azure Functions you can access your configuration values through:
log.Info(Environment.GetEnvironmentVariable("AzureWebJobsStorage", EnvironmentVariableTarget.Process));
Those environmental variables work when you are testing your function locally. However, when you deploy to the Azure Function Portal, you need to setup your variables using their built-in system to handle Environmental Variables.
Copy and past your key-values into the sections that I highlighted in the image below.
<

Categories