Skip to content

Parameters

The parameters utilities provide a way to retrieve parameter values from AWS Systems Manager Parameter Store, AWS Secrets Manager, Amazon DynamoDB, or AWS AppConfig.

Key features

  • 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

Install

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.

Note that you must provide the concrete parameters module you want to use below - see the TODOs!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<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>2.0.0-SNAPSHOT</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>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    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:2.0.0-SNAPSHOT'
    }

    sourceCompatibility = 11 // or higher
    targetCompatibility = 11 // or higher

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

Retrieving Parameters

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.

Built-in provider classes

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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) {
        // ... do something with the secretParam here
        return "something";
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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");

        // ... do something with the secretParam here
        return "something";
    }
}

SSM Parameter Store

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.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 param
        String value = ssmProvider
                .get("/my/secret");
                // We might instead want to retrieve multiple parameters at once, returning a Map of key/value pairs
                // .getMultiple("/my/secret/path");

        // Return the result
        return value;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 DynamoDbProvider. 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 param
        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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 AppConfigProvider. 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 param
        String value = appConfigProvider
                .get("/my/secret");

        // Return the result
        return value;
    }
}    

Advanced configuration

Caching

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;
    }
}

Transform values

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.

getMultiple() does not support transformation and will return simple Strings.

1
2
3
   String value = provider
                    .withTransformation(Transformer.base64)
                    .get("/my/parameter/b64");
1
2
3
   MyObj object = provider
                    .withTransformation(Transformer.json)
                    .get("/my/parameter/json", MyObj.class);

Create your own Transformer

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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);
        }
    }
}
1
2
3
    MyObj object = provider
                        .withTransformation(XmlTransformer.class)
                        .get("/my/parameter/xml", MyObj.class);

Fluent API

To simplify the use of the library, you can chain all method calls before a get.

1
2
3
4
5
    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

Create your own Provider

You can create your own custom parameter store provider by implementing a handful of classes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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");
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 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;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 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());
    }

}