Creating new .NET apps, or modernizing existing ones? If you’re following the 12-factor criteria, you’re probably keeping your configuration out of the code. That means not stashing feature flags in your web.config file, or hard-coding connection strings inside your classes. So where’s this stuff supposed to go? Environment variables are okay, but not a great choice; no version control or access restrictions. What about an off-box configuration service? Now we’re talking. Fortunately AWS, and now Microsoft Azure, offer one that’s friendly to .NET devs. I’ll show you how to create and access configurations in each cloud, and as a bonus, throw out a third option.
.NET Core has a very nice configuration system that makes it easy to read configuration data from a variety of pluggable sources. That means that for the three demos below, I’ve got virtually identical code even though the back-end configuration stores are wildly different.
AWS
Setting it up
AWS offers a parameter store as part of the AWS Systems Manager service. This service is designed to surface information and automate tasks across your cloud infrastructure. While the parameter store is useful to support infrastructure automation, it’s also a handy little place to cram configuration values. And from what I can tell, it’s free to use.
To start, I went to the AWS Console, found the Systems Manager service, and chose Parameter Store from the left menu. From here, I could see, edit or delete existing parameters, and create new ones.

Each parameter gets a name and value. For the name, I used a “/” to define a hierarchy. The parameter type can be a string, list of strings, or encrypted string.

The UI was smart enough that when I went to go add a second parameter (/seroterdemo/properties/awsvalue2), it detected my existing hierarchy.

Ok, that’s it. Now I was ready to use it my .NET Core web app.
Using from code
Before starting, I installed the AWS CLI. I tried to figure out where to pass credentials into the AWS SDK, and stumbled upon some local introspection that the SDK does. Among other options, it looks for files in a local directory, and those files get created for you when you install the AWS CLI. Just a heads up!
I created a new .NET Core MVC project, and added the Amazon.Extensions.Configuration.SystemsManager package. Then I created a simple “Settings” class that holds the configuration values we’ll get back from AWS.
public class Settings
{
public string awsvalue { get; set; }
public string awsvalue2 { get; set; }
}
In the appsettings.json file, I told my app which AWS region to use.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"AWS": {
"Profile": "default",
"Region": "us-west-2"
}
}
In the Program.cs file, I updated the web host to pull configurations from Systems Manager. Here, I’m pulling settings that start with /seroterdemo.
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(builder =>
{
builder.AddSystemsManager("/seroterdemo");
})
.UseStartup<Startup>();
}
Finally, I wanted to make my configuration properties available to my app code. So in the Startup.cs file, I grabbed the configuration properties I wanted, inflated the Settings object, and made it available to the runtime container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Settings>(Configuration.GetSection("properties"));
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
}
Last step? Accessing the configuration properties! In my controller, I defined a private variable that would hold a local reference to the configuration values, pulled them in through the constructor, and then grabbed out the values in the Index() operation.
private readonly Settings _settings;
public HomeController(IOptions<Settings> settings)
{
_settings = settings.Value;
}
public IActionResult Index()
{
ViewData["configval"] = _settings.awsvalue;
ViewData["configval2"] = _settings.awsvalue2;
return View();
}
After updating my View to show the two properties, I started up my app. As expected, the two configuration values showed up.

What I like
You gotta like that price! AWS Systems Manager is available at no cost, and there appears to be no cost to the parameter store. Wicked.
Also, it’s cool that you have an easily-visible change history. You can see below that the audit trail shows what changed for each version, and who changed it.

The AWS team built this extension for .NET Core, and they added capabilities for reloading parameters automatically. Nice touch!
Microsoft Azure
Setting it up
Microsoft just shared the preview release of the Azure App Configuration service. This managed service is specifically created to help you centralize configurations. It’s brand new, but seems to be in pretty good shape already. Let’s take it for a spin.
From the Microsoft Azure Portal, I searched for “configuration” and found the preview service.

I named my resource seroter-config, picked a region and that was it. After a moment, I had a service instance to mess with. I quickly added two key-value combos.

That was all I needed to do to set this up.
Using from code
I created another new .NET Core MVC project and added the Microsoft.Extensions.Configuration.AzureAppConfiguration package. Once again I created a Settings class to hold the values that I got back from the Azure service.
public class Settings
{
public string azurevalue1 { get; set; }
public string azurevalue2 { get; set; }
}
Next up, I updated my Program.cs file to read the Azure App Configuration. I passed the connection string in here, but there are better ways available.
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) => {
var settings = config.Build();
config.AddAzureAppConfiguration("[con string]");
})
.UseStartup<Startup>();
}
I also updated the ConfigureServices() operation in my Startup.cs file. Here, I chose to only pull configurations that started with seroterdemo:properties.
public void ConfigureServices(IServiceCollection services)
{
//added
services.Configure<Settings>(Configuration.GetSection("seroterdemo:properties"));
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
}
To read those values in my controller, I’ve got just about the same code as in the AWS example. The only difference was what I called my class members!
private readonly Settings _settings;
public HomeController(IOptions<Settings> settings)
{
_settings = settings.Value;
}
public IActionResult Index()
{
ViewData["configval"] = _settings.azurevalue1;
ViewData["configval2"] = _settings.azurevalue2;
return View();
}
I once again updated my View to print out the configuration values, and not shockingly, it worked fine.

What I like
For a new service, there’s a few good things to like here. The concept of labels is handy, as it lets me build keys that serve different environments. See here that I created labels for “qa” and “dev” on the same key.

I saw a “compare” feature which looks handy. There’s also a simple search interface here too, which is valuable.

Pricing isn’t yet available, no I’m not clear as to how I’d have to pay for this.
Spring Cloud Config
Setting it up
Both of the above service are quite nice. And super convenient if you’re running in those clouds. You might also want a portable configuration store that offers its own pluggable backing engines. Spring Cloud Config makes it easy to build a config store backed by a file system, git, GitHub, Hashicorp Vault, and more. It’s accessible via HTTP/S, supports encryption, is fully open source, and much more.
I created a new Spring project from start.spring.io. I chose to include the Spring Cloud Config Server and generate the project.

Literally all the code required is a single annotation (@EnableConfigServer).
@EnableConfigServer
@SpringBootApplication
public class SpringBlogConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBlogConfigServerApplication.class, args);
}
}
In my application properties, I pointed my config server to the location of the configs to read (my GitHub repo), and which port to start up on.
server.port=8888
spring.cloud.config.server.encrypt.enabled=false
spring.cloud.config.server.git.uri=https://github.com/rseroter/spring-demo-configs
My GitHub repo has a configuration file called blogconfig.properties with the following content:

With that, I started up the project, and had a running configuration server.
Using from code
To talk to this configuration store from my .NET app, I used the increasingly-popular Steeltoe library. These packages, created by Pivotal, bring microservices patterns to your .NET (Framework or Core) apps.
For the last time, I created a .NET Core MVC project. This time I added a dependency to Steeltoe.Extensions.Configuration.ConfigServerCore. Again, I added a Settings class to hold these configuration properties.
public class Settings
{
public string property1 { get; set; }
public string property2 { get; set; }
public string property3 { get; set; }
public string property4 { get; set; }
}
In my appsettings.json, I set my application name (to match the config file’s name I want to access) and URI of the config server.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"spring": {
"application": {
"name": "blogconfig"
},
"cloud": {
"config": {
"uri": "http://localhost:8888"
}
}
}
}
My Program.cs file has a “using” statement for the Steeltoe.Extensions.Configuration.ConfigServer package, and then used the “AddConfigServer” operation to add the config server as a source.
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.AddConfigServer()
.UseStartup<Startup>();
}
I once again updated the Startup.cs file to load the target configurations into my typed object.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<Settings>(Configuration);
}
My controller pulled the configuration object, and I used it to yank out values to share with the View.
public HomeController(IOptions<Settings> mySettings) {
_mySettings = mySettings.Value;
}
Settings _mySettings {get; set;}
public IActionResult Index()
{
ViewData["configval"] = _mySettings.property1;
return View();
}
Updating the view, and starting the .NET Core app yielded the expected results.

What I like
Spring Cloud Config is a very mature OSS project. You can deliver this sort of microservices machinery along with your apps in your CI/CD pipelines — these components are software that you ship versus services that need to be running — which is powerful. It offers a variety of backends, OAuth2 for security, encryption/decryption of values, and much more. It’s a terrific choice for a consistent configuration store on every infrastructure.
But realistically, I don’t care which of the above you use. Just use something to extract environment-specific configuration settings from your .NET apps. Use these robust external stores to establish some rigor around these values, and make it easier to share configurations, and keep them in sync across all of your application instances.
This is awesome. In a way the article mirrors what we’re trying to accomplish. Storing configuration values is an implementation detail separate from the application’s logic (and other implementation details) so it’s nice to have an article that addresses that separately.
Thanks! It’s a piece of advice that hasn’t been super easy to accommodate until recently!
Good writeup; I wasn’t aware of the new Azure App Config offering! It sounds like you can accomplish the same kind of thing with AWS Secrets Manager and Azure Key Vault, but those options cost money. I’m unclear on what the other capabilities are related to secrets that you might want to pay for. Do you have any insight on how these compare?
Thanks for the article. It was much helpful. Could you also tell what if we put JSON as a variable in parameter store? How should we bind it to a model in .net?
What would you suggest in scenarios where you want to have different values for development and production? A database connection string is a simple example. It seems that the AWS Secrets Manager Provider is loaded last in to the Configuration Providers … which means that it will always take priority over the local `appsettings.Development.json`, env vars, console parameters, etc … thoughts on that scenario?
Instead of just calling config.AddWhatever(), you can call config.Sources.Insert(0, new WhateverSource()) to put it at the top of the precedence. I also have the secrets scoped by environment.