Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/src/main/asciidoc/parameter-store.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,30 @@ With such config, properties `spring.datasource.url` and `spring.datasource.user
NOTE: Prefixes are added as-is to all property names returned by Parameter Store. If you want key names to be separated with a dot between the prefix and key name, make sure to add a trailing dot to the prefix.


Sometimes it is useful to group multiple properties in a text-based format, similar to application.properties. With Spring Cloud AWS, you can load a Parameter Store parameter as a text-based key/value configuration by using the spring.config.import property with the ?properties suffix:

[source,properties]
----
spring.config.import=aws-parameterstore:/config/my-datasource/?properties
----

All parameters stored under this path will be interpreted as key/value pairs. For example, if the value of a parameter is:

[source,properties]
----
spring.cloud.aws.region=eu-central-1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use different example properties to not confuse readers that it has something to do with Spring Cloud AWS region and endpoint configuration?

spring.cloud.aws.endpoint=randomEndpoint
----

Spring Cloud AWS will automatically load these as:
Key: `spring.cloud.aws.region`, Value: `eu-central-1`
Key: `spring.cloud.aws.endpoint`, Value: `randomEndpoint`

NOTE: Standard Parameter Store parameters are limited to 4 KB of data.

This approach allows you to maintain multiple related properties in a single parameter, making configuration management simpler and more organized.


=== Using SsmClient

The starter automatically configures and registers a `SsmClient` bean in the Spring application context. The `SsmClient` bean can be used to create or retrieve parameters from Parameter Store.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,28 @@ void resolvesPropertiesWithPrefixes() {
}
}

@Test
void resolvesPropertiesWithPrefixProperties() {
SpringApplication application = new SpringApplication(App.class);
application.setWebApplicationType(WebApplicationType.NONE);
String applicationProperties = """
first.message=value from tests
first.another-parameter=another parameter value
second.secondMessage=second value from tests
""";
putParameter(localstack, "/test/path/secondMessage", applicationProperties, REGION);

try (ConfigurableApplicationContext context = runApplication(application,
"aws-parameterstore:/test/path/?properties")) {
assertThat(context.getEnvironment().getProperty("first.message")).isEqualTo("value from tests");
assertThat(context.getEnvironment().getProperty("first.another-parameter"))
.isEqualTo("another parameter value");
assertThat(context.getEnvironment().getProperty("second.secondMessage"))
.isEqualTo("second value from tests");
assertThat(context.getEnvironment().getProperty("non-existing-parameter")).isNull();
}
}

@Test
void clientIsConfiguredWithCustomizerProvidedToBootstrapRegistry() {
SpringApplication application = new SpringApplication(App.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
package io.awspring.cloud.parameterstore;

import io.awspring.cloud.core.config.AwsPropertySource;
import java.io.InputStream;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
Expand All @@ -41,6 +44,8 @@ public class ParameterStorePropertySource extends AwsPropertySource<ParameterSto
// ParameterStoreConfigDataLoader
private static Log LOG = LogFactory.getLog(ParameterStorePropertySource.class);
private static final String PREFIX_PART = "?prefix=";

private static final String PREFIX_PROPERTIES_LOAD = "?properties";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that a notation you came up with or other Spring projects follow it too?

private final String context;

private final String parameterPath;
Expand All @@ -52,6 +57,8 @@ public class ParameterStorePropertySource extends AwsPropertySource<ParameterSto
@Nullable
private final String prefix;

private Boolean propertiesType = false;

private final Map<String, Object> properties = new LinkedHashMap<>();

public ParameterStorePropertySource(String context, SsmClient ssmClient) {
Expand Down Expand Up @@ -87,11 +94,20 @@ public Object getProperty(String name) {
private void getParameters(GetParametersByPathRequest paramsRequest) {
GetParametersByPathResponse paramsResult = this.source.getParametersByPath(paramsRequest);
for (Parameter parameter : paramsResult.parameters()) {
String key = parameter.name().replace(this.parameterPath, "").replace('/', '.').replaceAll("_(\\d)_",
"[$1]");
LOG.debug("Populating property retrieved from AWS Parameter Store: " + key);
String propertyKey = prefix != null ? prefix + key : key;
this.properties.put(propertyKey, parameter.value());
if (propertiesType) {
Arrays.stream(parameter.value().split("\\n")).map(line -> line.split("=", 2)).forEach(keyValue -> {
if (keyValue.length == 2) {
this.properties.put(keyValue[0].trim(), keyValue[1].trim());
}
});
}
else {
String key = parameter.name().replace(this.parameterPath, "").replace('/', '.').replaceAll("_(\\d)_",
"[$1]");
LOG.debug("Populating property retrieved from AWS Parameter Store: " + key);
String propertyKey = prefix != null ? prefix + key : key;
this.properties.put(propertyKey, parameter.value());
}
}
if (paramsResult.nextToken() != null) {
getParameters(paramsRequest.toBuilder().nextToken(paramsResult.nextToken()).build());
Expand All @@ -112,20 +128,36 @@ String getParameterPath() {
}

@Nullable
private static String resolvePrefix(String context) {
private String resolvePrefix(String context) {
int prefixIndex = context.indexOf(PREFIX_PART);
if (prefixIndex != -1) {
return context.substring(prefixIndex + PREFIX_PART.length());
}
return null;
}

private static String resolveParameterPath(String context) {
private String resolveParameterPath(String context) {
int prefixIndex = context.indexOf(PREFIX_PART);
if (prefixIndex != -1) {
return context.substring(0, prefixIndex);
}
prefixIndex = context.indexOf(PREFIX_PROPERTIES_LOAD);
if (prefixIndex != -1) {
this.propertiesType = true;
return context.substring(0, prefixIndex);
}
return context;
}

private Properties readProperties(InputStream inputStream) {
Properties properties = new Properties();
try (InputStream in = inputStream) {
properties.load(in);
}
catch (Exception e) {
throw new IllegalStateException("Cannot load environment", e);
}
return properties;
}

}