Let's consider the following class that I'm using for a configuration for my Azure function:
internal class Configuration
{
private readonly IConfigurationRoot config;
public Configuration(ExecutionContext context)
{
config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
public bool MyFlag
{
get => bool.TryParse(config[nameof(MyFlag)], out var value) ? value : false;
set => config[nameof(MyFlag)] = value.ToString();
}
}
The function can easily read the MyFlag property out of the app settings.
But I want my function to be able to set the value of the MyFlag property in azure function app settings as well. Unfortunately, the value doesn't get changed on both Azure and local environments.
I tried to bind the property like this
config.Bind("Values", this);
in the Configuration class constructor and it works but only on local environment. However, it doesn't work on Azure environment.
Is it possible to store the value to the app settings from azure function?
You need to use the IConfiguration class to be able to retrieve values from the appSettings config or also can be called as your local.settings.json.
To access you myFlag you can do this
public MyService(IConfiguration configuration){
// For a section
var emailConfig = configuration.GetSection("Email");
// For a single value
var myFlagVal = config["MyFlag"];
}
Another post with a solution exists here : Save Changes of IConfigurationRoot sections to its *.json file in .net Core 2.2
Hope it answers to your question as expected...
Related
I am a new a developer in terms of dealing with creating an Azure functions App. Since we have different environments for deployment, we use environment based appsettings.json files to load the correct values from the Azure Key Vault. I followed the tutorial here: https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection for adding appsettings.json files. Here is what my Startup class looks like.
[assembly: FunctionsStartup(typeof(Trialtimer.FunctionApp.Startup))]
namespace Trialtimer.FunctionApp
{
public class Startup: FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
var config = builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json"), optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<MyOptions>().Configure<IConfiguration>((options, configuration) =>
{
var section = configuration.GetSection("MyOptions");
section.Bind(options);
});
}
}
}
My appsettings.json file is empty while my appsettings.build.json file has the following, based on the tutorial above
"MyOptions": {
"MyCustomSetting": "Foobar"
}
My MyOptions class is the following
public class MyOptions
{
public string MyCustomSetting { get; set; }
}
My main class which houses the Azure Timer function is the following
public class TestTimerFunction
{
private readonly MyOptions _settings;
public TestTimerFunction(IOptions<MyOptions> options)
{
_settings = options.Value;
}
[FunctionName("AzureTimerFunction")]
public async Task Run([TimerTrigger("*/3 * * * * *")]TimerInfo myTimer, ILogger log)
{
var option = _settings;
log.LogInformation($"Renew function executed at: {DateTime.Now} successfully with {option.MyCustomSetting}");
}
}
The problem i'm having is, when i run the app, the "MyCustomSetting" variable is null. If i add the same json block that is in my appsettings.build.json file to the local.settings.json file, it looks like it gets read correctly and "MyCustomSetting" has the value "FooBar".
The ASPNETCORE_ENVIRONMENT variable is also correctly returning "build". I've checked to make sure that both the appsettings.json files are "Copy if Newer" and when hovering over the "configuration" variable in the "Configure" method, i see that my appsettings.build.json file there. How can I make the function app bind the block from appsettings.build.json rather than local.settings.json? Am i doing something incorrectly?
Got it to work by swapping out the
builder.Services.AddOptions<MyOptions>().Configure<IConfiguration>((options, configuration) =>
{
var section = configuration.GetSection("MyOptions");
section.Bind(options);
});
with
builder.Services.Configure<MyOptions>(builder.GetContext().Configuration.GetSection("MyOptions"));
I don't know why this worked while the way that was described in the tutorial did not. But this has resolved my issue so I'm marking the question as answered.
I have the following code.
IConfigurationRoot config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", true, true)
.Build();
string beep = config.GetSection("beep").Value;
My config.json looks like this.
{ "beep": "bopp" }
When I hit a breakpoint I can see that there's one provider but the data in it is of zero length. I've tried different approaches as config["beep"] etc. but sems to fail to get the value in. It's null all the time. I'm trying to follow the docs but must be missing something.
Make sure the json file is set to copy to directory as discussed in this blog. Otherwise when you build or debug the configuration file won’t be included. Make sure you change it in the properties.
The reason you are not able to access your configuration, is because the IConfigurationRoot does not reference the dependency for ConfigurationBuilder. To ensure that your configuration content will load, would be to do something along these lines:
public static class ConfigurationProvider
{
public static IConfiguration BuildConfiguration => new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true, true)
.Build();
}
The above will build our configuration, now we should use the configuration.
public static class ServiceProvider
{
public static IServiceProvider BuildServiceProvider(IServiceCollection services) => services
.BuildDependencies()
.BuildServiceProvider();
}
Once we have defined our provider, we can do the following so we can pass our IConfiguration around the application to access the objects.
var serviceCollection = new ServiceCollection()
.AddSingleton(configuration => ConfigurationProvider.BuildConfiguration());
var serviceProvider = ServiceProvider.BuildServiceProvider(serviceCollection);
Then inside of another class, you would have the following:
public class SampleContext : ISampleRepository
{
private readonly string dbConection;
public SampleContext(IConfiguration configuration) => configuration.GetConnectionString("dbConnection");
...
}
Then our appsettings.json would look like this:
{
"ConnectionStrings": {
"dbConnection": "..."
}
}
The above is the correct format for a json object. If you have a nested object along these lines:
{
"Sample" : {
"Api" : {
"SampleGet" : "..."
}
}
}
Then your C# would be:
configuration.GetSection("Sample:Api")["SampleGet"];
The above is based on the assumption your configuration and usage are not in the same sequential area, ie directly in your main. Also you should use appsettings.json since that is the default, less extra wiring if I remember correctly. Your json also needs to be correctly formatted.
But that will definately work, if you need more help let me know and I can send you some sample console core applications to demonstrate usage.
I feel like you are just missing the name for the object.
Try adding a name for the object in the config.json like so:
{"beep":{"beep":"bopp"}}
Then you can do string beep =config.GetSection("beep").Value
I build an Azure Function App (v2). Configuration tasks necessary for all functions are done in a Setup class that is structured like the following:
[assembly: WebJobsStartup(typeof(Startup))]
internal class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
Configuration = new ConfigurationBuilder()
.SetBasePath(<functionAppDirectory>)
.AddJsonFile("local.settings.json")
.Build();
builder.AddDependencyInjection(ConfigureServices);
}
public IConfiguration Configuration { get; set; }
private void ConfigureServices(IServiceCollection services)
{
var connection = Configuration.GetConnectionString("<myconnection-string>");
...
}
}
In ConfigureServices I want to read a connection string from a configuration file. For that the function app base folder has be specified with SetBasePath. But I found no way to get access to this path. According to https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function an ExecutionContext can be injected in a function, which contains the path need. But how do I access ExecutionContext in my Startup class?
You can use this piece of code in your startup file.
I have just tested it today for my project and it works on both cloud and local.
var executioncontextoptions = builder.Services.BuildServiceProvider()
.GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
TL;DR: just use Environment.GetEnvironmentVariable.
The ConfigurationBuilder approach shows up in a lot of blog posts, and worked up until we started doing DI. But there is no context parameter, so ConfigurationBuilder immediately starts to cause some strain.
I think people went this direction because in Azure Functions 2, we switched to ASP.NET Core configuration which caused ConfigurationManager to stop working. ConfigurationBuilder was a reasonable place to land. It felt congruent with MVC, and worked fine up until the introduction of DI.
But now that we are doing DI, it's becoming clear that Environment.GetEnvironmentVariable might have been the better choice all along for this platform... There's less code overhead, and it maps cleanly to the configuration model of Azure Functions: in dev, it picks up items in the local.settings.json > Values array, and in production it picks up your environment variables, and it just works.
It is different than what we do in MVC. Until these platforms come into closer parity, however, we should do what makes sense in Functions, rather than trying to force solutions from MVC.
So:
[assembly: WebJobsStartup(typeof(StartUp))]
namespace Keystone.AzureFunctions
{
public class StartUp : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var connectionString = Environment.GetEnvironmentVariable("KeystoneDB");
// Configure EF
builder.Services.AddDbContext<KeystoneDB>(options => options.UseSqlServer(connectionString));
}
}
}
And your local.settings.json might look like this:
{
"IsEncrypted": false,
"Values": {
"KeystoneDB": "[CONNECTION STRING HERE]"
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
You can also use Key Vault with Environment. It works great.
Greeting,
I found a solution that works in the Startup :
var fileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);
string path = fileInfo.Directory.Parent.FullName;
var configuration = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.SetBasePath(path)
.AddJsonFile("appsettings.json", false)
.Build();
You might want to use FunctionsStartupAttribute and IFunctionsHostBuilder from Microsoft.Azure.Functions.Extensions, for example:
[assembly:FunctionsStartup(typeof(SampleFunction.FunctionsAppStartup))]
namespace SampleFunction
{
public class FunctionsAppStartup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
string appRootPath = builder.GetContext().ApplicationRootPath;
// ...
}
}
}
The only workaround I found for configuration builder in Startup() method is to use hardcoded path "/home/site/wwwroot/"
var config = new ConfigurationBuilder()
.SetBasePath("/home/site/wwwroot/")
.AddJsonFile("config.json", optional: false)
.Build();
System.Environment.CurrentDirectory does not work in Azure. Though it works locally. But in Azure it gives an error: The configuration file 'config.json' was not found and is not optional. The physical path is '/config.json'. And function does not start.
Try using Environment.CurrentDirectory
I added a CustomSettings section keys in appSettings.json in ASP.NET Core project:
{
"ConnectionStrings": {
"DefaultConnectionString": "Data Source=..."
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"CustomSettings": {
"Culture": "es-CO"
}
}
I've not been able to load Culture key in following controller:
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILogger<AccountController> logger,
IConfiguration configuration)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(configuration.GetSection("CustomSettings")["Culture"])),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
}
No matter if I do following, always they return NULL:
configuration.GetSection("CustomSettings")["Culture"];
configuration.GetSection("CustomSettings").GetValue("Culture");
I tried help based in ASP.NET Core: Step by Step Guide to Access appsettings.json in web project and class library and I've created CustomSettings class with string Culture property and injecting in Startup as follows:
// Load Custom Configuration from AppSettings.json
services.Configure<Models.CustomSettings>(Configuration.GetSection("CustomSettings"));
Accesing by inject IOptions customSettings, the value of
customSettings.Value.Culture returns NULL.
First Question: ¿What am I doing wrong or what is missing?
Second Question: ¿Why doing following in Index of HomeController throws an exception?
public class HomeController : Controller
{
public IActionResult Index(IConfiguration configuration)
{
}
}
Exception:
An unhandled exception occurred while processing the request.
InvalidOperationException: Could not create an instance of type 'Microsoft.Extensions.Options.IOptions`1[[OmniMerchant.Models.CustomSettings, OmniMerchant, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(ModelBindingContext bindingContext)
Third Question: I need to set Culture from Starting for all the app in background based on Culture property on appSettings.json, I read MSDN documentation, but I've not been able to achieve that, ¿How can I achieve this?
Thanks
First create the modal that matches the appsetting section
public class CustomSettings
{
public string Culture { get; set; }
}
Then register it in the ConfigureServices method in Startup.cs
services.Configure<CustomSettings>(Configuration.GetSection("CustomSettings"));
Then inject it using IOptions where its needed
AccountController(IOptions<CustomSettings> settings)
{
_settings = settings.Value;
}
Why configuration section values are null?
By default there are two config files. For Release build and one for Debug. Have you checked that you actually editing the correct one (probably appsettings.Development.json)
Why DI is not working.
In .NET Core basically you can use DI in two ways. By injecting it in constructor or directly in method. In the second option you have to use special attribute [FromServices]
In your application properties -> Debug section -> Environment variables
If this is set
ASPNETCORE_ENVIRONMENT: Development
It will use appsettings.Development.json
TL:DR; Your appsettings.json file needs to be in the same working directory as your dll file.
You need to make sure that you are running the app from it's working directory.
For example, if your dll's are built to the bin folder, the following won't work:
cd bin
dotnet run app.dll
Because the appsettings.json file is not in the same folder as the dll that you are running from.
However if you are in the directory that the appsettings.json file is in, the current working directory will be set and that file will be read.
dotnet run bin/app.dll
If using VS Code launch.json you can set the cwd property to achieve this.
I had problems reading from different configuration files until I added each of them specifically in the Startup constructor like below:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json") //***
.AddJsonFile("appsettings.development.json") //***
.AddEnvironmentVariables();
Configuration = builder.Build();
}
This is a bug I think but probably you can solve it by setting "HostingEnvironment.ContentRootPath" manualy.
Try to add the following code in the Startup method in Startup.cs:
if (env.EnvironmentName== "Production")
{
env.ContentRootPath = System.IO.Directory.GetCurrentDirectory();
}
or hardcode path like this:
if (env.EnvironmentName == "Production")
{
env.ContentRootPath = "/var/aspnetcore/...";
}
For example if your files located in "/var/aspnetcore/my_ASP_app", the startup method should be something like this:
public Startup(IHostingEnvironment env)
{
if (env.EnvironmentName== "Production")
{
//env.ContentRootPath = "/var/aspnetcore/my_ASP_app";
env.ContentRootPath = System.IO.Directory.GetCurrentDirectory();
}
//env.ContentRootPath = System.IO.Directory.GetCurrentDirectory();//you can write this line outside of IF block.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true);
Configuration = builder.Build();
}
It is better to use Directory.GetCurrentDirectory() instead of hardcoding.
This worked for me in Linux Ubuntu with nginx, but I don't know if it is applicable for your enviornment.
I'm new to Azure's function... I've created a new timer function (will be fired every 30 minutes) and it has to perform a query on a URL, then push data on the buffer..
I've done
public static void Run(TimerInfo myTimer, TraceWriter log)
{
var s = CloudConfigurationManager.GetSetting("url");
log.Info(s);
}
And in my function settings I've
What am I doing wrong?
Thanks
Note that for Azure Functions v2 this is no longer true.
The following is from Jon Gallant's blog:
For Azure Functions v2, the ConfigurationManager is not supported and you must use the ASP.NET Core Configuration system:
Include the following using statement:
using Microsoft.Extensions.Configuration;
Include the ExecutionContext as a parameter
public static void Run(InboundMessage inboundMessage,
TraceWriter log,
out string outboundMessage,
ExecutionContext context)
Get the IConfiguration Root
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
And use it to reference AppSettings keys
var password = config["password"]
When debugging locally, it gets the settings from local.settings.json under the "Values" keyword. When running in Azure, it gets the settings from the Application settings tab.
You can use System.Environment.GetEnvironmentVariable like this:
var value = Environment.GetEnvironmentVariable("your_key_here")
This gets settings whenever you're working locally or on Azure.
You need to go to Platform Features -> Application settings and add it there.
Add the setting under App settings.
Reading the setting can be done by first adding this at the top:
using System.Configuration;
And then reading a setting with:
string setting = ConfigurationManager.AppSettings["url"];
Where url is your setting key. The setting variable will contain your setting value.
Recommended way — use environment variable to read settings
string Secret = System.Environment.GetEnvironmentVariable("Secret");
This works perfect if you run from your local PC or from Azure Functions using C#
You don't have to do step 3 anymore. Just do the following in your Startup.cs
`
Startup.cs
public IConfiguration Configuration { get; }
public Startup() { }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
Then use DI in your code file:
`
YourCode.cs
public class SomeWorker : ISomeWorker
{
private readonly IConfiguration _configuration;
public SomeWorker(IConfiguration configuration)
{
_configuration = configuration;
}
public void bool ForKicks()
{
// Get Value
var val = _configuration["SomeValueInLocalSettingsJsonFile"];
}
// Other code here...
}
For those who needs to do it in python :
import os
os.environ['ENVIRONMENT_VARIABLE']