Today, we’ll get the code needed to retrieve the App Configuration service setting values in the application code.
In my last post, Coding Azure 22, I showed how to add settings to an App Configuration service to hold configuration information that needs to be shared among multiple components in a distributed application (the example I used was the name of a queue that would be written to by some frontend application and read from by some backend processor). In this post, I’ll look at the code you need to retrieve those setting values in your application code.
If you’ve worked with retrieving information from a .NET configuration file (e.g., the appsettings.json file in an ASP.NET Core application) through the IConfiguration interface, then you’re already familiar with most of what you need to do. It’s just a matter of integrating your App Configuration service into your configuration settings.
That means that you can treat your configuration file and your App Configuration service (and, if your application is running in an App Service, your App Service’s Environment Variables) as a single source.
Having said that, an App Configuration service provides a lot more options than a plain old JSON file or an Environments settings file. You may want to consider making an App Configuration service as your only source for configuration information.
To enable your application to access your App Configuration service, your application needs two NuGet packages:
You’ll then need to update your application’s Program.cs file to integrate your App Configuration service into your application’s configuration information. Provided you have only one setting for any Key in your service, you just need to call the Configuration object’s AddAzureAppConfiguration, passing the URL for your service (as a Uri object) and some credential object. That could be as simple as this code:
builder.Configuration.AddAzureAppConfiguration(o =>
o.Connect(new Uri("<url for your App Configuration Service>"),
new DefaultAzureCredential())
You can retrieve the URL for your service from your App Configuration’s Overview page. Using DefaultAzureCredential will enable you to run your code from an App Service using the App Service’s Managed Identity and test your code from with Visual Studio or Visual Studio Code (assuming that you’ve added the Azure extension to Visual Studio Code).
In an ASP.NET Core application, to retrieve a value from your merged configuration source, you must first retrieve the IConfiguration object from your application’s services collection. This example from an ASP.NET controller retrieves the service in the controller’s constructor and stores it in a field called config:
private readonly IConfiguration config;
public Worker(ILogger<Worker> logger, IConfiguration config)
{
this.config = config;
With the IConfiguration object in the config field, you could retrieve the value for a setting with its Key set to “QueueName” using this code:
string? qname = config["QueueName"];
In a Razor file you can retrieve the IConfiguration object using the inject directive. This code retrieves the IConfiguration object into a field called config and then uses it to retrieve a setting with a Key set to “AltText”:
@inject IConfiguration config
…
<img alt='@config ["AltText"]' …
If the Key you specify doesn’t exist or there is more than one version of the Key, then both of those sets of code return null. (Because of that, it’s a good practice to always check for null after retrieving configuration values.)
You can have multiple versions of a Key if you’ve taken advantage of the App Configuration service’s ability to have multiple settings with the same Key, each with a different Label (e.g., “production,” “dev”). To avoid retrieving multiple versions of the same key, you’ll need to specify which version of any Key you want to retrieve when configuring your application.
In your Program.cs file, you can specify the version of each Key that you want by configuring AddAzureAppConfiguration through its options object, using the object’s Select method. The Select method accepts two parameters:
KeyFilter.AnyLabelFiler.Null which matches to Labels that have no valueYou can use multiple Selects when configuring AddAzureAppConfiguration. Multiple Selects will not result in duplicate Keys because values retrieved by later Select methods overwrite values retrieved in earlier Selects.
The following example is typical code in a Program.cs in the production version of your application:
Select retrieves settings that don’t have a Label (i.e., settings that are to be used in any environment)Select retrieves settings that have their Label set to “production”builder.Configuration.AddAzureAppConfiguration(o =>
{
o.Connect(new Uri("<url for your App Configuration Service>"),
new DefaultAzureCredential())
.Select(KeyFilter.Any, LabelFilter.Null)
.Select(KeyFilter.Any, "production");
});
Order matters here: If there is a Key that has both a version with no Label and a version with a Label set to "production,” the “production” version will overwrite the values from the version without a Label because the “production” version is selected second.
You can make this code more portable by replacing the hard-coded “production” string with a call to builder.Environment.EnvironmentName (provided, of course, that your environment names match the Labels you’re using in your App Configuration service). Be aware: Labels are case-sensitive: “Production” is not a match to “production.”
You have a different issue if Keys in your App Configuration file duplicate settings in either your application’s configuration file (e.g., appsettings.json) or in an App Service’s Environment Variables (all of which are merged into IConfiguration). Those matching keys form a hierarchy: App Configuration values override Environment Variables which, in turn, override configuration file settings. The danger here is that you may lose a setting in your configuration file because it’s accidentally overridden by a matching Key in your App Configuration service.
By default, all configuration values are treated as strings. You can use the config object’s GetValue method to handle conversions and, as a bonus, provide a default value.
The GetValue method is a generic method, which lets you specify the data type of the value you’re retrieving. You pass it the Key of the setting to retrieve and, optionally, a default value to return if the Key can’t be found or if the Key can’t be converted.
This example converts the setting’s value to an integer or returns -1 if the setting isn’t found or can’t be converted:
int? value = config.GetValue<int>("QueueLength", -1);
If you used the recommended naming convention to group related Keys in your settings, then you retrieve those settings together. These sample Keys group together the WebServiceUrl and AuthorizationKey*n* settings by prefixing them with “SalesOrder”:
SalesOrder:WebServiceUrlSalesOrder:AuthorizationKey1SalesOrder:AuthorizationKey2
You can retrieve their values individually with code like this:
string url = config["SalesOrder:WebServiceUrl"];
string authKey = config["SalesOrder:AuthorizationKey1"];
However, you can also retrieve all the SalesOrder prefixed settings as a single group. You have two options here. One approach treats the SalesOrder settings as a class with properties; the other approach treats the SalesOrder settings as nested arrays.
The first step in treating grouped settings as a class is to define a class with property names that match the members of the section (the class name doesn’t matter but the property names, while not case sensitive, must match the names of the child settings). A typical class for my SalesOrder settings might look like this:
internal class SalesOrderSection
{
public string webServiceUrl {get; set;} = string.Empty;
public string authorizationKey1 {get; set;} = string.Empty;
public string authorizationKey2 {get; set;} = string.Empty;
}
Then, in your Program.cs file, as part of configuring your App Configuration service, you use the Configuration object’s GetSection method to retrieve the SalesOrder prefixed settings as a section. You then use the Services object’s Configure method to both load that section into your class and add that class to your application’s Services collection.
Typical code would look like this:
IConfiguration soSection = builder.Configuration
.GetSection("SalesOrder");
builder.Services.Configure<SalesOrderSection>(soSection);
That code would create an IOptions<SalesOrderSection> object and add it to your application’s Services collection. The Value property of that IOptions object would hold the class with the SalesOrder section’s values.
Typical code in one of your application’s components to pull that object from the Services collection and retrieve the values stored in it would look like this:
private SalesOrderSection soSettings;
public HomeController(IOptions<SalesOrderSection> soSettings)
{
this.soSettings = soSettings.Value;
string url = this.soSettings.webServiceUrl;
Treating the SalesOrder prefixed settings as an array is more flexible but require more code. With this approach, instead of working in your Program.cs file, you use the IConfiguration object’s GetSection method in your application where you would retrieve individual values.
The GetSection method, when passed the prefix that defines your section returns an IConfigurationSection object. You then use that section object’s GetChildren method to convert all the settings in the section into an array of IConfigurationSection objects, each of which has a Key and a Value property (and a GetChildren method if your settings have their own nested settings).
This approach can be especially useful if your settings consist of repeated values because you can loop through the array without having to know how many repeated values there are.
This sample code loops through my SalesOrder settings looking for my Authorization keys:
IConfigurationSection secSO = config.GetSection("SalesOrder");
IEnumerable<IConfigurationSection> childrenSO = secSO.GetChildren();
string? key = string.Empty;
foreach (IConfigurationSection childSo in childrenSO)
{
if ( childSO.Key.StartsWith("Auth") )
{
key = childSO.Value;
…process authorization key…
}
}
Alternatively, you can use LINQ to retrieve specific values, as this example does by using the child object’s Key property:
string? value = childrenSO.FirstOrDefault(c => c.Key == "WebServiceUrl")?.Value;
If the value of your setting is a JSON document and you’ve set the Content type property of the setting to application/json, then you can treat the JSON document as if it were a section.
I could have, for example, created a setting with a Key set to “SalesOrder” and put this JSON document in it (which would also let me define my authorization keys as an array):
{
"WebServiceUrl":"https://…",
"AuthorizationKeys": [
"a1…41",
"4b..ff"
]
}
I can then retrieve individual values as if they were configuration settings in my service using code like this:
string? url = config["SalesOrder:url"]
string? authkey1 = config["SalesOrder:AuthorizationKeys:0"]
string? authkey2 = config["SalesOrder:AuthorizationKeys:1"]
You can also use the IConfigurationSection object with the GetSection and GetChildren methods to process your JSON document as I described earlier.
If you are using Key Vault references in your settings, you’ll need to configure your App Configuration’s service in your application’s Program.cs file to grant permission to access the vault at runtime.
The AddAzureAppConfiguration’s options object has a ConfigureKeyVault method that accepts an options object. You need to pass that options object’s SetCredential method the credentials that allow access to the Key Vault used in the Key Vault reference. Using DefaultAzureCredential will pick up your credentials in Visual Studio or Visual Studio Code for testing purposes and, at runtime, the Managed Identity assigned to the App Service hosting your application in the cloud.
This code would pick up the default credentials and use them for any Key Vault references:
builder.Configuration.AddAzureAppConfiguration(o =>
{
o.ConfigureKeyVault(kvo =>
{
kvo.SetCredential(new DefaultAzureCredential());
});
o.Connect(…
});
You can then retrieve the unencrypted value from the Key Vault like any other setting just by using the setting’s Key like any other configuration setting:
string? value = config["AuthorizationKey"];
If you’re using multiple Key Vaults, the credentials you provide to the ConfigureKeyVault options object will be used for all the Key Vaults. If your Key Vaults require different credentials, you can use that options object’s SetSecretResolver method to specify different sets of credentials for different Key Vaults.
If you’ve created an App Configuration snapshot to hold a collection of settings, you must configure the AddAzureAppConfiguration’s options object to select the snapshot you want to use. That’s done through the options object’s SelectSnapshot method, passing the Key for your Snapshot Reference setting (i.e., not the name of the snapshot itself). Be aware: If the snapshot can’t be found, no exception is raised.
Your snapshot’s settings are merged with the rest of your service’s settings. This can result in duplicate Keys (e.g., there’s a setting with a Key of “QueueName” in both your configuration settings and in the snapshot). If that happens, the last entry in the configuration settings list overrides any earlier settings, regardless of where the setting comes from.
This means that if you want to have your snapshot’s settings to always override your configuration settings then you should give your Snapshot References names that appear late in the alphabet (e.g., begin all your Snapshot reference Key values with “x-”).
Because snapshots are intended to provide a history of revisions made to your settings, you can not replace or edit a snapshot. However, you can change the snapshot used by a Snapshot Reference in your configuration. So, if you want to change the settings used by an existing Snapshot Reference in your configuration settings, then you must:
With the code so far, if the value in a setting in your App Configuration service is changed while your application is running, your application won’t pick up the change.
If you want to keep your application using the current version of the settings in your App Configuration service (or, at least, one that isn’t very far out of date), then you should configure the service to refresh its values at some interval. You can do that by using the AddAzurAppConfiguration’s options object and calling its ConfigureRefresh method, passing a lambda expression that calls the SetCacheExpiration method (that method accepts a TimeSpan object).
The code for doing that is probably easier to understand than the explanation. Here’s some code that configures the configuration service to refresh its values every 10 minutes:
builder.Configuration.AddAzureAppConfiguration(o =>
{
o.ConfigureRefresh(ropt =>
ropt.SetCacheExpiration(TimeSpan.FromMinutes(10)));
For this to be effective, though, in your code you must always retrieve your configuration values from the IConfiguration object. If, for example, you store a string value from the IConfiguration object in a field in your application, that field won’t be updated when your service is refreshed.
If you find that you can’t do what you want by merging your App Configuration service into the rest of your configuration sources, you can create a ConfigurationClient object). The ConfigurationClient object provides methods for managing your App Configuration store, including adding and removing settings and snapshots.
More relevant to this post, the ConfigurationClient also provides GetConfigurationSetting and GetConfigurationSettingForSnapshot methods with multiple overloads that allow you to retrieve a value by any combination of Key, Label and date/time.
This code, for example, retrieves a complete setting by specifying both the Key and the Label:
ConfigurationClient cc = new(new Uri("https://whconfig.azconfig.io"),
new DefaultAzureCredential());
ConfigurationSetting cs = cc.GetConfigurationSettingAsync("QueueName", "production");
And that wraps up the App Configuration service! While App Configuration is essential for distributed applications, its integration with Key Vaults, its support Snapshots and its ability to be automatically refreshed all make it an attractive replacement, I think, for storing configuration information even for standalone applications.
Peter Vogel is both the author of the Coding Azure series and the instructor for Coding Azure in the Classroom. Peter’s company provides full-stack development from UX design through object modeling to database design. Peter holds multiple certifications in Azure administration, architecture, development and security and is a Microsoft Certified Trainer.