Configuration Management with Azure App Configuration
For a variety of reasons, you might find yourself changing configuration (settings, connection strings, credentials, etc.) and then having to touch all the applications that consume that configuration. Once that happens, we can start looking at lifting out and centralizing that configuration somewhere else. Microsoft refers to this as the External Configuration Store pattern.
Move configuration information out of the application deployment package to a centralized location. This can provide opportunities for easier management and control of configuration data, and for sharing configuration data across applications and application instances. — Microsoft Architecture Center: External Configuration Store
Using Azure App Configuration as the system of record for configuration can help manage, version and audit that configuration. Other software/services exist (i.e. Vault, Consul) but as most of the services we are building today leverage Azure PaaS Azure App Configuration is a good fit.
There are three patterns we’ve used when building applications and managing their configuration.
- Manually configure per environment
- Configuration on Deploy
- Configuration on Startup
Manually Configure per Environment
We are including this here for completeness but the only acceptable use for manually applying application configuration is prototyping or a VERY small footprint. There is very little traceability here when changes are made, why they were made, and they are less likely to be reproducible.
Configuration on Startup
Using the configuration on startup pattern, the service only has context of an identity, configuration key sets (labels, tags, name filters, etc.) and the configuration endpoint. The service authenticates to the configuration endpoint on startup, retrieves its configuration, and applies before continuing with its boot process.
Pros
- Applying of configuration is “IN YO FACE”
- Changing configuration doesn’t require restart/redeploy
Cons
- Applying of configuration is “IN YO FACE”
- Authenticating with DefaultAzureCredential is often very slow, which is lag for developer feedback loop
Configuration on Deploy
Using the “Config on Deploy” pattern, the configuration for the service is applied when the service is deployed. The service then loads configuration by conventional means (local.appsettings.json, appSettings.json, environment variables, etc.).
Pros
- Configuration is more of Operational concern and less of Development Concern
- Leverages traditional IConfiguration, IOption, and related programming models
- Better traceability for how to resolve configuration
- Managed Identity is not required for configuration (but still worthwhile!)
Cons
- Applying of configuration is a pipeline activity
- Changing configuration requires re-deploy
How to Publish Configuration?
Most configuration is published either from the system, or application, pipelines themselves. For example, a system level resource like an Azure Blob storage location will be created (using Azure Bicep via pipeline) and its key will be published, along with a naming convention for retrieval, to Azure Application Configuration. An application level resource, like an Azure Function, can also publish (again using Bicep via a pipeline) to Azure App Configuration.
How to Consume Configuration?
Consumers of configuration, such as pipelines or services, will read Azure Application Configuration, using the agreed naming conventions, and then apply that configuration using either the Configure on Startup or Configure on Deploy patterns.
What about Local Development?
AZ CLI PUSH/PULL config
Possible Additional Improvements
- Strongly typed configuration via Option. Today, we use
_configuration["config_key"]
frequently. This is likely fine for small, less complex components. However, we have some that might benefit from strongly typing the configuration. Aides in understanding, traceability and refactoring. - Validate configuration as early as possible, not on first access. ie. When the service starts, we could validate that we have all the configuration required for the service to run instead of waiting for methods to be called.