cannot read values for few keys from local.settings.json file - c#

I am unable to read the few of the keys from local.settings.json file. Below is the file content.
{
"IsEncrypted": false,
"Values": {
"KeyVaultUrl": "https://mykeyvault.azure.net/",
"SecretKey": "myconnectionstring",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
I used the below code to read these values
var keyVaultUrl = Environment.GetEnvironmentVariable("KeyVaultUrl"); // returns null
var secretKey = Environment.GetEnvironmentVariable("SecretKey"); // returns null
var sample = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); // returns "UseDevelopmentStorage=true"
I am not sure why it returns null for the key I have added. I have set Copy Always in Copy to Output Directory and Build Action to None.
Please assist.

Values you are trying to read, are not written in environment variables. To use config files, use IConfiguration. It can contain environment variables, multiple config files and much more.
It depends on type of your application, if you are using simple console application without hosts and builders, use this, but if you are using some kind of framework use this approach.

An environment variable is a variable whose value is set outside the program, typically through functionality built into the operating system!
Your data in appsettings is just key-value pairs of configuration settings for your application (from it's name - appsettings).
To access data in your appsettings you need to inject IConfiguration to your service (or controller or whatever)
private readonly IConfiguration _configuration;
public ServiceConstructor(public IConfiguration)
=> _configuration = configuration;
and then you can read values from there like this:
_configuration["IsEncrypted"]
and
_configuration["Values:KeyVaultUrl"]

Related

Can we use a single appsettings.json file instead of maintaining multiple versions of appsettings.{environmentname}.json for different environments?

I'm using .NET 6 and I want to achieve 2 things:
Using a single appsettings.json file instead of maintaining multiple versions of appsettings.{environmentname}.json for different environments
Remove hard-coding from the appsettings.{environmentname}.json file to allow (1)
(Currently I'm hardcoding secrets like DB connection string, key vault configration details etc. for different environments in respective appsettings files which I want to remove and instead read the same secret values from Azure Key Vault where I'm already storing the same)
How can I achieve these?
Below code snippet is how I'm maintaining 2 appsettings.{environmentname}.json files(Development & Release):
var builder = WebApplication.CreateBuilder(args);
var hostingEnvironment = builder.Environment;
if (hostingEnvironment.IsDevelopment())
{
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.Development.json", false, true)
.AddEnvironmentVariables();
}
else
{
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.Release.json", false, true)
.AddEnvironmentVariables();
}
Note: I did some research and understood that for (1) we can probably do something like:
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable(
"ASPNETCORE_ENVIRONMENT")}.json", false, true)
.AddEnvironmentVariables();
But then, assuming the fact that the app would be automatically deployed every time leveraging CI/CD, can I set the value of ASPNETCORE_ENVIRONMENT in the pipeline?
As for, (2), I tried removing hardcoded secrets from appsettings.{environment}.json files and configured key vault like below but it doesn't work:
builder.Services.Configure<KeyVaultConfig>(options => builder.Configuration.GetSection("KeyVaultConfig").Bind(options));
if (!string.IsNullOrEmpty(builder.Configuration["KeyVaultConfig:KeyVaultUrl"]))
{
var credentials = new ClientSecretCredential(builtConfig["KeyVaultConfig:TenantID"], builtConfig["KeyVaultConfig:ClientID"], builtConfig["KeyVaultConfig:ClientKey"]);
var secretClient = new SecretClient(new Uri(builtConfig["KeyVaultConfig:KeyVaultUrl"]), credentials);
//builder.Configuration.AddAzureKeyVault...
}
If in the end you are going to use CI/CD you can only need 2 files (appsettings.json and appsettings.Development.json) and the default C# handling of these.
In the development one you hardcode all the configuration you need for dev.
In the standard one, you put keys that would be replaced during the CI/CD pipelines.
for example
in appsettings.development.json you have
"ConnectionStrings": {
"myDb": "Server=myDbServer;Database=myDb;User Id=userDev;Password=myPassword;"
}
in appsettings.json you have
"ConnectionStrings": {
"myDb": "#{connectionString}#"
}
And then replace "#{connectionString}#" with the environment value in the CI/CD pipelines configuration
If you really want to have only one file, you could try Json variable substitution like documented here documented here during the CI/CD pipelines, but the first approach allows you to handle secrets with vault more easily

Parse GUID from windows environment variable with .NET core 3.1 .AddEnvironmentVariables()

I use to set some environment variable in order to overwrite appsettings values that are specific to my dev machine (ie connection strings, local endpoints etc). I set them in User variables for my account for example Variable Name: ConnectionStrings:DbConnectionString, Variable value: ******************
Then I call in Main method (of a .NET core 3.1 web app):
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builderContext, config) =>
{
var env = builderContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
config.AddCommandLine(args);
})
This works pretty fine overwriting appsettings values in connectionstrings and in few custom Options classes, but I can't make it read GUID values. I tried writing them with or without double apices, but they're are ignored, while they're correctly loaded and parsed from appsettings.
If I change the type of the property in my Options class to String then the GUID is loaded as string from env variables as expected.
So, as a workaround, I ended up loading the value in a string property and converting it to GUID in a different property of the Options class.
It works, but I'd like to understand whether there's a cleaner way to do that. Is there a special syntax to write GUID in env variables?
Thanks
After some more tests, I found out the issue was about the syntax of the Guid: while in appsettings file you can write it with hyphens (ie 0713d50d-e508-4212-b174-7582a5c90224), in windows env variable the value has to be written without hyphens and without quotes, either single or double (ie ee1b55626c8148aab62cc67dc7f6010e).
In this way it's correctly read a parsed as Guid (if it's a valid Guid of course)

Preprocessing configurations file before using properties

In my appsettings.json file I have a couple of properties that need to be replaced at runtime.
Example :
"ConnectionStrings": {
"SqlConnection": "ReferenceToActualSqlConnection"
}
The appsettings file is loaded into a IConfiguration file and the value is used further
services.AddDbContext<DatabaseContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("ConnectionStrings:SqlConnection").Value);
});
Except that before actually using it, I want to replace "ReferenceToActualSqlConnection" with the actual value in a separate module that has the IConfiguration file injected from the DI container, so the method above uses the replaced value. As far as I'm concerned the injection goes one way, so I can get the values in the preprocess module, but not get them updated back before usage. The module is registered as:
services.AddTransient<IConfigProcessor, ConfigProcessor>();
Any pointers on how to achieve this?

Configuration.GetSection("SectionName") is always null when using manually added settings.json file to dotnet core console app

I am trying to add settings.json file manually to a .net core 2.1 console application. So I add these NuGet packages to the project:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json
and create the appsettings.json file like this:
{
"Section1": {
"Prop1": "value",
"Prop2": 300
}
}
Finally, I try to get value from the settings file like this:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
_configuration = builder.Build();
var section = _configuration.GetSection("Section1");//section.Value is null
var test = _configuration.GetSection("Section1:Prop1");//it returns the proper value
var model = section as Section1Model;
But, section.Value is null and in consequence, the model is null. If I try to get values like _configuration.GetSection("Section1:Prop1") it returns the correct value. Also, If I call _configuration.GetSection("Section1).GetChildren() it returns a collection of settings. What I did wrong?
P.S: I promise the settings file is copied to the bin folder
ConfigurationBuilder only returns generic IConfiguration instances. If you want a strongly typed model out of that, it has to be bound first. There are several ways of doing that -- Microsoft's own implementation lives in Microsoft.Extensions.Configuration.Binder. This then gives you access to the static class ConfigurationBinder in several ways with extension methods on IConfiguration:
var model = _configuration.GetSection("Section1").Get<Section1Model>();
Oddly enough there's no extension method for directly getting a section into an object, but it would be easy enough to write one.
If you're using dependency injection (Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.Options.ConfigurationExtensions) there'll be a .Configure<Section1Model>() extension method to register this binding.

How can I use config.json to set environment-specific variables for a Visual Studio 2015/DNX project?

I have a Visual Studio 2015 solution made up of projects targeting DNX framework. I have been working locally but I plan to deploy to Azure environments (dev/test/prod). Naturally, the solution uses different database connection strings and other variables dependent on the environment. In the past I made use of cloud configuration files to set these variables, reading them with the CloudConfigurationManager.
I am given to understand that I need to use a config.json file. I have the following code in my Startup.cs file:
public Startup(IHostingEnvironment env, IApplicationEnvironment app)
{
Configuration = new ConfigurationBuilder(app.ApplicationBasePath)
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables()
.Build();
Configuration.Set("ASPNET_ENV", "Development");
}
My config.json file is currently an empty object { }. How do I add variables to this file (syntax?), and how do I access them from code?
Note this line in your startup code:
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true)
This adds an additional JSON config file which is named depending on your environment name. So add a new file to your project called:
config.<environment-name>.json
And set up the details in there, such as:
{
"AppSettings": {
"SomeVariable": "Blah"
},
"Data": {
"YourConnectionString": {
"ConnectionString": "<connection string here>"
}
}
}
For reading the configuration, there's a good answer here: Using IConfiguration globally in mvc6
You can use plain old JSON for that. For example, here's how to define a connection string:
{
"Foo": "Bar"
}
To access the configuration value, you do:
config.Get("username")
or:
config["username"];
You can find more information here: http://docs.asp.net/en/latest/fundamentals/configuration.html

Categories