Azure App Configuration: Configuration of .NET Applications

In older solutions I’ve created a service that returns all the different configurations used by different projects in a solution, e.g. URLs to APIs, connection strings, and more. Now with Azure App Configuration a service is offered by Microsoft Azure that makes this easy a lot easier: a service that can be used by your solution. In this article series I’m covering different features of Azure App Configuration. The first article of this series shows how Azure App Configuration can be used in the development and in the production environment with a .NET application.

Settings Gear

Create Azure App Configuration

At first, I’m creating an Azure App Configuration. For the sample app, I’m using the free tier. The free tier is limited to 1000 requests per day and returns a 429 result afterwards, and a maximum of 10 MB for the configuration storage and no SLA. With the production environment a switch to the Standard tier should be done which is about $ 1,20 per day which includes 1 GB storage and 200,000 requests a day.

Create Azure App Configuration

After the resource is created, configurations can be added using the Configuration explorer. A simple key-value configuration I’m adding is using the key app1:configuration1 and a value. Using colons with the key, a hierarchy of configuration values can be created.

Create app configuration values

With the Access keys section, the connection string needs to be retrieved – this string is used to access the Azure App Configuration from .NET. A connection string for read-only keys is enough for the application accessing the configuration values. Write access is not needed in that scenario.

Using the Configuration with .NET Applications

With an ASP.NET Core Web application, the Host class is used by default to access configuration values. By default multiple providers are used: a JSON configuration provider to read the configuration from the file appsettings.json, another JSON configuration provider that accesses the file appsettings.{environment}.json, a configuration provider accessing environmental variables, and a configuration provider using command-line arguments.

The connection string to the Azure App Configuration shouldn’t be put in the configuration file that’s stored with the source code repository. Instead, user secrets can be used during development time. The user secretes configuration provider is added to a .NET application if user secrets are used. User secrets are stored in the user profile – so every developer working in a team on this project needs to configure the user secrets as well.

User secrets can be initialized using the dotnet CLI with dotnet user-secrets init – or with Visual Studio in the Solution Explorer from the context menu Manage User Secrets. The initialization creates a user secrets identifier in the project configuration file: UserSecretsId. To not mess up with configurations of different applications which are all stored in the users profile, a unique identifier is used to only work with user secrets from the profile that map to this identifier.

User secrets can be set with dotnet user-secrets set or changing secrets.json from Visual Studio:

dotnet user-secrets set ConnectionStrings:AzureAppConfiguration "the secret connection string".

The secrets.json file contains this information:

{
  "ConnectionStrings": {
    "AzureAppConfiguration": "the secret connection string"
  }
}

Of course, the secret connection string needs to be changed to the connection string from the Azure App Configuration.

Now, the Azure App Configuration Provider can be added. This provider is available in the NuGet package Microsoft.Extensions.Configuration.AzureAppConfiguration. In the Program class, the method ConfigureAppConfiguration can be used to change the configuration providers. The extension method AddAzureAppConfiguration is used to add the Azure App Configuration Provider. Because this configuration provider itself needs configuration values (the connection string), the configuration is build first to access the configuration values from the preconfigured providers. Taking the connection string from the user secrets, the value is passed to the AddAzureAppConfiguration method:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)           
        .ConfigureAppConfiguration(config =>
        {
           var settings = config.Build();
           var connectionString = settings.GetConnectionString("AzureAppConfiguration");
           config.AddAzureAppConfiguration(connectionString);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

With this in place, configuration can be retrieved from a service or a controller. In the constructor of the HomeController class, the IConfiguration interface is injected:

private readonly ILogger<HomeController> _logger;
private readonly IConfiguration _configuration;

public HomeController(IConfiguration configuration, ILogger<HomeController> logger)
{
    _configuration = configuration;
    _logger = logger;
}

The Configuration1 method makes use of the IConfiguration variable to access the section and the key stored in Azure App Configuration and returns the value.

public string Configuration1()
{
    return _configuration.GetSection("app1")["configuration1"];
}

Instead of using Azure App Configuration in development mode, you might also decide to put all the configuration in the JSON file appsettings.json (or development specific settings to appsettings.Development.json), and all the secrets to user secrets.

Using Azure App Configuration in Production

To use Azure App Configuration in production, a change is necessary as the connection string cannot be retrieved from the user secrets. A good practice to do this is to use Managed Identities with the Azure App Service where the application is published to. The Managed Identity

With the App Service where the Web application will be deployed to, you can enable a System-assigned managed identity. This configuration can be found with the App Service in the portal, in the Settings category, with the Identity configuration:

Create a Managed Identity with the App Service

With the Azure App Configuration, access control needs to be configured to allow access to the managed identity. With the Access control setting of the App Configuration, a role assignment with the role App Configuration Data Reader can be assigned to the system-assigned managed identity of the App Service:

Assign access to the Managed Identity

In the application code now just the endpoint of the Azure App Configuration needs to be configured. There’s no need to configure this with user secrets, as this is not a secret – and this information needs to be accessed from proruction. Putting this into appsettings.production.json is a good option:

{
  "AppConfigEndpoint": "https://dotnetappconfigsample.azconfig.io"
}

The method CreateHostBuilder in the Program class is now changed for the non-Development environment to retrieve the endpoint from the configuration. After this, a different overload of the AddAzureAppConfiguration method is invoked to pass a delegate with options of type AzureAppConfigurationOptions. Using these options, the Connect method is invoked to pass the endpoint and credentials instead of the complete connection string:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)           
        .ConfigureAppConfiguration((context, config) =>
        {
            var settings = config.Build();
            if (context.HostingEnvironment.IsDevelopment())
            {
                var connectionString = settings.GetConnectionString("AzureAppConfiguration");
                config.AddAzureAppConfiguration(connectionString);
            }
            else
            {
                var endpoint = settings["AppConfigEndpoint"];                
                var credentials = new ManagedIdentityCredential();
                config.AddAzureAppConfiguration(options =>
                {
                    options.Connect(new Uri(endpoint), credentials);
                });
            }
        }).ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Publishing the Web application to the Microsoft Azure AppService, the Web app retrieves the configuration from the Azure App Configuration.

Take away

Configuration management for a complete solution can be in one place. There’s no need to create a custom service for this functionality. Azure App Configuration already covers this – and it seamless integrates with the configuration providers of .NET.

Other important features of Azure App Configuration are that Azure Key Vault can be used behind the scenes, and Feature Flags. These are topics of upcoming articles.

If you’ve read this far, please consider buying me a coffee which helps me staying up longer and writing more articles.

Buy Me A Coffee

You can get the complete sample code.

Enjoy learning and programming!

Christian

Links

It’s all in the Host Class – Dependency Injection with .NET

Configuration with .NET Core

More information on C# and programming .NET Core applications is in my book Professional C# 7 and .NET Core 2.0, and in my workshops.

Settings gear image ID 140276272 © Mungkorn Lasonthi | Dreamstime.com