Skip to content
Closed
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
69 changes: 57 additions & 12 deletions docs/utilities/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ description: Utility
---


The parameters utility provides a way to retrieve parameter values from
[AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) or
[AWS Secrets Manager](https://aws.amazon.com/secrets-manager/). It also provides a base class to create your parameter provider implementation.
The parameters utility provides a way to retrieve parameter values from:

- [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html)
- [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/)
- [AWS AppConfig](https://aws.amazon.com/systems-manager/features/appconfig/)

It also provides a base class to create your parameter provider implementation.

**Key features**

Expand Down Expand Up @@ -40,11 +44,12 @@ To install this utility, add the following dependency to your project.

This utility requires additional permissions to work as expected. See the table below:

Provider | Function/Method | IAM Permission
------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------
SSM Parameter Store | `SSMProvider.get(String)` `SSMProvider.get(String, Class)` | `ssm:GetParameter`
SSM Parameter Store | `SSMProvider.getMultiple(String)` | `ssm:GetParametersByPath`
Secrets Manager | `SecretsProvider.get(String)` `SecretsProvider.get(String, Class)` | `secretsmanager:GetSecretValue`
| Provider | Function/Method | IAM Permission |
|---------------------|------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| SSM Parameter Store | `SSMProvider.get(String)` `SSMProvider.get(String, Class)` | `ssm:GetParameter` |
| SSM Parameter Store | `SSMProvider.getMultiple(String)` | `ssm:GetParametersByPath` |
| Secrets Manager | `SecretsProvider.get(String)` `SecretsProvider.get(String, Class)` | `secretsmanager:GetSecretValue` |
| AppConfig | `AppConfigProvider.get(String)` `AppConfigProvider.get(String, Class)` | `appconfig:GetLatestConfiguration` & `appconfig:StartConfigurationSession` |

## SSM Parameter Store

Expand Down Expand Up @@ -99,10 +104,10 @@ in order to get data from other regions or use specific credentials.

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

**Example:**

Expand Down Expand Up @@ -166,6 +171,46 @@ in order to get data from other regions or use specific credentials.
}
```

## AppConfig

!!! info
See how to create configuration in AppConfig in the [documentation](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-working.html).

=== "AppConfigProvider"

```java hl_lines="5 9-10"
import software.amazon.lambda.powertools.parameters.AppConfigProvider;
import software.amazon.lambda.powertools.parameters.ParamManager;

public class AppWithConfig implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
// Get an instance of the AppConfig Provider
AppConfigProvider appConfigProvider = ParamManager.getAppConfigProvider();

// Retrieve some configuration
// The key must be in form /application/environment/configuration and match your AppConfig setup
String value = appConfigProvider.get("/app/prod/config");

}
```

=== "AppConfigProvider with a custom client"

```java hl_lines="5 8 11"
import software.amazon.lambda.powertools.parameters.AppConfigProvider;
import software.amazon.lambda.powertools.parameters.ParamManager;

public class AppWithConfig implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
AppConfigDataClient client = AppConfigDataClient.builder().region(Region.EU_CENTRAL_1).build();

// Get an instance of the AppConfig Provider
AppConfigProvider appConfigProvider = ParamManager.getAppConfigProvider(client);

// Retrieve a single secret
String value = appConfigProvider.get("/app/prod/config");

}
```

## Advanced configuration

### Caching
Expand Down
44 changes: 43 additions & 1 deletion powertools-parameters/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfigdata</artifactId>
<exclusions>
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</exclusion>
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
Expand Down Expand Up @@ -118,6 +132,34 @@
<artifactId>aspectjweaver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
<id>jdk16</id>
<activation>
<jdk>[16,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package software.amazon.lambda.powertools.parameters;

import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationRequest;
import software.amazon.awssdk.services.appconfigdata.model.GetLatestConfigurationResponse;
import software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionRequest;
import software.amazon.awssdk.services.appconfigdata.model.StartConfigurationSessionResponse;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
import software.amazon.lambda.powertools.parameters.transform.TransformationManager;
import software.amazon.lambda.powertools.parameters.transform.Transformer;

import java.time.temporal.ChronoUnit;
import java.util.Map;

public class AppConfigProvider extends BaseProvider {

private AppConfigDataClient client;

AppConfigProvider(CacheManager cacheManager) {
this(cacheManager, defaultClient());
}

AppConfigProvider(CacheManager cacheManager, AppConfigDataClient client) {
super(cacheManager);
this.client = client;
}

/**
* Create a builder that can be used to configure and create a {@link AppConfigProvider}.
*
* @return a new instance of {@link Builder}
*/
public static Builder builder() {
return new Builder();
}

@Override
protected String getValue(String key) {
String[] profile = key.split("/");
if (profile.length < 3) {
throw new IllegalArgumentException("Your key is incorrect, please specify an 'application', an 'environment' and the 'configuration' separated with '/', eg. '/myapp/prod/myvar'");
}
int index = key.startsWith("/") ? 1 : 0;
String application = profile[index];
String environment = profile[index + 1];
String configuration = profile[index + 2];

return getValueWithClient(application, environment, configuration);
}

private String getValueWithClient(String application, String environment, String configuration) {
StartConfigurationSessionResponse sessionResponse = client.startConfigurationSession(StartConfigurationSessionRequest.builder()
.applicationIdentifier(application)
.environmentIdentifier(environment)
.configurationProfileIdentifier(configuration)
.build());
GetLatestConfigurationResponse configurationResponse = client.getLatestConfiguration(GetLatestConfigurationRequest.builder()
.configurationToken(sessionResponse.initialConfigurationToken())
.build());
return configurationResponse.configuration().asUtf8String();
}

/**
* @throws UnsupportedOperationException as it is not possible to get multiple values simultaneously from App Config
*/
@Override
protected Map<String, String> getMultipleValues(String path) {
throw new UnsupportedOperationException("Impossible to get multiple values from AWS Secrets Manager");
}

/**
* {@inheritDoc}
*/
@Override
public AppConfigProvider defaultMaxAge(int maxAge, ChronoUnit unit) {
super.defaultMaxAge(maxAge, unit);
return this;
}

/**
* {@inheritDoc}
*/
@Override
public AppConfigProvider withMaxAge(int maxAge, ChronoUnit unit) {
super.withMaxAge(maxAge, unit);
return this;
}

/**
* {@inheritDoc}
*/
@Override
public AppConfigProvider withTransformation(Class<? extends Transformer> transformerClass) {
super.withTransformation(transformerClass);
return this;
}

private static AppConfigDataClient defaultClient() {
return AppConfigDataClient.builder()
.httpClientBuilder(UrlConnectionHttpClient.builder())
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
.build();
}

static class Builder {

private AppConfigDataClient client;
private CacheManager cacheManager;
private TransformationManager transformationManager;

/**
* Create a {@link AppConfigProvider} instance.
*
* @return a {@link AppConfigProvider}
*/
public AppConfigProvider build() {
if (cacheManager == null) {
throw new IllegalStateException("No CacheManager provided, please provide one");
}
AppConfigProvider provider;
if (client != null) {
provider = new AppConfigProvider(cacheManager, client);
} else {
provider = new AppConfigProvider(cacheManager);
}
if (transformationManager != null) {
provider.setTransformationManager(transformationManager);
}
return provider;
}

/**
* Set custom {@link AppConfigDataClient} to pass to the {@link AppConfigProvider}. <br/>
* Use it if you want to customize the region or any other part of the client.
*
* @param client Custom client
* @return the builder to chain calls (eg. <pre>builder.withClient().build()</pre>)
*/
public Builder withClient(AppConfigDataClient client) {
this.client = client;
return this;
}

/**
* <b>Mandatory</b>. Provide a CacheManager to the {@link AppConfigProvider}
*
* @param cacheManager the manager that will handle the cache of parameters
* @return the builder to chain calls (eg. <pre>builder.withCacheManager().build()</pre>)
*/
public Builder withCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
return this;
}

/**
* Provide a transformationManager to the {@link AppConfigProvider}
*
* @param transformationManager the manager that will handle transformation of parameters
* @return the builder to chain calls (eg. <pre>builder.withTransformationManager().build()</pre>)
*/
public Builder withTransformationManager(TransformationManager transformationManager) {
this.transformationManager = transformationManager;
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package software.amazon.lambda.powertools.parameters;

import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.ssm.SsmClient;
import software.amazon.lambda.powertools.parameters.cache.CacheManager;
Expand All @@ -36,7 +37,7 @@ public final class ParamManager {

/**
* Get a concrete implementation of {@link BaseProvider}.<br/>
* You can specify {@link SecretsProvider} or {@link SSMProvider} or create your custom provider
* You can specify {@link SecretsProvider} or {@link SSMProvider} or {@link AppConfigProvider} or create your custom provider
* by extending {@link BaseProvider} if you need to integrate with a different parameter store.
* @return a {@link SecretsProvider}
*/
Expand Down Expand Up @@ -65,6 +66,15 @@ public static SSMProvider getSsmProvider() {
return getProvider(SSMProvider.class);
}

/**
* Get a {@link AppConfigProvider} with default {@link AppConfigDataClient}.<br/>
* If you need to customize the region, or other part of the client, use {@link ParamManager#getAppConfigProvider(AppConfigDataClient)} instead.
* @return a {@link AppConfigProvider}
*/
public static AppConfigProvider getAppConfigProvider() {
return getProvider(AppConfigProvider.class);
}

/**
* Get a {@link SecretsProvider} with your custom {@link SecretsManagerClient}.<br/>
* Use this to configure region or other part of the client. Use {@link ParamManager#getSsmProvider()} if you don't need this customization.
Expand All @@ -91,6 +101,19 @@ public static SSMProvider getSsmProvider(SsmClient client) {
.build());
}

/**
* Get a {@link AppConfigProvider} with your custom {@link AppConfigDataClient}.<br/>
* Use this to configure region or other part of the client. Use {@link ParamManager#getAppConfigProvider()} if you don't need this customization.
* @return a {@link AppConfigProvider}
*/
public static AppConfigProvider getAppConfigProvider(AppConfigDataClient client) {
return (AppConfigProvider) providers.computeIfAbsent(AppConfigProvider.class, (k) -> AppConfigProvider.builder()
.withClient(client)
.withCacheManager(cacheManager)
.withTransformationManager(transformationManager)
.build());
}

public static CacheManager getCacheManager() {
return cacheManager;
}
Expand Down
Loading