Skip to content

Commit 2c66d33

Browse files
authored
docs(parameters): release tasks + add docs on bring your own provider (#1565)
* chore: remove beta notice from README + headings * chore: remove utility from beta pool * chore: remove beta notice from other readmes * docs: removed beta notice from docs * docs: add custom provider example
1 parent 3a982fc commit 2c66d33

File tree

13 files changed

+292
-39
lines changed

13 files changed

+292
-39
lines changed

.github/scripts/release_patch_package_json.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ if (process.argv.length < 3) {
1818
const basePath = resolve(process.argv[2]);
1919
const packageJsonPath = join(basePath, 'package.json');
2020
const alphaPackages = ['@aws-lambda-powertools/idempotency'];
21-
const betaPackages = ['@aws-lambda-powertools/parameters'];
21+
const betaPackages = [];
2222

2323
(() => {
2424
try {

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ You can use the library in both TypeScript and JavaScript code bases.
3434
* **[Tracer](https://docs.powertools.aws.dev/lambda-typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions
3535
* **[Logger](https://docs.powertools.aws.dev/lambda-typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context
3636
* **[Metrics](https://docs.powertools.aws.dev/lambda-typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF)
37-
* **[Parameters (beta)](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM Parameter Store, AWS Secrets Manager, AWS AppConfig, and Amazon DynamoDB
37+
* **[Parameters](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM Parameter Store, AWS Secrets Manager, AWS AppConfig, and Amazon DynamoDB
3838
* **[Idempotency (beta)](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/idempotency/)** - Class method decorator, Middy middleware, and function wrapper to make your Lambda functions idempotent and prevent duplicate execution based on payload content
3939

4040
## Getting started

docs/snippets/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"aws-sdk": "^2.1405.0",
3636
"aws-sdk-client-mock": "^2.2.0",
3737
"aws-sdk-client-mock-jest": "^2.2.0",
38-
"axios": "^1.2.4"
38+
"axios": "^1.2.4",
39+
"hashi-vault-js": "^0.4.13"
3940
}
4041
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Logger } from '@aws-lambda-powertools/logger';
2+
import { BaseProvider } from '@aws-lambda-powertools/parameters/base';
3+
import Vault from 'hashi-vault-js';
4+
import { isErrorResponse } from './customProviderVaultTypes';
5+
import type {
6+
HashiCorpVaultProviderOptions,
7+
HashiCorpVaultGetOptions,
8+
} from './customProviderVaultTypes';
9+
10+
class HashiCorpVaultProvider extends BaseProvider {
11+
public client: Vault;
12+
readonly #token: string;
13+
readonly #logger: Logger;
14+
15+
/**
16+
* It initializes the HashiCorpVaultProvider class.
17+
*
18+
* @param {HashiCorpVaultProviderOptions} config - The configuration object.
19+
*/
20+
public constructor(config: HashiCorpVaultProviderOptions) {
21+
super();
22+
23+
const { url, token, clientConfig, vaultClient } = config;
24+
if (vaultClient) {
25+
if (vaultClient instanceof Vault) {
26+
this.client = vaultClient;
27+
} else {
28+
throw Error('Not a valid Vault client provided');
29+
}
30+
} else {
31+
const config: Vault.VaultConfig = {
32+
baseUrl: url,
33+
...(clientConfig ?? {
34+
timeout: 10000,
35+
rootPath: '',
36+
}),
37+
};
38+
this.client = new Vault(config);
39+
}
40+
this.#token = token;
41+
this.#logger = new Logger({
42+
serviceName: 'HashiCorpVaultProvider',
43+
});
44+
}
45+
46+
/**
47+
* Retrieve a secret from HashiCorp Vault.
48+
*
49+
* You can customize the retrieval of the secret by passing options to the function:
50+
* * `maxAge` - The maximum age of the value in cache before fetching a new one (in seconds) (default: 5)
51+
* * `forceFetch` - Whether to always fetch a new value from the store regardless if already available in cache
52+
* * `sdkOptions` - Extra options to pass to the HashiCorp Vault SDK, e.g. `mount` or `version`
53+
*
54+
* @param {string} name - The name of the secret
55+
* @param {HashiCorpVaultGetOptions} options - Options to customize the retrieval of the secret
56+
*/
57+
public async get(
58+
name: string,
59+
options?: HashiCorpVaultGetOptions
60+
): Promise<Record<string, unknown> | undefined> {
61+
return super.get(name, options) as Promise<
62+
Record<string, unknown> | undefined
63+
>;
64+
}
65+
66+
/**
67+
* Retrieving multiple parameter values is not supported with HashiCorp Vault.
68+
*/
69+
public async getMultiple(path: string, _options?: unknown): Promise<void> {
70+
await super.getMultiple(path);
71+
}
72+
73+
/**
74+
* Retrieve a secret from HashiCorp Vault.
75+
*
76+
* @param {string} name - The name of the secret
77+
* @param {HashiCorpVaultGetOptions} options - Options to customize the retrieval of the secret
78+
*/
79+
protected async _get(
80+
name: string,
81+
options?: HashiCorpVaultGetOptions
82+
): Promise<Record<string, unknown>> {
83+
const mount = options?.sdkOptions?.mount ?? 'secret';
84+
const version = options?.sdkOptions?.version;
85+
86+
const response = await this.client.readKVSecret(
87+
this.#token,
88+
name,
89+
version,
90+
mount
91+
);
92+
93+
if (isErrorResponse(response)) {
94+
this.#logger.error('An error occurred', {
95+
error: response.vaultHelpMessage,
96+
});
97+
throw response;
98+
} else {
99+
return response.data;
100+
}
101+
}
102+
103+
/**
104+
* Retrieving multiple parameter values from HashiCorp Vault is not supported.
105+
*
106+
* @throws Not Implemented Error.
107+
*/
108+
protected async _getMultiple(
109+
_path: string,
110+
_options?: unknown
111+
): Promise<void> {
112+
throw new Error('Method not implemented.');
113+
}
114+
}
115+
116+
export { HashiCorpVaultProvider };
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { GetOptionsInterface } from '@aws-lambda-powertools/parameters/base/types';
2+
import type Vault from 'hashi-vault-js';
3+
4+
/**
5+
* Base interface for HashiCorpVaultProviderOptions.
6+
* @interface
7+
*/
8+
interface HashiCorpVaultProviderOptionsBase {
9+
/**
10+
* Indicate the server name/IP, port and API version for the Vault instance, all paths are relative to this one.
11+
* @example 'https://vault.example.com:8200/v1'
12+
*/
13+
url: string;
14+
/**
15+
* The Vault token to use for authentication.
16+
*/
17+
token: string;
18+
}
19+
20+
/**
21+
* Interface for HashiCorpVaultProviderOptions with clientConfig property.
22+
* @interface
23+
*/
24+
interface HashiCorpVaultProviderOptionsWithClientConfig
25+
extends HashiCorpVaultProviderOptionsBase {
26+
/**
27+
* Optional configuration to pass during client initialization to customize the `hashi-vault-js` client.
28+
*/
29+
clientConfig?: Omit<Vault.VaultConfig, 'baseUrl'>;
30+
/**
31+
* This property should never be passed.
32+
*/
33+
vaultClient?: never;
34+
}
35+
36+
/**
37+
* Interface for HashiCorpVaultProviderOptions with vaultClient property.
38+
*
39+
* @interface
40+
*/
41+
interface HashiCorpVaultProviderOptionsWithClientInstance
42+
extends HashiCorpVaultProviderOptionsBase {
43+
/**
44+
* Optional `hashi-vault-js` client to pass during HashiCorpVaultProvider class instantiation. If not provided, a new client will be created.
45+
*/
46+
vaultClient?: Vault;
47+
/**
48+
* This property should never be passed.
49+
*/
50+
clientConfig: never;
51+
}
52+
53+
/**
54+
* Options for the HashiCorpVaultProvider class constructor.
55+
*
56+
* @param {string} url - Indicate the server name/IP, port and API version for the Vault instance, all paths are relative to this one.
57+
* @param {string} token - The Vault token to use for authentication.
58+
* @param {Vault.VaultConfig} [clientConfig] - Optional configuration to pass during client initialization, e.g. timeout. Mutually exclusive with vaultClient.
59+
* @param {Vault} [vaultClient] - Optional `hashi-vault-js` client to pass during HashiCorpVaultProvider class instantiation. Mutually exclusive with clientConfig.
60+
*/
61+
type HashiCorpVaultProviderOptions =
62+
| HashiCorpVaultProviderOptionsWithClientConfig
63+
| HashiCorpVaultProviderOptionsWithClientInstance;
64+
65+
type HashiCorpVaultReadKVSecretOptions = {
66+
/**
67+
* The mount point of the secret engine to use. Defaults to `secret`.
68+
* @example 'kv'
69+
*/
70+
mount?: string;
71+
/**
72+
* The version of the secret to retrieve. Defaults to `undefined`.
73+
* @example 1
74+
*/
75+
version?: number;
76+
};
77+
78+
interface HashiCorpVaultGetOptions extends GetOptionsInterface {
79+
/**
80+
* The Parameters utility does not support transforming `Record<string, unknown>` values as returned by the HashiCorp Vault SDK.
81+
*/
82+
transform?: never;
83+
sdkOptions?: HashiCorpVaultReadKVSecretOptions;
84+
}
85+
86+
/**
87+
* Typeguard that discriminates the type of the response and excludes the ErrorResponse type.
88+
*
89+
* @param object The response object to discriminate
90+
*/
91+
export const isErrorResponse = (
92+
object: Vault.ReadKVSecretResponse
93+
): object is Vault.ErrorResponse => {
94+
return 'isVaultError' in object;
95+
};
96+
97+
export type { HashiCorpVaultProviderOptions, HashiCorpVaultGetOptions };
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { HashiCorpVaultProvider } from './customProviderVault';
2+
3+
const secretsProvider = new HashiCorpVaultProvider({
4+
url: 'https://vault.example.com:8200/v1',
5+
token: 'my-token',
6+
});
7+
8+
export const handler = async (): Promise<void> => {
9+
// Retrieve a secret from HashiCorp Vault
10+
const secret = await secretsProvider.get('my-secret');
11+
console.log(secret);
12+
};

docs/utilities/parameters.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ title: Parameters
33
description: Utility
44
---
55

6-
???+ warning
7-
**This utility is currently released as beta developer preview** and is intended strictly for feedback and testing purposes **and not for production workloads**.. The version and all future versions tagged with the `-beta` suffix should be treated as not stable. Up until before the [General Availability release](https://github.com/aws-powertools/powertools-lambda-typescript/milestone/10) we might introduce significant breaking changes and improvements in response to customers feedback.
8-
96
The Parameters utility provides high-level functions to retrieve one or multiple parameter values from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html){target="_blank"}, [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html){target="_blank"}, [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html){target="_blank"}, [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html){target="_blank"}, or your own parameter store.
107

118
## Key features
@@ -275,6 +272,29 @@ DynamoDB provider can be customized at initialization to match your table struct
275272
--8<-- "docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts"
276273
```
277274

275+
### Create your own provider
276+
277+
You can create your own custom parameter store provider by extending the `BaseProvider` class, and implementing the `get()` and `getMultiple()` methods, as well as its respective `_get()` and `_getMultiple()` private methods to retrieve a single, or multiple parameters from your custom store.
278+
279+
All caching logic is handled by the `BaseProvider`, and provided that the return types of your store are compatible with the ones used in the `BaseProvider`, all transformations will also work as expected.
280+
281+
Here's an example of implementing a custom parameter store using an external service like HashiCorp Vault, a widely popular key-value secret storage.
282+
283+
=== "Provider implementation"
284+
```typescript
285+
--8<-- "docs/snippets/parameters/customProviderVault.ts"
286+
```
287+
288+
=== "Provider types"
289+
```typescript
290+
--8<-- "docs/snippets/parameters/customProviderVaultTypes.ts"
291+
```
292+
293+
=== "Provider usage"
294+
```typescript
295+
--8<-- "docs/snippets/parameters/customProviderVaultUsage.ts"
296+
```
297+
278298
### Deserializing values with transform parameter
279299

280300
For parameters stored in JSON or Base64 format, you can use the `transform` argument for deserialization.

package-lock.json

Lines changed: 17 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/commons/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ You can use the library in both TypeScript and JavaScript code bases.
3131
* **[Tracer](https://docs.powertools.aws.dev/lambda-typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions
3232
* **[Logger](https://docs.powertools.aws.dev/lambda-typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context
3333
* **[Metrics](https://docs.powertools.aws.dev/lambda-typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF)
34-
* **[Parameters (beta)](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB
34+
* **[Parameters](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB
3535

3636
## Getting started
3737

packages/logger/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ You can use the library in both TypeScript and JavaScript code bases.
3030
* **[Tracer](https://docs.powertools.aws.dev/lambda-typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions
3131
* **[Logger](https://docs.powertools.aws.dev/lambda-typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context
3232
* **[Metrics](https://docs.powertools.aws.dev/lambda-typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF)
33-
* **[Parameters (beta)](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB
33+
* **[Parameters](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB
3434

3535
## Getting started
3636

packages/metrics/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ You can use the library in both TypeScript and JavaScript code bases.
3030
* **[Tracer](https://docs.powertools.aws.dev/lambda-typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions
3131
* **[Logger](https://docs.powertools.aws.dev/lambda-typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context
3232
* **[Metrics](https://docs.powertools.aws.dev/lambda-typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF)
33-
* **[Parameters (beta)](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB
33+
* **[Parameters](https://docs.powertools.aws.dev/lambda-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB
3434

3535
## Getting started
3636

0 commit comments

Comments
 (0)