Lambda Powertools Parametersユーティリティの紹介：AWSサービスからのパラメータの取得を簡素化する

今日、Lambda Powertoolsはバージョン1.7.0をリリースしました。これは**ベータ版の開発者プレビュー**ですが、GA版に非常に近いものです。このバージョンには、私が長い間待っていた機能が含まれており、早速使用して紹介したいと思います。この機能は「Parameters utility」と呼ばれ、AWSのさまざまなサービスから1つまたは複数のパラメータ値を取得し、キャッシュ、変換、エラー処理を処理するために必要なカスタムコードの量を減らすことができます。

Parameters utilityは、[AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html), [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html), [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html), [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html)、または独自のパラメータストアから1つまたは複数のパラメータ値を取得するための高レベルな機能を提供します。

#### 主な機能

-   下位プロバイダから1つまたは複数のパラメータを取得します。
-   指定された時間（デフォルトは5秒）にわたってパラメータ値をキャッシュします。
-   JSONまたはbase64エンコードされた文字列からパラメータ値を変換します。
-   独自のパラメータストアプロバイダを持ち込む


#### 始めるには

ベータ版のエイリアス`@aws-lambda-powertools/parameters@1.7.0-beta`を使用して、npmからユーティリティをインストールします。

さらに、使用するパラメータストアのAWS SDKクライアントを追加する必要があります。Parameters utilityは[AWS SDK v3 for JavaScript](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/#aws-sdk-for-javascript-v3)のみをサポートしており、必要なSDKパッケージのみをインストールしてバンドルサイズを小さくすることができます。

-   SSM Parameters Store -> `@aws-sdk/client-ssm`
-   Secrets Manager -> `@aws-sdk/client-secrets-manager`
-   AppConfig -> `@aws-sdk/client-appconfigdata`
-   DynamoDB -> `@aws-sdk/client-dynamodb @aws-sdk/util-dynamodb`

次に、Lambda関数の実行ロールの[AWS Identity and Access Management（IAM）](https://aws.amazon.com/iam/)のアクセス許可を割り当てて、パラメータストアからパラメータを取得することを許可します。必要なアクションとストアごとの必要なアクセス許可については、ユーティリティのドキュメントの[IAM Permissions section](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/utilities/parameters/#iam-permissions)を参照してください。

:::note  
以下のセクション内容ではSecrets Managerをストアとして使用して、Parameters utilityの異なる機能を紹介しています。同じ機能は他のすべてのプロバイダーでも使用できます。他のストアに興味がある場合は、[Parameters docs](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/utilities/parameters)を参照してください。
:::

#### 1つのシークレットを取得する

個々のパラメータを取得するには、Parameters utilityは`getSecret`関数を提供します。
```typescript
import { getSecret } from '@aws-lambda-powertools/parameters/secrets';  
  
export const handler = async (): Promise<void> => {  
  // Retrieve a single secret  
  const secret = await getSecret('my-secret');  
  console.log(secret);  
}
```

#### キャッシュTTLの調整

デフォルトでは、**取得した値は5秒間インメモリでキャッシュされます**。このキャッシュされた値は、Lambda関数の後続の呼び出しで使用され、期限が切れるまでキャッシュされます。値を長く保持したい場合は、Parameters utilityを介してtime-to-live（TTL）を調整することができます。
```typescript
import { getSecret } from '@aws-lambda-powertools/parameters/secrets';  
  
export const handler = async (): Promise<void> => {  
  // Retrieve a single secret with a 60 seconds cache TTL
  const secret = await getSecret('my-secret', { maxAge: 60 });  
  console.log(secret);  
}
```

代わりに、常に最新の値を取得し、キャッシュをスキップする場合は、`forceFetch`オプションを使用できます。
```typescript
import { getSecret } from '@aws-lambda-powertools/parameters/secrets';  
  
export const handler = async (): Promise<void> => {  
  // Retrieve a single secret skipping cache
  const secret = await getSecret('my-secret', { forceFetch: true });  
  console.log(secret);  
}
```

#### JSONまたはbase64形式で保存されたシークレットのデコード

いくつかのシークレットがbase64またはJSONで保存されている場合、Parameters utilityの`transform`引数を使用して逆シリアル化できます。
```typescript
import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets';  
  
const secretsProvider = new SecretsProvider();  
  
export const handler = async (): Promise<void> => {  
  // Retrieve a secret and deserialize it as JSON  
  const json = await secretsProvider.get('my-secret-json', { transform: 'json' });  
  console.log(json);  
  
  // Transform a base64 encoded string  
  const binary = await secretsProvider.getMultiple('my-secret-binary', { transform: 'binary' });  
  console.log(binary);  
}
```

#### AWS SDKのカスタマイズ

場合によっては、特定のパラメータを設定したい場合やトレースを適用する場合など、独自のAWS SDKを使用して値を取得する必要がある場合があります。これは、提供者クラスを直接使用することで実現できます。
```typescript
import { Tracer } from "@aws-lambda-powertools/tracer";  
import { SecretsProvider } from "@aws-lambda-powertools/parameters/secrets";  
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";  
  
// initialize the tracer  
const tracer = new Tracer();  
// construct your client and instrument it to emit tracing data  
const secretsManagerClient = tracer.captureAWSv3Client(new SecretsManagerClient({}));  
// pass the client to the SecretProvider  
const secretProvider = new SecretsProvider({ awsSdkV3Client: secretsManagerClient });  
  
export const handler = async (): Promise<void> => {  
  // this operation will now appear in your X-Ray traces and service map  
  const secret = await secretProvider.get('my-secret');  
  console.log(secret);  
}
```
各プロバイダーに利用可能なすべての機能について詳しく知りたい場合は、[Parameters docs](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/utilities/parameters)を確認してください。

### その他のプロバイダー

上記の例ではシークレットがすべてでしたが、AppConfig、SSM、またはDynamoDBに保存された設定にアクセスする場合は、同じアプローチを使用できます。
```typescript
import { AppConfigProvider } from "@aws-lambda-powertools/parameters/appconfig";  
import { SSMProvider } from "@aws-lambda-powertools/parameters/ssm";  
import { DynamoDBProvider } from "@aws-lambda-powertools/parameters/dynamodb";
```

#### SSMプロバイダー

SSMを使用して、一度に複数のパラメータを取得し、特定の変換やTTLを適用することができます。
```typescript
import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm';  
import type { SSMGetParametersByNameOptionsInterface } from "@aws-lambda-powertools/parameters/types";  
  
const props: Record<string, SSMGetParametersByNameOptionsInterface> = {  
  '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' },  
  '/no_cache_param': { maxAge: 0 },  
  '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values 
  '/this/param/does/not/exist': {}, // <- Example of non-existent parameter 
};  
  
  
export const handler = async (): Promise<void> => {  
  // This returns an object with the parameter name as key  
  const {  
    _errors: errors,  
    ...parameters  
  } = await getParametersByName(props, { maxAge: 60 });  
  
  // Handle gracefully, since `/this/param/does/not/exist` will only be available in `_errors`  
  if (errors && errors.length) {  
    console.error(`Unable to retrieve parameters: ${errors.join(',')}`);  
  }  
  
  for (const [key, value] of Object.entries(parameters)) {  
    console.log(`${key}: ${value}`);  
  }  
};
```