© 2016-2021 the original authors.
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. |
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.
The Azure Spring Cloud App Configuration Client Library loads configurations and feature flags from the Azure App Configuration service. The client library generates PropertySource
abstractions, to match those already being generated by the Spring environment such as; Environment Variables, Command-line configurations, local configuration files, and so on.
1. Getting Help
If you have any questions about this doc, please ask by creating GitHub issue. And Pull requests is welcome.
GitHub repositories | Description |
---|---|
This repository used to hold the source code. |
|
This repository used to hold release notes, and general Azure App Configuration Issues. |
|
This repository used to hold the document which is displaying in current page. |
2. Setting up your App Configuration Store
To create your Azure App Configuration store, you can use:
az appconfig create --resource-group <your-resource-group> --name <name-of-your-new-store> --sku Standard
This command will create you a new empty configuration store. You can upload you configurations using the import command:
az appconfig kv import -n <name-of-your-new-store> -s file --path <location-of-your-properties-file> --format properties --prefix /application/
It will have you confirm your configurations before loading them. You can upload yaml files by changing the format to yaml. The prefix field is important as it is the default prefix loaded by the client library.
3. Client 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 our Spring Boot starter com.azure.spring:azure-spring-cloud-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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>{spring-cloud-version}</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>azure-spring-cloud-starter-appconfiguration-config</artifactId>
<version>{azure-appconfiguration-version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
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);
}
}
with bootstrap.properties
containing:
spring.cloud.azure.appconfiguration.stores[0].connection-string=${CONFIG_STORE_CONNECTION_STRING}
where 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 running:
az appconfig credential list --name <name-of-your-store>
By default, if no configurations are set, then 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 there, as an empty space.
4. Loading Configuration
The library supports the loading of one or multiple App Configuration stores. This allows for complex configuration scenarios, such as loading of configurations from multiple stores at once or having fallbacks for added resilience. In the situation where a key is duplicated across multiple stores, loading all stores will result in the highest priority, last one wins, stores configuration being loaded. 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, last one wins.
Azure App Configuration settings can be used link any other Spring Configuration. See Spring Boot Docs or Azure App Configuration Quick Start. |
4.1. 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}
is not set then configuration with the null label seen as (No Label)
in the portal are loaded.
The configurations that are loaded can be changed by selecting different key and label filters.
spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter=[my-key]
spring.cloud.azure.appconfiguration.stores[0].selects[0].label-filter=[my-label]
key-filter
allows for any key prefix to be set. * isn’t supported in the key filter and is automatically added to the end of any key filter. The key filter is limited to 5 CSVs.
If you want to load all configurations then key-filter needs to be set to an empty value. In properties this is done by setting key-filter= and in yaml this is done by setting key-filter: "" .
|
label-filter
supports the following filters:
Label | Description |
---|---|
|
Matches |
|
Matches label 1.0.0 exactly |
|
Matches labels that start with 1.0. |
|
Matches labels |
If you are using yaml with label filters and need to start with null
, then the label filter needs to be surrounded by single quotes.
spring:
cloud:
azure:
appconfiguration:
stores:
-
selects:
-
label-filter: ',1.0.0'
You are unable to combine * with , in filters. In that case you need to use an additional select.
|
4.2. Spring Profiles
By default, spring.profiles.active
is set as the default label-filter
for all selected configurations. This functionality can be overridden by the label-filter
. The Spring Profiles can be used in the label-filter
by using ${spring.profiles.active}
in the label-filter
.
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 null
label are loaded, then all configurations matching the Spring Profiles. Spring Profiles have priority over the null
configurations, because they are 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.
4.3. Disabled Stores
Using the configuration spring.cloud.azure.appconfiguration.enabled
you can disable loading all configuration stores. With the spring.cloud.azure.appconfiguration.stores[0].enabled
configuration, you can disable an individual store.
In addition to disabling all stores, stores can be configured to be disabled if they fail to load using the spring.cloud.azure.appconfiguration.stores[0].fail-fast
. When fail-fast
is enabled, set to true
, a RuntimeException
that would result in the application not starting up will instead have the store disabled with no configurations from it loaded. If a configuration store is disabled on startup it will not be checked for changes with refresh, or attempted to be loaded from if configurations are updated.
If, an error resulting in a RuntimeException
happens during a refresh check or a reload of configuration the refresh attempt is ended and will be retried after the refresh-interval
has passed.
5. Authentication
The client library supports all forms of Identity supported by the Azure Identity Library. Authentication can be done through configuration for Connection Strings and Managed Identity. All other forms of Identity can be done by using the TokenCredentialProvider
.
5.1. Connection String
Authentication through connection string is the simplest form to setup. You can access a stores connection strings using:
az appconfig credential list --name <name-of-your-store>
The connection string can be then set to the property spring.cloud.azure.appconfiguration.stores[0].connection-string
. It is highly recommended that the connection string in the local configuration file should be a placeholder value, which should map to an Environment Variable to avoid having it added to source control.
5.2. User Assigned Identity
If you already don’t have one, you can create a new Managed Identity using:
az identity create -g myResourceGroup -n myUserAssignedIdentity
Connecting to Azure app configuration with any authentication method other connection string requires three steps to setup.
-
Adding the Identity to the resource connecting to your App Configuration store
-
Adding the required information to your client application.
-
Authorizing the Identity to use App Configuration.
For user assigned, the identity needs to be added to the azure resource. How the user assigned identity is added varies between resources so you will need to check the specific docs of that resource.
Configuration of the client application just needs two values to be set.
spring.cloud.azure.appconfiguration.managed-identity.client-id= <your client id>
spring.cloud.azure.appconfiguration.stores[0].endpoint= <URI of your Configuration Store>
The first property tells us which of the configured User Assigned identities to use. The second is which configuration store we are connecting to. With this method, all configuration stores need to use the same User Assigned Identity.
Next, the User Assigned Identity needs to be setup in your configuration store. Setup can be done through IAM in the Azure Portal or using the cli:
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>
5.3. System Assigned Identity
Setting up System Assigned Identity involves enabling it on the azure resource. How the identity is enabled depends on the resource, so you will need to check the documentation for that resource. Once it is enabled, you need to configure your client application to use it.
spring.cloud.azure.appconfiguration.stores[0].endpoint= <URI of your Configuration Store>
You will then need to assign the System Assigned Identity to read configurations.
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>
5.4. Token Credential
To authenticate with any other method supported by the Azure Identity Library, you can use AppConfigurationCredentialProvider
. AppConfigurationCredentialProvider
allows you to dynamically return any TokenCredential
for any given URI to connect to your Configuration stores.
@Bean
public AppConfigurationCredentialProvider appConfigurationCredentialProvider() {
return new AppConfigurationCredentialProvider() {
@Override
public TokenCredential getAppConfigCredential(String uri) {
...
return myTokenCredential;
}
};
}
You will also need to configure the endpoints that you will be connecting too.
spring.cloud.azure.appconfiguration.stores[0].endpoint= <URI of your Configuration Store>
Note: Only 1 authentication method can be defined per endpoint; Connection String, User Assigned Identity, Token Credential. If you need to mix and match you can have AppConfigurationCredentialProvider
return null
for stores that use a different method.
6. 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 JSON content type, Spring placeholders, an Key Value references.
6.1. Placeholders
The client library supports configurations with ${}
-style environment placeholders. If referencing a Azure App Configuration key with a placeholder any prefix needs to be removed in the reference. For example: /application/config.message
is referenced as ${config.message}
.
The prefix being removed matches the value spring.cloud.azure.appconfiguration.stores[0].selects[0].key-filter .
|
6.2. JSON
Configurations that have the a content-type application/json
are processed as json objects. This enables one configuration mapping to a complex object in a @Configuration
. The json key /application/config.colors
with the value
{
"Red": {
"value": [255, 0, 0]
},
"Blue": {
"value": [0, 255, 0]
},
"Green": {
"value": [0, 0, 255]
}
}
maps to
@ConfigurationProperties(prefix = "config")
public class MyConfigurations {
private HashMap<String, Color> colors;
}
6.3. Key Vault References
Azure App Configuration and its client libraries support referencing secrets stored in Key Vault. In App Configuration, keys can be created which have values that map to a secret stored in a Key Vault. The secrets are securely stored in Key Vault, but can be accessed the same as any other configuration once 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 will connect to Key Vault to retrieve their values for you.
Key Vault only allows for secrets to be retrieved one at a time, so each Key Vault reference stored in App Configuration will result in a pull against Key Vault. |
6.3.1. Creating Key Vault References
You can easily create a Key Vault reference in the Azure Portal in the Configuration explorer using the Create → Key Vault reference option. You will be able to select a secret to reference, this can be from any of the Key Vaults you have access to. You can also create arbitrary Key Vault references using the Input option. Through the portal a valid URI Key Vault is required and using a Key Vault reference format.
You can also create a Key Vault reference through the cli using:
az appconfig kv set-keyvault --name <name-of-your-store> --key <key-name> --secret-identifier <uri-to-your-secret>
Though the CLI any secret-identifier with the format of {vault}/{collection}/{name}/{version?}
where the version section is optional.
6.3.2. Using Key Vault References
If you are using a User Assigned or System Assigned Identity all you need to do is make sure that identity also has access to the Key Vault and reading secrets.
Otherwise, you need to provide a Token Credential for the client library to use to connect to your Key Vault with.
public class MyCredentials implements KeyVaultCredentialProvider {
@Override
public TokenCredential getKeyVaultCredential(String uri) {
return buildCredential();
}
TokenCredential buildCredential() {
return new DefaultAzureCredentialBuilder().build();
}
}
Using the KeyVaultCredentialProvider you can provide any TokenCredential type supported by the Azure Identity Library.
6.3.3. Resolve Non Key Vault Secrets
The App Configuration client provides a method of locally resolving secrets that don’t have a Key Vault associated with them. This 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 return value becomes the value of the secret.
Creating a KeyVaultSecretProvider overrides the automatic use of System Assigned Identity. In order to use both, KeyVaultCredentialProvider needs to be used and return null for URI’s that need to resolve using KeyVaultSecretProvider .
|
public class MySecretProvider implements KeyVaultSecretProvider {
@Override
public String getSecret(String uri) {
...
}
}
7. Feature Management
Feature Management provides a way for Spring Boot applications to dynamically access content. Feature Management has a variety of functions, such as Feature Flags that can enable or disable content, Feature Filters for targeting when content is shown, Customized Feature Filters, and Feature Gates for dynamically enabling endpoints.
To use feature flags, they can be enabled through configuration:
spring.cloud.azure.appconfiguration.stores[0].feature-flags.enabled= true
With Feature Flags enabled, they will be loaded into the Spring Configuration system with the prefix feature-management
. Feature Flags can also be registered in the local configuration file, see Feature Flag Declaration. The easiest way to use Feature Management is by using the azure-spring-cloud-feature-management
and azure-spring-cloud-feature-management-web
libraries. The difference between the two libraries is that azure-spring-cloud-feature-management-web
takes a dependency on the spring-web
and spring-webmvc
libraries to add additional features, such as Feature Gates.
Feature Flags can also be loaded by label. By default, a null label, seen as (No Label)
is assigned. The Feature Flags that are loaded can be configured by setting a label filter:
spring.cloud.azure.appconfiguration.stores[0].feature-flags.label-filter= dev
7.1. Feature Management Basics
7.1.1. Feature Flags
Feature flags are composed of two parts, a name and a list of feature-filters that are used to turn the feature on. 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.
7.1.2. 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 4 pre-defined filters: AlwaysOnFilter, PercentageFilter, TimeWindowFilter, TargetingFilter.
Custom Feature Filters be made. For example, a Feature Filter could be used to provide a custom experience for only the customers who are using a Microsoft Edge browser. The features in this Feature Filter can be customized, for example to show a specific header for the Edge browser audience.
7.1.3. Feature Flag Declaration
The Feature Management library supports Azure App Configuration along with application.yml or bootstrap.yml as sources for Feature Flags. Below is an example of the format used to set up Feature Flags in a 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
-
feature-t
is set to false, this setting will always return the Feature Flag’s value. -
feature-u
is used in conjunction with Feature Filters. These Filters will be defined under theenabled-for
property. In this case,feature-u
has one Feature Filter calledRandom
, which does not 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 case, for aTimeWindowFilter
, pass in the start and end times for which the feature will be active. -
feature-w
is used for theAlwaysOnFilter
, which will always evaluate totrue
. Theevaluate
field is used to stop the evaluation of the Feature Filters, and results in the Feature Filter always returnfalse
.
7.2. Evaluating Feature Flags
azure-spring-cloud-feature-management
library provides FeatureManager
to check if a Feature Flag is enabled. It provides an asynchronous way to check the state of the flag.
azure-spring-cloud-feature-management-web
along with providing FeatureManager
has FeatureManagerSnapshot
which caches the state of previously evaluated Feature Flags in the @RequestScope
to guarantee all requests return the same value. In addition, the web library provides @FeatureGate
which can either block or redirect web requests to different endpoints.
7.2.1. Feature Flag Check
FeatureManager
is a @Bean
that can be @Autowired
or injected into @Component
type objects. FeatureManager
has a method isEnabledAsync
that, when passed the name of a Feature Flag, returns its state.
@Autowired
FeatureManager featureManager;
if(featureManager.isEnabledAsync("feature-t").block()) {
// Do Something
}
If Feature Management has not been configured or the Feature Flag doesn’t exist, isEnabledAsync
will always return false. If an existing Feature Flag is configured with an unknown Feature Filter, then a FilterNotFoundException
is thrown. This can be changed to return false by configuring fail-fast
to false.
Name | Description | Required | Default |
---|---|---|---|
spring.cloud.azure.feature.management.fail-fast |
If an exception occurs, a RuntimeException is thrown, if set to false then returns false instead. |
No |
true |
FeatureManagerSnapshot
works the same as FeatureManager
, besides its caching of results in the @RequestScope
.
7.2.2. Feature Gate
With the Feature Management Web library, you can require that a given feature is enabled in order to execute an endpoint. This can be done by using the @FeatureGate
annotation.
@GetMapping("/featureT")
@FeatureGate(feature = "feature-t")
@ResponseBody
public String featureT() {
...
}
The featureT
endpoint can only be accessed if "feature-t" is enabled.
Disabled Action Handling
When an endpoint is blocked because the feature it specifies is disabled, IDisabledFeaturesHandler
will be invoked. By default, an HTTP 404 is returned. This can be overridden by implementing IDisabledFeaturesHandler
.
@Component
public class DisabledFeaturesHandler implements IDisabledFeaturesHandler{
@Override
public HttpServletResponse handleDisabledFeatures(HttpServletRequest request, HttpServletResponse response) {
...
return response;
}
}
Routing
Certain routes may expose application capabilities that are gated by features. These routes can be redirected to another endpoint if a feature has been disabled.
@GetMapping("/featureT")
@FeatureGate(feature = "feature-t" fallback= "/oldEndpoint")
@ResponseBody
public String featureT() {
...
}
@GetMapping("/oldEndpoint")
@ResponseBody
public String oldEndpoint() {
...
}
7.3. Built-In Feature Filters
There are a few Feature Filters that come with the azure-spring-cloud-feature-management
package. These Feature Filters are not added automatically, but can be setup in an @Configuration
for use.
7.3.1. AlwaysOnFilter
This filter always returns true. Usage can be see in Feature Flag Declaration.
7.3.2. PercentageFilter
Each evaluation of PercentageFilter
can return a different result, which are not consistent among one user’s requests. This can be circumvented using the FeatureManagementSnapshot
, which will cache the result of the Feature Flag per user. This ensures a User will have a consistent experience even if they have to resend the request.
feature-management:
feature-v:
enabled-for:
-
name: PercentageFilter
parameters:
Value: 50
7.3.3. TimeWindowFilter
This filter provides the capability to enable a feature based on a time window. If only End
is specified, the feature will be considered on until that time. If only start is specified, the feature will be considered on at all points after that time. If both are specified the feature will be 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"
7.3.4. TargetingFilter
This filter provides the capability to enable a feature for a target audience. An in-depth explanation of targeting is explained in the targeting section below. 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 which defines the percentage of that group’s members which have access to the feature. If a user is specified in the users section directly, or if the user is in the included percentage of any of the group rollouts, or if the user falls into the default rollout percentage, then that user will have the feature enabled.
feature-management:
target:
enabled-for:
-
name: targetingFilter
parameters:
users:
- Jeff
- Alicia
groups:
-
name: Ring0
rolloutPercentage: 100
-
name: Ring1
rolloutPercentage: 100
defaultRolloutPercentage: 50
7.4. 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, the FeatureFilter
interface must be implemented. 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 the feature should be enabled. If false
it will continue evaluating the Feature’s filters until one returns true. If all return false
then the feature is off.
Feature Filters are found by being defined as being Spring Beans, so they are either defined as @Component
or defined in an @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;
}
}
7.4.1. Parameterized Feature Filters
Some Feature Filters require parameters to decide whether a feature should be turned on or not. For example, a browser Feature Filter may turn on a feature for a certain set of browsers. It may be desired that Edge and Chrome browsers enable a feature, while Firefox does not. To do this, a Feature Filter can be designed 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>
.
7.5. 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.
7.5.1. 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, it must be added as a @Bean
like any other Feature Filter. TargetingFilter
relies on another @Bean
to be added to the application, ITargetingContextAccessor
. The ITargetingContextAccessor
allows for defining the current TargetingContext
to be used for defining the current user id and groups. An example of this is:
public class TargetingContextAccessor implements ITargetingContextAccessor {
@Override
public Mono<TargetingContext> getContextAsync() {
TargetingContext context = new TargetingContext();
context.setUserId("Jeff");
ArrayList<String> groups = new ArrayList<String>();
groups.add("Ring0");
context.setGroups(groups);
return Mono.just(context);
}
}
7.5.2. Targeting Evaluation Options
Options are available to customize how targeting evaluation is performed across a given TargetingFilter
. An optional parameter, TargetingEvaluationOptions
can be set during TargetingFilter
creation.
@Bean
public TargetingFilter targetingFilter(ITargetingContextAccessor contextAccessor) {
return new TargetingFilter(contextAccessor, new TargetingEvaluationOptions().setIgnoreCase(true));
}
8. Configuration Refresh
Enabling config refresh for your configurations lets you pull their latest values from your App Configuration store(s) without having to restart the application.
To enable refresh, monitoring needs to be enabled along with monitoring triggers. A monitoring trigger is a key with an optional label that are checked for value change for triggering updates. The value of the monitoring trigger can be any value, as long as it changes when it a refresh is needed.
Any operation that changes the etag of a monitoring trigger will cause a refresh, such as a content-type change. |
spring.cloud.azure.appconfiguration.stores[0].monitoring.enabled=true
spring.cloud.azure.appconfiguration.stores[0].monitoring.triggers[0].key=[my-watched-key]
spring.cloud.azure.appconfiguration.stores[0].monitoring.triggers[0].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 will trigger the creation of a log. For example, changing the value of /application/config.message
triggers the log message below:
INFO 17496 --- [TaskScheduler-1] o.s.c.e.event.RefreshEventListener : Refresh keys changed: [config.message]
After generating the log, the application will refresh all @Bean
's in the refresh scope.
By default, @ConfiugrationProperties annotated beans will be included in this scope.
|
8.1. 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. Once the refresh interval has passed, all triggers will be checked in the given store for changes. Any change to the key will cause a refresh to trigger. Because the libraries integrate with the Spring refresh system, any refresh will reload all configurations from all stores. The refresh-interval can be set to any interval longer than 1 second. The supported units for the refresh-interval are s, m, h, d for seconds, minutes, hours, and days respectively.
spring.cloud.azure.appconfiguration.stores[0].monitoring.refresh-interval= 5m
8.1.1. Automated
When using the azure-spring-cloud-appconfiguration-config-web
library, the application will automatically check for refresh whenever a servlet request occurs, specifically ServletRequestHandledEvent
. The most common way this event is sent is by connections to endpoints in a @RestController
.
8.1.2. Manual
In applications that only use azure-spring-cloud-appconfiguration-config
, such as console applications, refresh can be manually triggered. This can be done by calling AppConfigurationRefresh’s refreshConfiguration method. `AppConfigurationRefresh
is a @Bean
that can be injected into any @Component
.
Also, because the library uses Spring’s configuration system, triggering a refresh will also cause a refresh of all of your configurations, not just reloading the ones from your Azure App Configuration store.
8.2. Push Based Refresh
The azure-spring-cloud-appconfiguration-config-web
library can be setup to receive push notifications from your Azure App Configuration store to refresh your configuration values. This is done via an Azure Event Grid Web Hook, which can be configured to send notifications of changes to specified keys. By adding the Spring Actuator library as a dependency you can expose App Configuration’s refresh endpoint(s). There are two different endpoints, appconfiguration-refresh
and appconfiguration-refresh-bus
these endpoints work sort of similarly to their counterparts refresh
and refresh-bus
, where the app configuration endpoints expire the refresh interval instead of forcing a refresh upon receiving. The refresh
and refresh-bus
can still be used, but can’t be directly connected to Azure Event Grid with a Web Hook as they require a response in setup.
appconfiguration-refresh
expires the refresh interval, so the remaining refresh interval isn’t waited on before the next refresh check. appconfiguration-refresh-bus
will send 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 the refresh interval, but almost does by a small jitter amount. This makes sure not 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.
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]
8.2.1. Setting Up Web Hooks
To setup a Web Hook open your Azure App Configuration store and select the events tab. Select "+ Event Subscription". Set the name of your Event and select the Endpoint type to be Web Hook. Selecting Web Hook will cause an Endpoint option to appear, select "Select an endpoint". Your endpoint will look like the following:
https://www.myaplication.com/actuator/appconfiguration-refresh?myTokenName=mySecret
In order to "Confirm Selection" will send a setup notification to the given URI and it expects a response. If no response is returned it will fail. The azure-spring-cloud-appconfiguration-web
library if setup for endpoints will return the correct response if the Azure App Configuration store is configured for the application. This confirmation can be sent in other ways see Event Grid Web Hook Delivery for more information on Web Hook validation.
This validation only happens on the creation/modification of the endpoint. |
It is highly recommended that filters are set up as otherwise a refresh will be triggered after every key creation and modification.
8.3. Forced Client Refresh
The library can be configured to force a refresh of all configurations at a refresh interval.
Name | Description | Required | Default |
---|---|---|---|
spring.cloud.azure.appconfiguration.refresh-interval |
Amount of time, of type Duration, configurations are stored before a check can occur. |
No |
null |
Refreshing with spring.cloud.azure.appconfiguration.refresh-interval
doesn’t check any configured watch keys. This is mainly used to make sure Key Vault Secrets are kept up to date, as Azure App Configuration can’t tell when they are updated.
Since 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.
8.4. Feature Flag Refresh
If Feature Flags and Monitoring are both enabled, then by default the refresh interval for feature flags is set to 30s. Once the refresh interval has passed, all feature flags will be checked in the given store for changes. Any change to the key will cause a refresh to trigger. Because the libraries integrate with the Spring refresh system, any refresh will reload all configurations from all stores. The refresh-interval can be set to any interval longer than 1 second. The supported units for the refresh-interval are s, m, h, d for seconds, minutes, hours, and days respectively.
spring.cloud.azure.appconfiguration.stores[0].monitoring.feature-flag-refresh-interval= 5m
9. Health Indicator
The client library comes with a Health Indicator that checks whether the connection to the Azure App Configuration store(s) is healthy or not. If enabled for each store, it gives a status of:
-
UP - The last connection was successful
-
DOWN- The last connection resulted in a non 200 error code. This could be due to a number of issues ranging from credentials expiring to a service issue. The client library will automatically retry 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 from the file at startup. Either the config store was disabled by configuration. Otherwise, the configuration(s) loading failed to load at startup while the
fail-fast
configuration for the store was set to false.
You can enable the Health Indicator by setting management.health.azure-app-configuration.enabled=true
.
10. Client Customization
The App Configuration library uses the Azure SDK for Java for connecting to Azure App Configuration and Azure Key Vault. Two interfaces, ConfigurationClientBuilderSetup
and SecretClientBuilderSetup
are provided to modify the clients. Each interface has a setup
method that takes in their respective builder along with the String
value of the URI that the client will connect to.
public interface ConfigurationClientBuilderSetup {
public void setup(ConfigurationClientBuilder builder, String endpoint);
}
public interface SecretClientBuilderSetup {
public void setup(SecretClientBuilder builder, String uri);
}
These interfaces allow for customization of the http client and its configurations, for example, by replacing the default HttpClient
with another that uses a proxy for all traffic directed to App Configuration and Key Vault. This example can be seen below
The ConfigurationClientBuilder and SecretClientBuilder are already setup for use when passed into setup . Any changes to the clients, including the credentials and retry policy will override those already in place.
|
public class CustomClient implements ConfigurationClientBuilderSetup, SecretClientBuilderSetup {
@Override
public void setup(ConfigurationClientBuilder builder, String endpoint) {
builder.httpClient(buildHttpClient());
}
@Override
public void setup(SecretClientBuilder builder, String uri) {
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();
}
}
11. For more information
See Starter Readme for more details and compete documentation of all settings.