title | description |
---|---|
Parameters |
Utility |
The parameters utilities provide a way to retrieve parameter values from AWS Systems Manager Parameter Store, AWS Secrets Manager, Amazon DynamoDB, or AWS AppConfig.
- Retrieve one or multiple parameters from an underlying provider in a standard way
- Cache parameter values for a given amount of time (defaults to 5 seconds)
- Transform parameter values from JSON or base 64 encoded strings
In order to provide lightweight dependencies, each parameters module is available as its own package:
- Secrets Manager -
powertools-parameters-secrets
- SSM Parameter Store -
powertools-parameters-ssm
- Amazon DynamoDB -
powertools-parameters-dynamodb
- AWS AppConfig -
powertools-parameters-appconfig
You can easily mix and match parameter providers within the same project for different needs.
Depending on which Java version you are using, you configuration will differ. Note that you must also provide the concrete parameters module you want to use below - see the TODOs!
=== "Maven Java 11+"
```xml hl_lines="4-12 17 24 30-34"
<dependencies>
...
<dependency>
<groupId>software.amazon.lambda</groupId>
<!-- TODO! Provide the parameters module you want to use here -->
<artifactId>powertools-parameters-secrets</artifactId>
<artifactId>powertools-parameters-ssm</artifactId>
<artifactId>powertools-parameters-dynamodb</artifactId>
<artifactId>powertools-parameters-appconfig</artifactId>
<version>{{ powertools.version }}</version>
</dependency>
...
</dependencies>
...
<!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project -->
<build>
<plugins>
...
<plugin>
<groupId>dev.aspectj</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.13.1</version>
<configuration>
<source>11</source> <!-- or higher -->
<target>11</target> <!-- or higher -->
<complianceLevel>11</complianceLevel> <!-- or higher -->
<aspectLibraries>
<!-- TODO! Provide an aspectLibrary for each of the parameters module(s) you want to use here -->
<aspectLibrary>
<groupId>software.amazon.lambda</groupId>
<artifactId>powertools-parameters-secrets</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
```
=== "Maven Java 1.8"
```xml hl_lines="4-12 17 24 30-34"
<dependencies>
...
<dependency>
<groupId>software.amazon.lambda</groupId>
<!-- TODO! Provide the parameters module you want to use here -->
<artifactId>powertools-parameters-secrets</artifactId>
<artifactId>powertools-parameters-ssm</artifactId>
<artifactId>powertools-parameters-dynamodb</artifactId>
<artifactId>powertools-parameters-appconfig</artifactId>
<version>{{ powertools.version }}</version>
</dependency>
...
</dependencies>
...
<!-- configure the aspectj-maven-plugin to compile-time weave (CTW) the aws-lambda-powertools-java aspects into your project -->
<build>
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
<aspectLibraries>
<!-- TODO! Provide an aspectLibrary for each of the parameters module(s) you want to use here -->
<aspectLibrary>
<groupId>software.amazon.lambda</groupId>
<artifactId>powertools-parameters-secrets</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
```
=== "Gradle Java 11+"
```groovy hl_lines="3 11 12"
plugins {
id 'java'
id 'io.freefair.aspectj.post-compile-weaving' version '8.1.0'
}
repositories {
mavenCentral()
}
dependencies {
// TODO! Provide the parameters module you want to use here
aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}'
}
sourceCompatibility = 11 // or higher
targetCompatibility = 11 // or higher
```
=== "Gradle Java 1.8"
```groovy hl_lines="3 11 12"
plugins {
id 'java'
id 'io.freefair.aspectj.post-compile-weaving' version '6.6.3'
}
repositories {
mavenCentral()
}
// TODO! Provide an aspectLibrary for each of the parameters module(s) you want to use here
dependencies {
aspect 'software.amazon.lambda:powertools-parameters-secrets:{{ powertools.version }}'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
```
IAM Permissions
This utility requires additional permissions to work as expected. See the table below:
Provider | Function/Method | IAM Permission |
---|---|---|
SSM | SSMProvider.get(String) SSMProvider.get(String, Class) |
ssm:GetParameter |
SSM | SSMProvider.getMultiple(String) |
ssm:GetParametersByPath |
SSM | If using withDecryption(true) |
You must add an additional permission kms:Decrypt |
Secrets | SecretsProvider.get(String) SecretsProvider.get(String, Class) |
secretsmanager:GetSecretValue |
DynamoDB | DynamoDBProvider.get(String) DynamoDBProvider.getMultiple(string) |
dynamodb:GetItem dynamoDB:Query |
AppConfig | AppConfigProvider.get(String) AppConfigProvider.getMultiple(string) |
appconfig:StartConfigurationSession , appConfig:GetLatestConfiguration |
You can retrieve parameters either using annotations or by using the xParamProvider
class for each parameter
provider directly. The latter is useful if you need to configure the underlying SDK client, for example to use
a different region or credentials, the former is simpler to use.
This section describes the built-in provider classes for each parameter store, providing examples showing how to inject parameters using annotations, and how to use the provider interface. In cases where a provider supports extra features, these will also be described.
=== "Secrets Manager: @SecretsParam"
```java hl_lines="8 9"
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.lambda.powertools.parameters.secrets.SecretsParam;
public class ParametersFunction implements RequestHandler<String, String> {
// Annotation-style injection from secrets manager
@SecretsParam(key = "/powertools-java/userpwd")
String secretParam;
public string handleRequest(String request, Context context) {
// Because this is a secret, we probably don't want to return it! Return something indicating
// we could access it instead.
return "Retrieved a secret, and sensibly refusing to return it!";
}
}
```
=== "Secrets Manager: SecretsProvider"
```java hl_lines="12-15 19"
import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64;
import com.amazonaws.services.lambda.runtime.Context;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class RequestHandlerWithParams implements RequestHandler<String, String> {
// Get an instance of the SecretsProvider. We can provide a custom client here if we want,
// for instance to use a particular region.
SecretsProvider secretsProvider = SecretsProvider
.builder()
.withClient(SecretsManagerClient.builder().build())
.build();
public String handleRequest(String input, Context context) {
// Retrieve a single secret
String value = secretsProvider.get("/my/secret");
// Because this is a secret, we probably don't want to return it! Return something indicating
// we could access it instead.
return "Retrieved a secret, and sensibly refusing to return it!";
}
}
```
The AWS Systems Manager Parameter Store provider supports two additional arguments for the get()
and getMultiple()
methods:
Option | Default | Description |
---|---|---|
withDecryption() | False |
Will automatically decrypt the parameter. |
recursive() | False |
For getMultiple() only, will fetch all parameter values recursively based on a path prefix. |
=== "SSM Parameter Store: @SSMParam"
```java hl_lines="8 9"
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.lambda.powertools.parameters.ssm.SSMParam;
public class ParametersFunction implements RequestHandler<String, String> {
// Annotation-style injection from SSM Parameter Store
@SSMParam(key = "/powertools-java/param")
String ssmParam;
public string handleRequest(String request, Context context) {
return ssmParam; // Request handler simply returns our configuration value
}
}
```
=== "SSM Parameter Store: SSMProvider"
```java hl_lines="12-15 19-20 22"
import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.services.ssm.SsmClient;
import software.amazon.lambda.powertools.parameters.ssm.SSMProvider;
public class RequestHandlerWithParams implements RequestHandler<String, String> {
// Get an instance of the SSMProvider. We can provide a custom client here if we want,
// for instance to use a particular region.
SSMProvider ssmProvider = SSMProvider
.builder()
.withClient(SsmClient.builder().build())
.build();
public String handleRequest(String input, Context context) {
// Retrieve a single secret
String value = ssmProvider
.get("/my/secret");
// We might instead want to retrieve multiple secrets at once, returning a Map of key/value pairs
// .getMultiple("/my/secret/path");
// Return the result
return value;
}
}
```
=== "SSM Parameter Store: Additional Options"
```java hl_lines="9 12"
import software.amazon.lambda.powertools.parameters.SSMProvider;
import software.amazon.lambda.powertools.parameters.ParamManager;
public class AppWithSSM implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
// Get an instance of the SSM Provider
SSMProvider ssmProvider = ParamManager.getSsmProvider();
// Retrieve a single parameter and decrypt it
String value = ssmProvider.withDecryption().get("/my/parameter");
// Retrieve multiple parameters recursively from a path prefix
Map<String, String> values = ssmProvider.recursive().getMultiple("/my/path/prefix");
}
```
=== "DynamoDB: @DyanmoDbParam"
```java hl_lines="8 9"
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.lambda.powertools.parameters.dynamodb.DynamoDBParam;
public class ParametersFunction implements RequestHandler<String, String> {
// Annotation-style injection from DynamoDB
@DynamoDbParam(table = "my-test-tablename", key = "myKey")
String ddbParam;
public string handleRequest(String request, Context context) {
return ddbParam; // Request handler simply returns our configuration value
}
}
```
=== "DynamoDB: DynamoDbProvider"
```java hl_lines="12-15 19-20 22"
import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.lambda.powertools.parameters.dynamodb.DynamoDbProvider;
public class RequestHandlerWithParams implements RequestHandler<String, String> {
// Get an instance of the SecretsProvider. We can provide a custom client here if we want,
// for instance to use a particular region.
DynamoDbProvider ddbProvider = DynamoDbProvider
.builder()
.withClient(DynamoDbClient.builder().build())
.build();
public String handleRequest(String input, Context context) {
// Retrieve a single secret
String value = ddbProvider
.get("/my/secret");
// We might instead want to retrieve multiple values at once, returning a Map of key/value pairs
// .getMultiple("my-partition-key-value");
// Return the result
return value;
}
}
```
=== "AppConfig: @AppConfigParam"
```java hl_lines="8 9"
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.lambda.powertools.parameters.appconfig.AppConfigParam;
public class ParametersFunction implements RequestHandler<String, String> {
// Annotation-style injection from AppConfig
@AppConfigParam(application = "my-app", environment = "my-env", key = "myKey")
String appConfigParam;
public string handleRequest(String request, Context context) {
return appConfigParam; // Request handler simply returns our configuration value
}
}
```
=== "AppConfig: AppConfigProvider"
```java hl_lines="12-15 19-20"
import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
import software.amazon.lambda.powertools.parameters.appconfig.AppConfigProvider;
public class RequestHandlerWithParams implements RequestHandler<String, String> {
// Get an instance of the SecretsProvider. We can provide a custom client here if we want,
// for instance to use a particular region.
AppConfigProvider appConfigProvider = AppConfigProvider
.builder()
.withClient(AppConfigDataClient.builder().build())
.build();
public String handleRequest(String input, Context context) {
// Retrieve a single secret
String value = appConfigProvider
.get("/my/secret");
// Return the result
return value;
}
}
```
Each provider uses the CacheManager
to cache parameter values. When a value is retrieved using from the provider, a
custom cache duration can be provided using withMaxAge(duration, unit)
.
If this is not specified, the default value set on the CacheManager
itself will be used. This default can be customized
by calling setDefaultExpirationTime(duration, unit)
on the CacheManager
.
=== "Customize Cache"
```java hl_lines="9 10 14 19 22-25"
import java.time.Duration;
import software.amazon.lambda.powertools.parameters.appconfig.AppConfigProvider;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
public class CustomizeCache {
public void CustomizeCache() {
CacheManager cacheManager = new CacheManager();
cacheManager.setDefaultExpirationTime(Duration.ofSeconds(10));
AppConfigProvider paramProvider = AppConfigProvider
.builder()
.withCacheManager(cacheManager)
.withClient(AppConfigDataClient.builder().build())
.build();
// Will use the default specified above - 10 seconds
String myParam1 = paramProvider.get("myParam1");
// Will override the default above
String myParam2 = paramProvider
.withMaxAge(20, ChronoUnit.SECONDS)
.get("myParam2");
return myParam2;
}
}
```
Parameter values can be transformed using withTransformation(transformerClass)
.
Base64 and JSON transformations are provided. For more complex transformation, you need to specify how to deserialize.
!!! warning "getMultiple()
does not support transformation and will return simple Strings."
=== "Base64 Transformation"
java String value = provider .withTransformation(Transformer.base64) .get("/my/parameter/b64");
=== "Complex Transformation"
```java
MyObj object = provider
.withTransformation(Transformer.json)
.get("/my/parameter/json", MyObj.class);
```
You can write your own transformer, by implementing the Transformer
interface and the applyTransformation()
method.
For example, if you wish to deserialize XML into an object.
=== "XmlTransformer.java"
```java
public class XmlTransformer<T> implements Transformer<T> {
private final XmlMapper mapper = new XmlMapper();
@Override
public T applyTransformation(String value, Class<T> targetClass) throws TransformationException {
try {
return mapper.readValue(value, targetClass);
} catch (IOException e) {
throw new TransformationException(e);
}
}
}
```
=== "Using XmlTransformer"
```java
MyObj object = provider
.withTransformation(XmlTransformer.class)
.get("/my/parameter/xml", MyObj.class);
```
To simplify the use of the library, you can chain all method calls before a get.
=== "Fluent API call"
```java
ssmProvider
.withMaxAge(1, MINUTES) // will set the cache TTL for this value at 1 minute
.withTransformation(json) // json is a static import from Transformer.json
.withDecryption() // enable decryption of the parameter value
.get("/my/param", MyObj.class); // finally get the value
You can create your own custom parameter store provider by implementing a handful of classes:
=== "CustomProvider.java"
```java
import java.util.Map;
import software.amazon.lambda.powertools.parameters.BaseProvider;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
/**
* Our custom parameter provider itself. This does the heavy lifting of retrieving
* parameters from whatever our underlying parameter store might be.
**/
public class CustomProvider extends BaseProvider {
public CustomProvider(CacheManager cacheManager, TransformationManager transformationManager) {
super(cacheManager, transformationManager);
}
public CustomProviderBuilder builder() {
return new CustomProviderBuilder();
}
@Override
protected String getValue(String key) {
throw new RuntimeException("TODO - return a single value");
}
@Override
protected Map<String, String> getMultipleValues(String path) {
throw new RuntimeException("TODO - Optional - return multiple values");
}
}
```
=== "CustomProviderBuilder.java"
```java
/**
* Provides a builder-style interface to configure our @{link CustomProvider}.
**/
public class CustomProviderBuilder {
private CacheManager cacheManager;
private TransformationManager transformationManager;
/**
* Create a {@link CustomProvider} instance.
*
* @return a {@link CustomProvider}
*/
public CustomProvider build() {
if (cacheManager == null) {
cacheManager = new CacheManager();
}
return new CustomProvider(cacheManager, transformationManager);
}
/**
* Provide a CacheManager to the {@link CustomProvider}
*
* @param cacheManager the manager that will handle the cache of parameters
* @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>)
*/
public CustomProviderBuilder withCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
return this;
}
/**
* Provide a transformationManager to the {@link CustomProvider}
*
* @param transformationManager the manager that will handle transformation of parameters
* @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>)
*/
public CustomProviderBuilder withTransformationManager(TransformationManager transformationManager) {
this.transformationManager = transformationManager;
return this;
}
}
```
=== "CustomProviderParam.java"
```java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import software.amazon.lambda.powertools.parameters.transform.Transformer;
/**
* Aspect to inject a parameter from our custom provider. Note that if you
* want to implement a provider _without_ an Aspect and field injection, you can
* skip implementing both this and the {@link CustomProviderAspect} class.
**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CustomProviderParam {
// The parameter key
String key();
// The transformer to use
Class<? extends Transformer> transformer() default Transformer.class;
}
```
=== "CustomProviderAspect.java"
```java
/**
* Aspect to inject a parameter from our custom provider where the {@link CustomProviderParam}
* annotation is used.
**/
@Aspect
public class CustomProviderAspect extends BaseParamAspect {
@Pointcut("get(* *) && @annotation(ddbConfigParam)")
public void getParam(CustomProviderParam customConfigParam) {
}
@Around("getParam(customConfigParam)")
public Object injectParam(final ProceedingJoinPoint joinPoint, final CustomProviderParam customConfigParam) {
BaseProvider provider = CustomProvider.builder().build();
return getAndTransform(customConfigParam.key(), ddbConfigParam.transformer(), provider,
(FieldSignature) joinPoint.getSignature());
}
}
```