© 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.

Table 1. GitHub repositories
GitHub repositories Description

Azure/azure-sdk-for-java

This repository used to hold the source code.

github.com/Azure/AppConfiguration

This repository used to hold release notes, and general Azure App Configuration Issues.

microsoft/spring-cloud-azure

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 configured 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 supports the following filters:

Table 2. Key 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 5 CSV)

label-filter supports the following filters:

Table 3. Label Filters
Label Description

*

Matches any label, which includes \0

\0

Matches null labels, seen as (No Label) in the 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 or 1.0.0, limited to five CSVs

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.

  1. Adding the Identity to the resource connecting to your App Configuration store

  2. Adding the required information to your client application.

  3. 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 the enabled-for property. In this case, feature-u has one Feature Filter called Random, which does not require any configuration, so only the name property is required.

  • feature-v specifies a Feature Filter named TimeWindowFilter. This Feature Filter can be passed parameters to use as configuration. In this case, for a TimeWindowFilter, pass in the start and end times for which the feature will be active.

  • feature-w is used for the AlwaysOnFilter, which will always evaluate to true. The evaluate field is used to stop the evaluation of the Feature Filters, and results in the Feature Filter always return false.

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:

  1. Individual users Jeff and Alicia are granted access to the Beta

  2. Another user, Mark, asks to opt-in and is included.

  3. Twenty percent of a group known as "Ring1" users are included in the Beta.

  4. The number of "Ring1" users included in the beta is bumped up to 100 percent.

  5. Five percent of the user base is included in the beta.

  6. 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.