Configuration as a Service
In my previous post, I talked a lot about the challenges of application configuration. In this post, I will delve deeper into the complexity and provide example implementations, focusing on the challenge of lack of standardization.
All implementations in this post will use Microsoft technologies, including Azure, .NET Core, Bicep, and related technologies.
"Regular" App Configuration
From the start of a new .NET Core Web API project, we immediately get a simple appsettings.json and appsettings.Development.json file structure.
pwsh dotnet new webapi --name app-x
tree /f /a
\---app-x
| appsettings.Development.json <----- THIS
| appsettings.json <------------------ AND THIS
| Program.cs
| app-x.csproj
| WeatherForecast.cs
+---Controllers
| WeatherForecastController.cs
\---Properties
launchSettings.json
But what are these files, and how can we use them?
- appsettings.json:
- appsettings.json is the main configuration file for your application. It contains configuration settings that apply to all environments and is used to store application-specific settings.
- appsettings.Development.json:
-
appsettings.Development.json is an environment-specific configuration file that overrides settings defined in appsettings.json specifically for the development environment.
-
It allows you to have environment-specific settings and configurations, which can be useful for testing and development purposes.
-
The 'Development' environment is one of the built-in environments in ASP.NET Core, but you can also create your own custom environments if needed.
-
When running the application in the development environment (e.g., using the dotnet run command with no specific environment specified), the settings from appsettings.Development.json will override the corresponding settings from appsettings.json.
While simple appsettings.json files are perfect for small private projects or proof of concept scenarios, they lack certain attributes discussed in my previous post:
- We have an open question about defining and consuming secrets.
- We cannot share settings across applications.
- We have limited abilities to enforce standardization across applications.
- No ability to centrally manage settings across applications.
- File coupling between application and configuration.
Though appsettings.json files serve well in small-scale projects, we could use a more in-depth implementation for complex scenarios.
Azure App Configuration
Enter Azure App Configuration, a cloud-based service offered by Microsoft Azure for managing application configuration settings. It provides a centralized, secure, and scalable solution for storing and accessing configuration data. With features like real-time updates, versioning, and integration with other Azure services, it enables dynamic and efficient configuration management for applications deployed in the cloud.
In my previous post, I mentioned a couple of alternatives to Azure App Configuration, but I will now continue to explore Azure App Configuration.
Azure App Configuration provides a deeper and more flexible approach to handle various configuration implementations. However, it is not without challenges. When we introduce configuration as a service, some immediately raise concerns about the service's availability. And yes, we need to address some of those issues, but we also gain other benefits.
Let's tackle the configuration availability issue.
Configuration Availability
Applications often require configuration at startup (or during scale-up), so when we start an application, the configuration needs to be fetched from a file or, in this case, an external service. The service needs to be available, and if it's not, we encounter problems.
Azure App Configuration Standard tier has a 99.9% availability SLA (at the time of writing). This means the service (in some way) could be offline or degraded for the following durations: Daily: 1m 26s, Weekly: 10m 4.8s, Monthly: 43m 28s.
While this may be acceptable in most cases, we can replicate our configuration to another region entirely. Replication is currently a manual process for this resource (hoping for replication like Cosmos DB in the future).
We can specify a 'Bicep template' to provision App Configuration resources in multiple regions and then consume all endpoints in our applications.
param environmentName string = 'nonprod'
param locations array = [
'swedencentral'
'westeurope'
]
param skuName string = 'standard'
resource appcs 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = [for (location, index) in locations: {
name: 'appcs-dist-cfg-${environmentName}-${padLeft(index + 1, 2, '0')}'
location: location
sku: {
name: skuName
}
properties: {
softDeleteRetentionInDays: 7
}
}]
The above example will provision two App Configurations in two regions: Sweden Central and West Europe, with the names:
apps-dist-cfg-nonprod-01
apps-dist-cfg-nonprod-02
To use them in a .NET application, we can:
configurationBuilder.AddAzureAppConfiguration(options =>
{
var connectionStrings = new string[] {
Environment.GetEnvironmentVariable("APPS_DIST_CFG_NONPROD_01"),
Environment.GetEnvironmentVariable("APPS_DIST_CFG_NONPROD_02") };
options.Connect(connectionStrings);
});
The above code requires that the application is executed with environment variables corresponding to the keys, but this could equally be stored in the appsettings.json files instead (I prefer the diversity of environment variables regarding hosting environments).
Wrapping up
We have now discussed the simplicity of configuration using files and the problems associated with such an approach. We have also explored configuration as a service with Azure App Configuration and the availability issues introduced by separating configuration from our applications, along with the advantages it offers.
We have moved towards lesser complex configuration and towards a standardized solution for configuration.
In future posts, I will delve deeper into Azure App Configuration and demonstrate using the configuration service to store configuration and secrets in a standardized manner.