App configuration support
This article describes the Spring Cloud Azure App Configuration library. This library loads configurations and feature flags from the Azure App Configuration service. The library generates PropertySource
abstractions to match the abstractions already generated by the Spring environment, such as environment variables, command-line configurations, local configuration files, and so on.
Spring is an open-source application framework developed by VMware that provides a simplified, modular approach for creating Java applications. Spring Cloud Azure is an open-source project that provides seamless Spring integration with Azure services.
Prerequisites
- An Azure subscription - create one for free.
- Java Development Kit (JDK) version 8 or higher.
- Apache Maven
- Azure CLI
Set up your App Configuration store
Use the following command to create your Azure App Configuration store:
az appconfig create \
--resource-group <your-resource-group> \
--name <name-of-your-new-store> \
--sku Standard
This command creates a new, empty configuration store. You can upload your configurations by using the following import command:
az appconfig kv import \
--name <name-of-your-new-store> \
--source file \
--path <location-of-your-properties-file> \
--format properties \
--prefix /application/
Confirm your configurations before loading them. You can upload YAML files by changing the format to YAML. The prefix field is important because it's the default prefix loaded by the client library.
Library usage
To use the feature in an application, you can build it as a Spring Boot application. The most convenient way to add the dependency is with the Spring Boot starter com.azure.spring:spring-cloud-azure-starter-appconfiguration-config
. The following example pom.xml file uses Azure App Configuration:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>{spring-boot-version}</version>
<relativePath />
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>5.18.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-appconfiguration-config</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Note
If you're using Spring Boot 2.x, be sure to set the spring-cloud-azure-dependencies
version to 4.19.0
.
For more information about the version used for this BOM, see Which Version of Spring Cloud Azure Should I Use.
The following example shows a basic Spring Boot application using App Configuration:
@SpringBootApplication
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For this example, the bootstrap.properties file contains the following line:
spring.cloud.azure.appconfiguration.stores[0].connection-string=${CONFIG_STORE_CONNECTION_STRING}
CONFIG_STORE_CONNECTION_STRING
is an environment variable with the connection string to your Azure App Configuration Store. You can access your connection string by using the following command:
az appconfig credential list --name <name-of-your-store>
By default, if no configurations are set, the configurations starting with /application/
are loaded with a default label of (No Label)
unless a Spring Profile is set, in which case the default label is your Spring Profile. Because the store is empty, no configurations are loaded, but the Azure App Configuration Property Source is still generated.
A property source named /application/https://<name-of-your-store>.azconfig.io/
is created containing the properties of that store. The label used in the request is appended to the end of the name. If no label is set, the character \0
is present as an empty space.
Loading configuration
The library supports the loading of one or multiple App Configuration stores. In the situation where a key is duplicated across multiple stores, loading all stores results in the highest priority stores configuration being loaded. The last one wins. This process is illustrated in the following example:
spring.cloud.azure.appconfiguration.stores[0].connection-string=[first-store-connection-string]
spring.cloud.azure.appconfiguration.stores[1].connection-string=[second-store-connection-string]
In the example, if both the first and second stores have the same configuration, the configuration in the second store has the highest priority, and the last one wins.
Note
You can use Azure App Configuration settings like any other Spring Configuration. For more information, see Core Features in the Spring Boot documentation or Quickstart: Create a Java Spring app with Azure App Configuration.
Selecting configurations
Configurations are loaded by their key and label. By default, the configurations that start with the key /application/
are loaded. The default label is ${spring.profiles.active}
. If ${spring.profiles.active}
isn't set, then configurations with the null
label are loaded. The null
label appears as (No Label)
in the Azure portal.
You can configure the configurations that are loaded by selecting different key and label filters, as shown in the following example:
spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter=[my-key]
spring.cloud.azure.appconfiguration.stores[0].selects[0].label-filter=[my-label]
The key-filter
property supports the following filters:
Key filter | Effect |
---|---|
* |
Matches any key. |
abc |
Matches a key named abc . |
abc* |
Matches key names that start with abc . |
abc,xyz |
Matches key names abc or xyz . Limited to five comma-separated values. |
The label-filter
property supports the following filters:
Label | Description |
---|---|
* |
Matches any label, including \0 . |
\0 |
Matches null labels, which appear as (No Label) in the Azure portal. |
1.0.0 |
Matches label 1.0.0 exactly. |
1.0.* |
Matches labels that start with 1.0.* . |
,1.0.0 |
Matches labels null and 1.0.0 . Limited to five comma-separated values. |
If you're using YAML with label filters, and you need to start with null
, then the label filter needs to be surrounded by single quotes, as shown in the following example:
spring:
cloud:
azure:
appconfiguration:
stores:
- selects:
- label-filter: ',1.0.0'
Note
You can't combine *
with ,
in filters. In that case, you need to use an additional select value.
Spring Profiles
By default, spring.profiles.active
is set as the default label-filter
for all selected configurations. You can override this functionality by using label-filter
. You can use the Spring Profiles in the label-filter
by using ${spring.profiles.active}
, as shown in the following example:
spring.cloud.azure.appconfiguration.stores[0].selects[0].label-filter=,${spring.profiles.active}
spring.cloud.azure.appconfiguration.stores[0].selects[1].label-filter=${spring.profiles.active}_local
In the first label-filter
, all configurations with the null
label are loaded, followed by all configurations matching the Spring Profiles. Spring Profiles have priority over the null
configurations, because they're at the end.
In the second label-filter
, the string _local
is appended to the end of the Spring Profiles, though only to the last Spring Profile.
Disabled stores
Using the configuration spring.cloud.azure.appconfiguration.enabled
, you can disable loading for all configuration stores. With the spring.cloud.azure.appconfiguration.stores[0].enabled
configuration, you can disable an individual store.
In addition to disabling stores, you can configure stores to be disabled if they fail to load. For this configuration, use spring.cloud.azure.appconfiguration.stores[0].fail-fast
. When fail-fast
is disabled by setting it to false
, a RuntimeException
results in the application store being disabled with no configurations from it being loaded. If a configuration store is disabled on startup, it isn't checked for changes upon refresh. Also, there's no attempt to load values from it if configurations are updated.
If an error resulting in a RuntimeException
happens during a refresh check or while attempting to reload configurations, then the refresh attempt ends and is retried after the refresh-interval
has passed.
Authentication
The library supports all forms of identity supported by the Azure Identity Library. You can do authentication through configuration for connection strings and managed identity.
Connection string
Authentication through connection string is the simplest form to set up. You can access a store's connection strings by using the following command:
az appconfig credential list --name <name-of-your-store>
You can then set the spring.cloud.azure.appconfiguration.stores[0].connection-string
property to the connection string. We highly recommend setting the connection string in the local configuration file to a placeholder value that maps to an environment variable. This approach enables you to avoid adding the connection string to source control.
Spring Cloud Azure configuration
You can use Spring Cloud Azure configuration to configure the library. You can use the following properties to configure the library:
spring.cloud.azure.appconfiguration.stores[0].endpoint= <URI-of-your-configuration-store>
When only the endpoint is set, the client library uses the DefaultAzureCredential to authenticate. The DefaultAzureCredential
uses the following methods to authenticate:
- Environment credential
- Managed Identity credential
- Azure Developer CLI credential
- IntelliJ credential
- Azure CLI credential
- Azure PowerShell credential
You need to assign an identity such as a system assigned identity to read configurations. You can create this assignment by using the following command:
az role assignment create \
--role "App Configuration Data Reader" \
--assignee <your-client-ID> \
--scope /subscriptions/<your-subscription>/resourceGroups/<your-stores-resource-group>/providers/Microsoft.AppConfiguration/configurationStores/<name-of-your-configuration-store>
Note
You can define only one authentication method per endpoint: connection string, user assigned identity, or token credential. If you need to mix and match, you can use ConfigurationClientCustomizer
to modify stores that use a different method.
Geo-replication
The library supports the geo-replication feature of Azure App Configuration. This feature enables you to replicate your data to other locations. This feature is useful for high availability and disaster recovery.
Each replica you create has a dedicated endpoint. If your application resides in multiple geolocations, you can update each deployment of your application in a location to connect to the replica closer to that location, which helps minimize the network latency between your application and App Configuration. Because each replica has its separate request quota, this setup also helps the scalability of your application while it grows to a multi-region distributed service.
The failover may occur if the library observes any of the following conditions:
- Receives responses with service unavailable status code (HTTP 500 or above) from an endpoint.
- Experiences network connectivity issues.
- Requests are throttled (HTTP status code 429).
Creating a configuration store with geo-replication
To create a replica of your configuration store, you can use the Azure CLI or the Azure portal. The following example uses the Azure CLI to create a replica in the East US 2 region:
az appconfig replica create --location --name --store-name [--resource-group]
Using the configuration store replica
After you've created a replica, you can use it in your application. Like the origin store, you can connect to your replica using Microsoft Entra ID or a connection string.
To use Microsoft Entra ID to connect to your replica, you need to list the endpoints
of your configuration store instances, as shown in the following example:
spring.cloud.azure.appconfiguration.stores[0].endpoints[0]=[your primary store endpoint]
spring.cloud.azure.appconfiguration.stores[0].endpoints[1]=[your replica store endpoint]
You can list as many endpoints as you have replicas. The library tries to connect to the endpoints in the order they're listed. If the library is unable to connect to a replica, it tries the next one in the list. After a period of time has passed, the library attempts to reconnect to the preferred endpoints.
Key values
Azure App Configuration supports multiple types of key values, some of which have special features built into them. Azure App Configuration has built-in support for the JSON content type, Spring placeholders, and Key Vault references.
Placeholders
The library supports configurations with ${}
-style environment placeholders. When referencing an Azure App Configuration key with a placeholder, remove prefixes from the reference. For example, /application/config.message
is referenced as ${config.message}
.
Note
The prefix being removed matches the value spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter
.
JSON
Configurations that have a content-type application/json
are processed as JSON objects. This feature enables you to map one configuration to a complex object inside a @ConfigurationProperties
. For example, consider the JSON key /application/config.colors
with the following value:
{
"Red": {
"value": [255, 0, 0]
},
"Blue": {
"value": [0, 255, 0]
},
"Green": {
"value": [0, 0, 255]
}
}
This key maps to the following code:
@ConfigurationProperties(prefix = "config")
public class MyConfigurations {
private Map<String, Color> colors;
}
Key Vault references
Azure App Configuration and its libraries support referencing secrets stored in Key Vault. In App Configuration, you can create keys with values that map to secrets stored in a Key Vault. Secrets are securely stored in Key Vault, but can be accessed in the same way as any other configuration after it's loaded.
Your application uses the client provider to retrieve Key Vault references, just as it does for any other keys stored in App Configuration. Because the client recognizes the keys as Key Vault references, they have a unique content-type, and the client connects to Key Vault to retrieve their values for you.
Note
Key Vault only allows for secrets to be retrieved one at a time, so each Key Vault reference stored in App Configuration results in a pull against Key Vault.
Creating Key Vault references
You can create a Key Vault reference in the Azure portal by going to Configuration explorer > Create > Key Vault reference. You can then select a secret to reference from any of the Key Vaults you have access to. You can also create arbitrary Key Vault references from the Input tab. In the Azure portal, enter a valid URI.
You can also create a Key Vault reference through the Azure CLI by using the following command:
az appconfig kv set-keyvault \
--name <name-of-your-store> \
--key <key-name> \
--secret-identifier <URI-to-your-secret>
You can create any secret-identifier through the Azure CLI. Secret identifiers just require the format {vault}/{collection}/{name}/{version?}
where the version section is optional.
Using Key Vault references
You can use Spring Cloud Azure configuration to configure the library. You can use the same credential used to connect to App Configuration to connect to Azure Key Vault.
Resolve non-Key Vault secrets
The App Configuration library provides a method to locally resolve secrets that don't have a Key Vault associated with them. This resolution is done through the KeyVaultSecretProvider
. The KeyVaultSecretProvider
is called when a TokenCredential
isn't provided for a Key Vault reference. The URI of the Key Vault reference is provided and the returned value becomes the value of the secret.
Warning
The use of a KeyVaultSecretProvider
overrides the automatic use of the system-assigned managed identity. To use both, you need to use KeyVaultCredentialProvider
and return null
for the URIs that need resolving.
public class MySecretProvider implements KeyVaultSecretProvider {
@Override
public String getSecret(String uri) {
...
}
}
Feature management
Feature management provides a way for Spring Boot applications to dynamically access content. Feature management has various functions such as the following ones:
- Feature flags that can enable or disable content
- Feature filters for targeting when content is shown
- Customized feature filters
- Feature gates for dynamically enabling endpoints
You can enable feature flags through the following configuration:
spring.cloud.azure.appconfiguration.stores[0].feature-flags.enabled= true
Enabled feature flags are loaded into the Spring configuration system with the prefix feature-management
. You can also register feature flags in the local configuration file. For more information, see the Feature flag declaration section.
The easiest way to use feature management is by using the spring-cloud-azure-feature-management
and spring-cloud-azure-feature-management-web
libraries. The difference between the two libraries is that spring-cloud-azure-feature-management-web
takes a dependency on the spring-web
and spring-webmvc
libraries to add more features, such as feature gates.
You can enable feature flags by using key/label filters. By default, a null
label, seen as (No Label)
, is assigned. You can configure the feature flags that are loaded by setting a label filter, as shown in the following example:
spring.cloud.azure.appconfiguration.stores[0].feature-flags.selects[0].key-filter=A*
spring.cloud.azure.appconfiguration.stores[0].feature-flags.selects[0].label-filter= dev
Feature management basics
Feature flags
Feature flags are composed of two parts: a name and a list of feature-filters that are used to turn on the feature. Feature flags can either have a boolean state of on/off, or they can have a list of feature filters. Feature flags evaluate feature filters until one returns true
. If no feature filter returns true
, then the feature flag returns false
.
Feature filters
Feature filters define a scenario for when a feature should be enabled. Feature filters are evaluated synchronously.
The feature management library comes with four predefined filters: AlwaysOnFilter, PercentageFilter, TimeWindowFilter, and TargetingFilter.
You can create custom feature filters. For example, you can use a feature filter to provide a custom experience for customers who are using a Microsoft Edge browser. You can customize the features in this feature filter, for example, to show a specific header for the Microsoft Edge browser audience.
Feature flag declaration
The feature management library supports Azure App Configuration along with application.yml or bootstrap.yml as sources for feature flags. Here's an example of the format used to set up feature flags in an application.yml file:
feature-management:
feature-t: false
feature-u:
enabled-for:
- name: Random
feature-v:
enabled-for:
- name: TimeWindowFilter
parameters:
Start: "Wed, 01 May 2019 13:59:59 GMT"
End: "Mon, 01 July 2019 00:00:00 GMT"
feature-w:
evaluate: false
enabled-for:
- name: AlwaysOnFilter
This example has the following feature flags:
feature-t
is set tofalse
. This setting always returns the feature flag's value.feature-u
is used with feature filters. These filters are defined under theenabled-for
property. In this case,feature-u
has one feature filter calledRandom
, which doesn't require any configuration, so only the name property is required.feature-v
specifies a feature filter namedTimeWindowFilter
. This feature filter can be passed parameters to use as configuration. In this example, aTimeWindowFilter
, passes in the start and end times during which the feature is active.feature-w
is used for theAlwaysOnFilter
, which always evaluates totrue
. Theevaluate
field is used to stop the evaluation of the feature filters, and results in the feature filter always returningfalse
.
Evaluating feature flags
The spring-cloud-azure-feature-management
library provides FeatureManager
to determine whether a feature flag is enabled. FeatureManager
provides an asynchronous way to check the state of the flag.
spring-cloud-azure-feature-management-web
, along with providing FeatureManager
, contains FeatureManagerSnapshot
, which caches the state of previously evaluated feature flags in the @RequestScope
to guarantee that all requests return the same value. In addition, the web library provides @FeatureGate
, which can either block or redirect web requests to different endpoints.
Feature flag check
FeatureManager
is a @Bean
that can be @Autowired
or injected into @Component
type objects. FeatureManager
has a method isEnabled
that, when passed the name of a feature flag, returns its state.
@Autowired
FeatureManager featureManager;
if (featureManager.isEnabled("feature-t")) {
// Do Something
}
Note
FeatureManger
also has an asynchronous version of isEnabled
called isEnabledAsync
.
If you haven't configured feature management or the feature flag doesn't exist, isEnabled
always returns false
. If an existing feature flag is configured with an unknown feature filter, then a FilterNotFoundException
is thrown. You can change this behavior to return false
by configuring fail-fast
to false
. The following table describes fail-fast
:
Name | Description | Required | Default |
---|---|---|---|
spring.cloud.azure.feature.management.fail-fast |
If an exception occurs, a RuntimeException is thrown. If this property is set to false , then isEnabled returns false instead. |
No | true |
The only difference between FeatureManagerSnapshot
and FeatureManager
is the caching of results in the @RequestScope
.
Feature gate
With the feature management web library, you can require that a given feature is enabled in order to execute an endpoint. You can set up this requirement by using the @FeatureGate
annotation, as shown in the following example:
@GetMapping("/featureT")
@FeatureGate(feature = "feature-t")
@ResponseBody
public String featureT() {
...
}
You can only access the featureT
endpoint if "feature-t" is enabled.
Disabled action handling
When an endpoint is blocked because the feature it specifies is disabled, DisabledFeaturesHandler
is invoked. By default, an HTTP 404 is returned. You can override this behavior by implementing DisabledFeaturesHandler
, as shown in the following example:
@Component
public class MyDisabledFeaturesHandler implements DisabledFeaturesHandler {
@Override
public HttpServletResponse handleDisabledFeatures(HttpServletRequest request, HttpServletResponse response) {
...
return response;
}
}
Routing
Certain routes may expose application capabilities that are gated by features. If a feature is disabled, you can redirect these routes to another endpoint, as shown in the following example:
@GetMapping("/featureT")
@FeatureGate(feature = "feature-t" fallback= "/oldEndpoint")
@ResponseBody
public String featureT() {
...
}
@GetMapping("/oldEndpoint")
@ResponseBody
public String oldEndpoint() {
...
}
Built-in feature filters
There are a few feature filters that come with the spring-cloud-azure-feature-management
package. These feature filters aren't added automatically, but you can set them up in a @Configuration
.
AlwaysOnFilter
This filter always returns true
. For a usage example, see the feature flag declaration section.
PercentageFilter
Each time a user makes a request, the evaluation of PercentageFilter
can return a different result. You can circumvent this inconsistency by using the FeatureManagementSnapshot
, which caches the result of the feature flag per user. This capability ensures that a user has a consistent experience, even if they have to resend the request.
feature-management:
feature-v:
enabled-for:
- name: PercentageFilter
parameters:
Value: 50
TimeWindowFilter
This filter provides the capability to enable a feature based on a time window. If you specify only End
, the feature is considered on until that time. If you specify only Start
, the feature is considered on at all points after that time. If you specify both, the feature is considered valid between the two times.
feature-management:
feature-v:
enabled-for:
- name: TimeWindowFilter
parameters:
Start: "Wed, 01 May 2019 13:59:59 GMT",
End: "Mon, 01 July 2019 00:00:00 GMT"
TargetingFilter
This filter provides the capability to enable a feature for a target audience. For an in-depth explanation of targeting, see the targeting section section. The filter parameters include an audience object that describes users, groups, and a default percentage of the user base that should have access to the feature. For each group object that is listed in the target audience, a percentage is required that defines the percentage of that group's members that have access to the feature. A user has the feature enabled in the following cases:
- The user is specified in the users' section directly.
- The user is in the included percentage of any of the group rollouts.
- The user falls into the default rollout percentage.
feature-management:
target:
enabled-for:
- name: targetingFilter
parameters:
users:
- Jeff
- Alicia
groups:
- name: Ring0
rollout-percentage: 100
- name: Ring1
rolloutPercentage: 100
default-rollout-percentage: 50
Custom feature filters
Creating a custom feature filter provides a way to enable features based on criteria that you define. To create a custom feature filter, you must implement the FeatureFilter
interface. FeatureFilter
has a single method evaluate
. When a feature specifies that it can be enabled with a feature filter, the evaluate
method is called. If evaluate
returns true
, it means that the feature should be enabled. If it returns false
, it continues evaluating feature filters until one returns true
. If all filters return false
, then the feature is off.
Feature filters are defined as Spring Beans, so they're either defined as @Component
or defined in a @Configuration
.
@Component("Random")
public class Random implements FeatureFilter {
@Override
public boolean evaluate(FeatureFilterEvaluationContext context) {
double chance = Double.valueOf((String) context.getParameters().get("chance"));
return Math.random() > chance / 100;
}
}
Parameterized feature filters
Some feature filters require parameters to determine whether a feature should be turned on. For example, a browser feature filter may turn on a feature for a certain set of browsers. You might want a feature enabled for Microsoft Edge and Chrome browsers, but not Firefox. To set up this situation, you can design a feature filter to expect parameters. These parameters would be specified in the feature configuration and in code, and would be accessible via the FeatureFilterEvaluationContext
parameter of evaluate
. FeatureFilterEvaluationContext
has a property parameters
, which is a HashMap<String, Object>
.
Targeting
Targeting is a feature management strategy that enables developers to progressively roll out new features to their user base. The strategy is built on the concept of targeting a set of users known as the target audience. An audience is made up of specific users, groups, and a designated percentage of the entire user base. The groups that are included in the audience can be broken down further into percentages of their total members.
The following steps demonstrate an example of a progressive rollout for a new 'Beta' feature:
- Individual users Jeff and Alicia are granted access to the Beta.
- Another user, Mark, asks to opt in and is included.
- Twenty percent of a group known as "Ring1" users are included in the Beta.
- The number of "Ring1" users included in the beta is bumped up to 100 percent.
- Five percent of the user base is included in the beta.
- The rollout percentage is bumped up to 100 percent and the feature is completely rolled out.
This strategy for rolling out a feature is built into the library through the included TargetingFilter
feature filter.
Targeting in an application
An example web application that uses the targeting feature filter is available in the example project.
To begin using the TargetingFilter
in an application, you must add it as a @Bean
like any other feature filter. TargetingFilter
relies on another @Bean
to be added to the application TargetingContextAccessor
. The TargetingContextAccessor
allows for defining the current TargetingContext
to be used for defining the current user ID and groups, as shown in the following example:
public class MyTargetingContextAccessor implements TargetingContextAccessor {
@Override
public void getContextAsync(TargetingContext context) {
context.setUserId("Jeff");
ArrayList<String> groups = new ArrayList<String>();
groups.add("Ring0");
context.setGroups(groups);
}
}
Targeting evaluation options
Options are available to customize how targeting evaluation is performed across a given TargetingFilter
. You can set an optional parameter, TargetingEvaluationOptions
, during the TargetingFilter
creation.
@Bean
public TargetingFilter targetingFilter(MyTargetingContextAccessor contextAccessor) {
return new TargetingFilter(contextAccessor, new TargetingEvaluationOptions().setIgnoreCase(true));
}
Configuration refresh
Enabling config refresh for your configurations lets you pull their latest values from your App Configuration store or stores without having to restart the application.
To enable refresh, you need to enable monitoring along with monitoring triggers. A monitoring trigger is a key with an optional label that's checked for value changes to trigger updates. The value of the monitoring trigger can be any value, as long as it changes when a refresh is needed.
Note
Any operation that changes the ETag of a monitoring trigger causes a refresh, such as a content-type change.
spring:
cloud:
azure:
appconfiguration:
stores:
- monitoring:
enabled: true
triggers:
- key: [my-watched-key]
label: [my-watched-label]
To trigger a configuration refresh, change the value of a key in your configuration store. Then, update one of watch keys to a new value. This change triggers the creation of a log. For example, changing the value of /application/config.message
triggers the following log message:
INFO 17496 --- [TaskScheduler-1] o.s.c.e.event.RefreshEventListener : Refresh keys changed: [config.message]
After the application generates the log, it refreshes all @Bean
s in the refresh scope.
Note
By default, @ConfigurationProperties
annotated beans are included in this scope.
Pull-based refresh
The App Configuration Spring libraries support the ability to periodically check on a refresh interval for changes made to the monitoring triggers. By default, the refresh interval is set to 30 seconds. After the refresh interval has passed, all triggers are checked in the given store for changes. Any change to the key causes a refresh to trigger. Because the libraries integrate with the Spring refresh system, any refresh reloads all configurations from all stores. You can set the refresh interval to any interval longer than 1 second. The supported units for the refresh interval are s
, m
, h
, and d
for seconds, minutes, hours, and days respectively. The following example sets the refresh interval to 5 minutes:
spring.cloud.azure.appconfiguration.stores[0].monitoring.refresh-interval= 5m
Automated
When you use the spring-cloud-azure-appconfiguration-config-web
library, the application automatically checks for a refresh whenever a servlet request occurs, specifically ServletRequestHandledEvent
. The most common way this event is sent is by requests to endpoints in a @RestController
.
Manual
In applications that use only spring-cloud-azure-appconfiguration-config
, such as console applications, you can manually trigger a refresh by calling AppConfigurationRefresh
's refreshConfiguration
method. AppConfigurationRefresh
is a @Bean
that you can inject into any @Component
.
Also, because the library uses Spring's configuration system, triggering a refresh causes a refresh of all of your configurations, not just a reload of the ones from your Azure App Configuration store.
Push-based refresh
You can set up the spring-cloud-azure-appconfiguration-config-web
library to receive push notifications from your Azure App Configuration store to refresh your configuration values. You can set up this configuration through an Azure Event Grid Web Hook, which you can configure to send notifications of changes to specified keys. By adding the Spring Actuator library as a dependency, you can expose App Configuration's refresh endpoints. There are two different endpoints: appconfiguration-refresh
and appconfiguration-refresh-bus
. These endpoints work similarly to their counterparts refresh
and refresh-bus
, where the app configuration endpoints expire the refresh interval instead of forcing a refresh upon receiving. You can still use the refresh
and refresh-bus
, but you can't connect them directly to Azure Event Grid with a Web Hook because they require a response in setup.
The appconfiguration-refresh
property expires the refresh interval, so the remaining refresh interval isn't waited on before the next refresh check. The appconfiguration-refresh-bus
property sends a notification to a connected messaging service, such as Azure Service Bus, to notify all instances of an application to refresh. In both cases, it doesn't completely expire at the refresh interval, but is off by a small jitter amount. This jitter ensures that every instance of your application doesn't try to refresh at the same time.
management.endpoints.web.exposure.include= appconfiguration-refresh, appconfiguration-refresh-bus
In addition to exposing the refresh endpoints, a required query parameter has been added for security. No token name or value is set by default, but setting one is required in order to use the endpoints, as shown in the following example:
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.primary-token.name=[primary-token-name]
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.primary-token.secret=[primary-token-secret]
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.secondary-token.name=[secondary-token-name]
spring.cloud.azure.appconfiguration.stores[0].monitoring.push-notification.secondary-token.secret=[secondary-token-secret]
Setting up web hooks
To set up a web hook, open your Azure App Configuration store and open Events from the navigation menu. Then, select Event Subscription. Set the name of your event and select the endpoint type to be Web Hook. Selecting Web Hook causes an Endpoint option to appear. Select Select an endpoint. Your endpoint should look like the following example: https://www.myaplication.com/actuator/appconfiguration-refresh?myTokenName=mySecret
.
Confirm Selection sends a setup notification to the given URI and it expects a response. If no response is returned, the setup fails. The azure-spring-cloud-appconfiguration-web
library setup for endpoints returns the correct response if the Azure App Configuration store is configured for the application. This confirmation can be sent in other ways. For more information about web hook delivery, see Webhook event delivery.
Note
This validation happens only upon the creation or modification of the endpoint.
We highly recommend that you set up filters because otherwise, a refresh is triggered after every key creation and modification.
Forced client refresh
You can configure the library to force a refresh of all configurations at a refresh interval. The following table describes the refresh-interval
property:
Name | Description | Required | Default |
---|---|---|---|
spring.cloud.azure.appconfiguration.refresh-interval |
The standard amount of time between refreshes. Is a Duration . |
No | null |
Refreshing with spring.cloud.azure.appconfiguration.refresh-interval
doesn't check any configured watch keys. This property is used to make sure Key Vault secrets are kept up to date because Azure App Configuration can't tell when they're updated.
Because Azure Key Vault stores the public and private key pair of a certificate as a secret, your application can retrieve any certificate as a Key Vault reference in App Configuration. Because certificates need to be rotated periodically, client applications need to update just as frequently, which can be done by using the client refresh interval.
Feature flag refresh
If feature flags and monitoring are both enabled, then by default the refresh interval for feature flags is set to 30 seconds. After the refresh interval has passed, all feature flags are checked in the given store for changes. Any change to the key causes a refresh to trigger. Because the libraries integrate with the Spring refresh system, any refresh reloads all configurations from all stores. You can set the refresh interval to any interval longer than 1 second. The supported units for the refresh interval are s
, m
, h
, and d
for seconds, minutes, hours, and days respectively. The following example sets the refresh interval to 5 minutes:
spring.cloud.azure.appconfiguration.stores[0].monitoring.feature-flag-refresh-interval= 5m
Health indicator
The client library comes with a health indicator that checks whether the connection to the Azure App Configuration store or stores is healthy. If enabled for each store, it gives one of the following status values:
- UP - The last connection was successful.
- DOWN- The last connection resulted in a non-200 error code. This status could be due to issues ranging from credentials expiring to a service issue. The client library automatically retries to connect to the store at the next refresh interval.
- NOT LOADED - The config store is listed in the local configuration file, but the config store wasn't loaded from the file at startup. The config store is disabled in the configuration file or the configuration or configurations failed to load at startup while the
fail-fast
configuration for the store was set tofalse
.
You can enable the health indicator by setting management.health.azure-app-configuration.enabled=true
.
Client customization
The App Configuration library uses the Azure SDK for Java for connecting to Azure App Configuration and Azure Key Vault. Two interfaces, ConfigurationClientCustomizer
and SecretClientCustomizer
, are provided to modify the clients. Each interface has a customize
method that takes in their respective builder along with the String
value of the URI the client is being configured for, as shown in the following interface definitions:
public interface ConfigurationClientCustomizer {
public void setup(ConfigurationClientBuilder builder, String endpoint);
}
public interface SecretClientCustomizer {
public void setup(SecretClientBuilder builder, String endpoint);
}
These interfaces allow for customization of the HTTP client and its configurations. The following example replaces the default HttpClient
with another one that uses a proxy for all traffic directed to App Configuration and Key Vault.
Note
The ConfigurationClientBuilder
and SecretClientBuilder
are already set up for use when passed into customize
. Any changes to the clients, including the credentials and retry policy, override those already in place.
You can also do this configuration by using Spring Cloud Azure configuration.
public class CustomClient implements ConfigurationClientCustomizer, SecretClientCustomizer {
@Override
public void customize(ConfigurationClientBuilder builder, String endpoint) {
builder.httpClient(buildHttpClient());
}
@Override
public void customize(SecretClientBuilder builder, String endpoint) {
builder.httpClient(buildHttpClient());
}
private HttpClient buildHttpClient() {
String hostname = System.getProperty("https.proxyHosts");
String portString = System.getProperty("https.proxyPort");
int port = Integer.valueOf(portString);
ProxyOptions proxyOptions = new ProxyOptions(ProxyOptions.Type.HTTP,
new InetSocketAddress(hostname, port));
return new NettyAsyncHttpClientBuilder()
.proxy(proxyOptions)
.build();
}
}