Azure App Service Application Settings not overriding appsettings.json values - azure

I have a React SPA with .Net Core WebAPI backend being deployed to Azure App Service. When developing locally i use a appsettings.json file to configure the WebAPI backend. I am setting a key/value pair for an external api called "XyzApi".
{
"XyzApi": "https://somedomain/api"
}
In a WebAPI controller I access this config setting using the Configuration provider.
var xyzApiUrl = Configuration["XyzApi"]
This works fine.
When I deploy the app to the App Service I need to change the the value of "XyzApi" to override the value from the appsettings.json with a new value. I have added a key/value pair named "XyzApi" in the App Service Application Settings section in Azure.
In the App Service documentation for Application Settings it says the value will be exposed to the configuration as an Environment Variable with the name "APPSETTING_XyzApi". It also says that it is supposed to override the value from the appsettings.json file for the key "XyzApi".
When the app spins up. The issue is that the value for "XyzApi" is still set to the value from the appsettings.json file. It is NOT being overridden. And as expected there is now a new key/value pair for "APPSETTING_XyzApi".
Why is the Azure App Service not overriding the key/value pair from the appsettings.json file with the new value configured in the Azure AppService Application Settings.
Here is the code that configures the Configuration provider.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json",
optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
});
}

All the steps you did is correct, except the appsettings' name in portal. You add "APPSETTING_XyzApi" in Configuration, which is different with "XyzApi" in appsettings.json. It would override only if the name fit.
Here is an example:
1. I set "myKey": "value1" in appsettings.json in local, And show the value on my index page.
2. After publish, I set the value to 123123:
3. The value override:

Related

Azure: Function host is not running

I have a Function App in azure and when I hit the URL of the function app it says "Function host is not running." I have checked the log also in the app insights or in the Azure portal's function app service, it shows the following error message in the function app.
Note: My pipeline's Build & Releases got succeeded, so I am not sure where to check and what is the solution for this. I tried with a new function app but still no luck.
My Startup.cs file to understand How I have referred the config values,
public override void Configure(IFunctionsHostBuilder builder)
{
//var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings:DBConnection");
var serviceProvider = builder.Services.BuildServiceProvider();
_configuration = serviceProvider.GetRequiredService<IConfiguration>();
var appSettingsSection = _configuration.GetSection("AppSettings");
builder.Services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
RuntimeConfig.appsettings = appSettings;
var ConnectionString = RuntimeConfig.appsettings.AppDBConnection;
///builder.Services.AddDbContext<ShardingDbContext>(options => options.UseSqlServer(ConnectionString), ServiceLifetime.Transient);
//builder.Services.AddScoped<ITestService, TestService>();
}
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "local.settings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"{context.EnvironmentName}.settings.json"), optional: true, reloadOnChange: false)
.AddEnvironmentVariables();
}
I am taking the config values as IConfiguration, it works for my local but don't know how to do the same in the server.
while deploying your Function app it neither upload local.settings.json to Azure or nor makes modification on Application Settings based on local.settings.json file.
The Key-value pair related to EIA present in local.settings.json, add the same key-value pair in Azure Function App Configuration > Application Settings in the Portal.
For that we have to manually update the App Settings in portal or if you are using Visual studio, we can update using VS publish panel.
Add Application Settings using Portal
Azure Portal -> Your Azure Function -> Configuration Panel -> Application Settings/Connection Strings (Add your custom configuration)
Add Application Settings using Visual Studio Publish panel
While publishing your azure function Add your Application settings.
In a hosting panel right corner click (...).
Add your app Settings in Manage Azure App Service Settings.
Add your settings like below

Where to store Azure ConnectionString in Azure Functions app

I made an Azure Functions app that connects to a CosmosDB. I made the following class to retrieve a CosmosClient instance:
public static class CosmosClientContext
{
private static readonly CosmosClient CosmosClient = GetCosmosClient();
public static CosmosClient GetCosmosClient()
{
return CosmosClient
//?? new CosmosClient("AccountEndpoint=https://mycosmosdb.documents.azure.com:443/;AccountKey=JkLv....etc;");
?? new CosmosClient("AccountEndpoint=https://localhost:8081/;AccountKey=C2y6...etc");
}
}
So as you can see I'm currently hard coding the ConnectionString in the class, which is obviously not optimal.
I noticed that I have a local.settings.json file in my project. Is that the place to store to local connection string in? And if so, do I have to use a specific key name for this? Or how do I read from it?
And how does this then work when I publish my Azure Functions app?
So how can I make it so that it locally uses my local ConnectionString, and when published it automatically uses the remote ConnectionString?
You store these as environment variables. Locally these will be in the local.settings.json and on Azure they will be in the Application Settings tab under the Configuration blade of your function app in the Azure Portal.
The name of your variable is arbitrary. Your local.settings.json will look something like this:
{
"IsEncrypted": false,
"Values": {
"CosmosDbConnectionString": "[CONNECTION STRING HERE]"
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
Be sure it goes inside of the "Values" section. The you simply access it using the GetEnvironmentVariable method:
Environment.GetEnvironmentVariable("CosmosDbConnectionString");
So something like:
return new CosmosClient(Environment.GetEnvironmentVariable("CosmosDbConnectionString"));
You don't need any sort of logic to switch between dev and prod. Since the environment variables are different if each place, it will automatically pick up the correct connection string.
Note, in the Portal, make sure you use the "Application Settings" section and NOT the "Connection Strings" section. It's confusing, but the Connections String section is only used for Entity Framework on Functions.
If you prefer to access configuration in the idiomatic ASP.NET Core manner with dependency injection, you can use a Startup.cs that looks something like the below. The values are stored in local.settings.json or in hosted App Settings.
[assembly: FunctionsStartup(typeof(MyApp.Functions.Startup))]
namespace MyApp.Functions
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
IServiceCollection services = builder.Services;
// Read configuration
var config = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddEnvironmentVariables().Build();
// Pass configuration values to a custom IServiceCollection extension
services.AddCosmosDb(new CosmosDbOptions
{
ConnectionString = config["CosmosDb:ConnectionString"],
DatabaseId = config["CosmosDb:DatabaseId"]
});
// Configuration access
services.Configure<AzureStorageOptions>(config.GetSection("AzureStorage"));
// Other setup, add more things to services, etc
services.AddMemoryCache();
}
}
}
Then in any function, you can constructor-inject any services configured at Startup, including your Cosmos service instance.

Can I centralize my app settings in multiple .net core web jobs in Azure?

I have an Azure web app with several web jobs in the back-end. Up until now, I have been using traditional .Net to create these web jobs. One of the core aspects of these web jobs is that they all access global configuration properties defined in the Azure portal, as shown below:
And I reference these settings in my traditional .Net app like so:
var appKey = ConfigurationManager.AppSettings["RingCentral_AppKey"];
I am now starting to transition my web jobs to .Net Core. However, the "best practice" for for managing/retrieving app settings like this seems to be in an appsettings.json file within each web job:
And I reference my settings in my .Net Core app like so:
class Program
{
public static IConfiguration StaticConfig { get; set; }
static void Main(string[] args)
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureAppConfiguration((hostContext, config) =>
{
var conf = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();
config.AddConfiguration(conf);
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
}
public class Functions
{
IConfiguration configuration;
public Functions(IConfiguration _configuration)
{
configuration = _configuration;
}
[NoAutomaticTrigger]
public void DoTrigger(ILogger logger, [Queue("messagestarterqueue")] ICollector<string> outputQueueMessage)
{
var appKey = configuration["RingCentral_AppKey"];
}
}
That's all good, but I have to have a separate appsettings.config file in each separate project, each with the same settings repeated. Is there a way to "centralize" theses settings for my .Net Core web jobs? Either create a "global" appsettings.config file that my Azure web app can reference? Or, is there some way that each .Net Core app can reference the "old style" application settings that are currently being used by the traditional .Net web jobs?
Allow me to suggest a bit different approach.
IMHO, the best way you can do it is by utilizing the Azure App Configuration service.
(Check it on GitHub)
It will allow you to call an API to get the configured values, while you can manage it conveniently through the portal.
Actually you could use Environment.GetEnvironmentVariable(your settings key) to get the settings.
After you set the settings in Configuration, they will be saved as environment variables, you could check them in https://yousitename.scm.azurewebsites.net/Env.cshtml.
So of course yo could get them GetEnvironmentVariable, it will show as below pic.

Confusion between .config files and Azure settings on ASP.Net core web app

I have built a project (to provide an API to a SPA web app) as a Asp.Net Core Web Application (.net framework) with the WebApi option (VS 2017 Community). This created a project with an app.config file and no web.config file. I have a section in app.config that I read using System.Configuration.ConnectionManager.ConnectionStrings['MainDb'] in my Startup.cs class which works fine locally.
When I deploy the app to Azure and set a 'MainDb' connection string in the portal, it does not get read by the web app. I have set these via the portal directly and via the settings pane available via the Azure server explorer in VS2017. In the server explorer I can see a web.config file but no app.config file, the web.config file has no connectionstring node but the web app seems to be seeing the connection string that was in app.config when I deployed.
I am somewhat confused as to the interaction between the app.config and the web.config here - where do I need to declare my connection string so that it can be overridden by the Azure portal setting?
Typically in ASP.NET Core we use an appsettings.json file for configuration. Though there are many other options too (XML, user secrets etc.): https://joonasw.net/view/asp-net-core-1-configuration-deep-dive.
So you would have an appsettings.json file like this:
{
"ConnectionStrings": {
"MainDb": "Data Source=.;Initial Catalog=MainDb;Integrated Security=True"
}
}
You can then read it by accessing the IConfiguration object in Startup:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
string connStr = Configuration.GetConnectionString("MainDb);
}
}
GetConnectionString("name") is actually a shorthand for Configuration.GetSection("ConnectionStrings")["name"].

ASP.NET 5 - Azure AD Authentication - Redirect Loop when run with DNX web command - secrets.json

I created a new ASP.NET 5 MVC 6 app (work and school accounts authentication) and I am getting a redirect loop when going to any authenticated page such as http://localhost:5000/Account/Signin. This happens if I publish the app and run web.cmd (DNX) in the command prompt.
What I do know is if I set the hosting environment to Development, the issue is resolved on my dev machine but copying the published app with the same "--ASPNET_ENV Development" setting to any other machine gets the redirect issue.
"commands": {
"web": "Microsoft.AspNet.Server.Kestrel --ASPNET_ENV Development"
},
I can reproduce this consistently. Turning off Authentication also resolves the issue but not helpful for me. Publishing to Azure also works.
Firstly, I would like to know why setting the Hosting Environment to development fixes the redirect issue on my dev machine and secondly, I would like to know why it does not work on any other machine. Hosing in IIS on another machine also gives the redirect loop issue but it works fine in IIS express on my dev machine.
This is easy to reproduce. In VS 2015, create a new ASP.NET 5 web app, choose (work and school accounts authentication) and publish choosing File System. Go to the publish directory and run web.cmd. If it shows Hosting Environment Production, you will likely get the issue. Changing Environment to Development will fix the issue but copying the the published app to another machine will have the same redirect issue even if with the Hosting Environment set to Development.
UPDATE:
I now know that the settings for Azure AD authentication e.g. Authentication:AzureAd:AADInstance are in the secrets.json file %APPDATA%\microsoft\UserSecrets\\secrets.json . I can read them with the command line user-secret list. This secrets.json file is only loaded if the environment is set to Development which answers my first question:
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
The secrets.json file is also not part of the deployment package so that answers my second question of why it does not work when I copy the package to another machine.
I know need to work out how secrets.json is meant to be used in a production environment.
Startup.cs is here as requested:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Authentication.OpenIdConnect;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace TestAzureAD
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.UseCookieAuthentication(options =>
{
options.AutomaticAuthenticate = true;
});
app.UseOpenIdConnectAuthentication(options =>
{
options.AutomaticChallenge = true;
options.ClientId = Configuration["Authentication:AzureAd:ClientId"];
options.Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"];
options.PostLogoutRedirectUri = Configuration["Authentication:AzureAd:PostLogoutRedirectUri"];
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
}
The settings for Azure AD authentication e.g. Authentication:AzureAd:AADInstance are in the secrets.json file %APPDATA%\microsoft\UserSecrets\secrets.json. This would also apply for other authentication types.
The secrets API can be used to manage the secrets. https://github.com/aspnet/Home/wiki/DNX-Secret-Configuration
dnu commands install Microsoft.Extensions.SecretManager
user-secret list
In development on the dev machine, the secrets.json file can be used but on other machines, which wont have the secrets.json file, the settings need to be read from appsettings.json and appsettings.[environmentname].json. Copying the secrets.json settings to appsettings.json resolves the issue as the azure authenication now knows where to go and no longer redirects to itself in a loop.
The startup code shows that the settings are read from appsettings.json and appsettings.[environmentname].json and also from secrets.json in Development environment.
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}

Resources