Centrally manage your App Configurations

10 minute read

The application landscape in Azure has grown significantly in recent years, with a wide range of tools and services available to help businesses build, deploy, and manage their applications in the cloud. From infrastructure as a service (IaaS) offerings like virtual machines and storage to the platform as a service (PaaS) offerings like Azure App Service and Azure Functions, Azure provides a comprehensive set of tools and services to meet the needs of businesses of all sizes.

This also has an impact on the application landscape of businesses within Azure. As Azure also evolves, the applications/services that are used evolve. This is not always going in a correct manner where there is time to remove technical debt. As the landscape expands, new services are created, and configurations are added.

This also surfaces a problem: when a configuration needs to be changed, this must be done on multiple locations, and you are bound to forget one.

Azure App Configuration

This is where Azure App Configuration comes in. Azure App Configuration is a fully managed service that lets you centralize your application's configuration and feature management. It helps to store and manage configuration data and feature flags in a centralized location, which multiple applications and environments can access.

One of the key benefits of using Azure App Configuration is that it allows you to manage the configuration of your applications in a consistent and organized manner. Instead of hardcoding configuration values into your application's codebase, you can store them in Azure App Configuration and retrieve them at runtime. This makes it easier to manage and update your application's configuration without redeploying your code.

App Configuration is already a pervasive solution that (at the time of writing this article) has the following capabilities:

  • Automatic refresh without restarting an application
  • Data encryption (transit/rest)
  • Point-in-time snapshots
  • Configuration comparison
  • Feature Management
  • Import / Export
  • Geo-Replication (preview)
  • Soft Delete
  • AAD authentication
  • Private Endpoint support

App Configuration can be used within many frameworks by using a specific client or provider or by using the Rest API:

  • .Net Core
  • ASP.Net Core
  • .Net Framework
  • Java Spring
  • Javascript
  • Python

When you would like to use it in, for example, PowerShell, you could leverage the API.

KeyVault integration

As the feature list above isn't already enough, Azure App Configuration also has KeyVault integration. With the KeyVault integration, you can add configurations referencing a KeyVault secret. Azure App Configuration will redirect you (your principal with a correct token) to retrieve the value from the KeyVault without noticing anything.

Getting Started

Of course, you can get started by using the Azure Portal, PowerShell, or the CLI, but let's check if we can create the service using Bicep.

Bicep

The bicep for setting up Azure App Configuration is very easy. Let's take a look at the example below.

resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' = {
    name: 'azappconfiguration-${name}'
    location: location
    sku: {
        name: 'standard'
    }
    properties:{
        disableLocalAuth: true
        enablePurgeProtection: true
        softDeleteRetentionInDays:7
    }
}

The code snippet creates a configuration store with the 'standard' SKU, enables purge protection, and sets the soft delete retention to 7 days. Next to that, it also disables the local authentication, meaning that you cannot authenticate to the configuration store by using a key but are required to use a token to authenticate.

Configurations, secret references, and features can also be added by using Bicep. For this, I created a handy module.

param configStoreName string

param configItems array

resource configStore 'Microsoft.AppConfiguration/configurationStores@2021-10-01-preview' existing = {
  name: configStoreName
}

resource configStoreKeyValue 'Microsoft.AppConfiguration/configurationStores/keyValues@2021-10-01-preview' = [for item in configItems:  {
  parent: configStore
  name: (!item.featureFlag) ? item.name : '.appconfig.featureflag~2F${item.name}'
  properties: {
    value: (!item.featureFlag) ? item.value : '{"id": "${item.name}", "description": "", "enabled": false, "conditions": {"client_filters":[]}}'
    tags: item.tags
    contentType:item.contentType
  }
}]

The item will be configured correctly based on the array supplied as a parameter. A sample array for the configuration could look like the snippet below.

[
    {
        name: 'Bicep:Config:Value'
        value: 'Test from Bicep'
        contenttype: ''
        featureFlag: false
        tags: {
            Bicep: 'Deployed'
        }
    }
        {
        name: 'Bicep:Secret:KeyVault'
        value: 'https://azkv-appconfiguration123.vault.azure.net/secrets/bicep-configuration-secret'
        contenttype: 'application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8'
        featureFlag: false
        tags: {
            Bicep: 'Deployed'
        }
    }
    {
        name: 'bicep-featureflag'
        value: ''
        contenttype: 'application/vnd.microsoft.appconfig.ff+json;charset=utf-8'
        featureFlag: true
        tags: {
            Bicep: 'Deployed'
        }
    }
]

Getting it in Code

Adding Azure App Configuration in code is very easy. This article will look into C# and .Net Core 6. Make sure you add the following prerequisites as NuGet packages to your project.

  • Microsoft.Extensions.Configuration.AzureAppConfiguration version 4.1.0 or later
  • Microsoft.Azure.Functions.Extensions version 1.10 or later: This one is needed to incorporate the Azure Functions configurations.

The next step is to add the following code to your application startup.

var endpoint = app.Configuration["AppConfig:Endpoint"];
builder.Configuration.AddAzureAppConfiguration(options => {
    options.Connect(new Uri(endpoint), new DefaultAzureCredential())
            .ConfigureKeyVault(kv => {
                kv.SetCredential(new DefaultAzureCredential());
            })
	     .Select("Demo:*", LabelFilter.Null)
            .ConfigureRefresh(refreshOptions =>
                refreshOptions.Register("Demo:Config:Sentinel", refreshAll: true));
});

On line 1 of the snippet, we retrieve the configuration store's endpoint and then aff Azure App Configuration to the application. By using 'DefaultAzureCredential', we make sure that we connect to the configuration store by the managed identity of the service. On line 4, we then set up the connection to the KeyVault to retrieve values and specify that for this, we also want to use the managed identity.

With the 'Select' we start specifying which configurations we want. In this example, we want all configurations that start with 'Demo:' and do not have a label. Using the labels, we could have specified an environment, for example.

On line 9, we then configure the refresh options to ensure that the application configurations are automatically refreshed when we update the sentinel key "Demo:Config:Sentinel" in the configuration store.

When you would also like to make use of feature management, you would also add the following lines

options.UseFeatureFlags(featureFlagOptions => {
        featureFlagOptions.Select("DemoApp-*", app.Environment.EnvironmentName);
        featureFlagOptions.CacheExpirationInterval = TimeSpan.FromSeconds(30);
    });

Using the configuration

Using the configuration is now very easy the below snippet is a function that retrieves a configuration value.

public class DummyFunction
    {
        private readonly IConfiguration _configuration;

        public DummyFunction(IConfiguration configuration) {
            _configuration = configuration;
        }

        [FunctionName("DummyFunction")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string configKey = "DemoFunc:Message";
            string message = _configuration[configKey];
            
            log.LogInformation($"Found the config in Azure App Configuration {message}");

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

            string responseMessage = string.IsNullOrEmpty(message)
                ? "There is no configuration value with the key 'Demo:FunctionApp:Message' in Azure App Configuration"
                : message;

            return new OkObjectResult(responseMessage);
        }
    }

Also, for features, this more or less looks the same. The only difference here is that we use a so-called FeatureManager. For this snippet, we removed some lines of code for simplicity.

public class WCController : ControllerBase {
       
        private readonly ILogger<WCController> _logger;

        private readonly IFeatureManager _featureManager;

        public WCController(ILogger<WCController> logger, IFeatureManager manager) {
            _logger = logger;
            _featureManager = manager;
        }

        [HttpGet(Name = "GetTeams")]
        public IEnumerable<Teams> Get() {

            IEnumerable<Teams> retVal = new List<Teams>();

            if (_featureManager.IsEnabledAsync("DemoApi-Points").Result) {
               
            } else {
               
            }

            return retVal;
        }
    }

When using features, you also have some other nice options as:

  • Feature Gates: The feature gate attributes make sure something is only available when the specified feature is activated.
    [FeatureGate("DemoApi-WC")]

  • Feature Tag: The <feature> tag ensures that items are shown only when the feature flag is enabled.
    <feature name="DemoApp-Beta">
        <p>Beta feature is enabled!</p>
    </feature>

Azure DevOps

In a real-life scenario, configurations are mostly managed and deployed from Azure DevOps. Azure App Configuration can also help in these situations because Azure DevOps has a task that retrieves configuration values and converts them to variables.

Take a look at the pipeline in the below snippet. In the first steps, the configuration is retrieved and later displayed with a PowerShell task. Good to mention here as well is that KeyVault references are also retrieved and specified as secure variables.

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:

- task: AzureAppConfiguration@5
  displayName: Get Azure App Configurations
  inputs:
    azureSubscription: 'sub-sub-sub'
    AppConfigurationEndpoint: 'https://azapp-sub.azconfig.io'
    KeyFilter: 'DevOps:*'
- task: PowerShell@2
  displayName: Display values from App Configuration
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "Regular value: $(DevOps:DemoValue)"
      Write-Host "Secret value: $(DevOps:Secret:DevOpsSecret)"

My Take away’s

  • Issues in the configuration can cause the application to break on start-up: As configurations are loaded at the application's startup, problems with the configuration or lack of permissions can break the complete application.
  • Do not forget to authorize the application in the Azure Key Vault: When working with KeyVault references, ensure that the identity used has the appropriate permissions on Azure App Configuration and the KeyVault.
  • Use labels to sort/group configurations: Labels add the ability to have different configurations for different scenarios like environments.
  • RBAC permissions can take time before they are in place.
  • Case Sensitive: All configurations added are case sensitive.

Conclusion

In conclusion, Azure App Configuration is a powerful service for managing your applications' configuration and feature management. Centralizing your configuration data and providing feature management capabilities helps you build more flexible and maintainable applications.

If you want to see the code in more detail and look at different examples, go check out my GitHub repo.

Chrismas Present

As a small present for Christmas, I also wanted to share an option we use very often. That option is using Azure App Configuration from PowerShell.

Function Get-AzAppConfigurationKey {
    Param(
        [parameter(Mandatory = $true)][string]$AppConfiguration,
        [parameter(Mandatory = $true)][string]$Key
    )