AWS Parameters and Secrets Lambda Extension つかってみた

## はじめに
2022/10/18 に AWS Parameters and Secrets Lambda Extension が利用できるようになりました。

https://aws.amazon.com/jp/about-aws/whats-new/2022/10/aws-parameters-secrets-lambda-extension/

AWS から提供される Lambda Extensions を使用して AWS Systems Manager Parameter Store からパラメーターを取得したり、AWS Secrets Manager からシークレットを取得したりできるようになります。

## 何が嬉しいのか
これまでは Lambda 関数の処理内で AWS SDK 等を使用してパラメーターやシークレットを取得していました。今回の拡張機能を使用することでこれらの値をキャッシュし、Lambda 関数のライフサイクル内で再利用できるようになります。これによりパラメーターやシークレットを取得するためのレイテンシーとコストが削減されます。

## 基本的な使い方
以下のドキュメントに記載のとおりではあるのですが、基本的な点について補足します。

https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html

https://docs.aws.amazon.com/secretsmanager/latest/userguide/retrieving-secrets_lambda.html

### Lambda 関数に拡張機能の Layer を設定する
Lambda Extensions は Lambda Layers を設定することで使用できるようになります。マネージドコンソールでは AWS レイヤーで Parameters and Secrets Lambda Extension を選択できるようになっていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/dbe5723e-a635-a0a3-d039-d6fb0946d4a7.png)

CLI 等から設定する場合は、公開されている Layer の ARN を指定します。リージョン毎の ARN のリストはドキュメントに記載されています。2022/10/19 現在で東京リージョンの ARN は `arn:aws:lambda:ap-northeast-1:133490724326:layer:AWS-Parameters-and-Secrets-Lambda-Extension:2` です。

### Lambda 関数内に HTTP GET のコードを書く
AWS Parameters and Secrets Lambda Extension を使用すると、AWS SDK での処理は不要になりますが、HTTP GET リクエストで値を取得するコードは必要です。サンプルコードは記事の後半を参照してください。

### Lambda 関数の実行ロールの変更
拡張機能は Lambda 関数自体の実行に使用される IAM ロールの認証情報を使用します。そのためパラメーターやシークレットを取得するための適切な IAM ポリシーが設定されている必要があります。例えば Parameter Store の場合、ssm:GetParameter および kms:Decrypt (SecureString 使用時) が必要です。

### (Optional) Lambda 関数に環境変数を設定する
パラメーターやシークレットのキャッシュに対する TTL、ログレベルなどを Lambda 関数の環境変数に設定することにより制御できます。

## サンプルコード
Amazon Linux 2 の AMI パブリックパラメーターを参照する例です。
注意点は以下です。

* パラメーター名に含まれる `/` はパーセントエンコーディングで変換しておく必要があります
* 拡張機能のローカル HTTP サーバーポートはデフォルト `2773` で起動します
    * 環境変数 PARAMETERS_SECRETS_EXTENSION_HTTP_PORT で変更することができます
* リクエストの検証のため、`X-Aws-Parameters-Secrets-Token` ヘッダーに環境変数 `AWS_SESSION_TOKEN` を設定する必要があります

```index.js
const https = require('http');

exports.handler = function(event, context, callback) {

    const options = {
        hostname: 'localhost',
        port: 2773,
        path: '/systemsmanager/parameters/get/?name=%2Faws%2Fservice%2Fami-amazon-linux-latest%2Famzn-ami-hvm-x86_64-gp2',
        headers: {
            'X-Aws-Parameters-Secrets-Token': process.env.AWS_SESSION_TOKEN
        },
        method: 'GET'
    };

    const req = https.request(options, res => {
        res.on('data', d => {
            console.log("Response from cache: "+d);
            return d;
        });
    });

    req.on('error', error => {
        console.error(error);
    });

    req.end();
};
```

実行結果のログは以下のようになります。パラメーター値が取得できてますね！

```
[AWS Parameters and Secrets Lambda Extension] 2022/10/19 06:51:08 PARAMETERS_SECRETS_EXTENSION_LOG_LEVEL is not present. Log level set to info.
[AWS Parameters and Secrets Lambda Extension] 2022/10/19 06:51:08 INFO Systems Manager Parameter Store and Secrets Manager Lambda Extension 1.0.94
[AWS Parameters and Secrets Lambda Extension] 2022/10/19 06:51:08 INFO Serving on port 2773
EXTENSION	Name: AWSParametersAndSecretsLambdaExtension	State: Ready	Events: [INVOKE,SHUTDOWN]
START RequestId: bb5bcc53-38cc-42d7-9dc5-xxxxxxxxxxxx Version: $LATEST
[AWS Parameters and Secrets Lambda Extension] 2022/10/19 06:51:08 INFO ready to serve traffic
2022-10-19T06:51:09.247Z	bb5bcc53-38cc-42d7-9dc5-xxxxxxxxxxxx	INFO	Response from cache: {"Parameter":{"ARN":"arn:aws:ssm:ap-northeast-1::parameter/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2","DataType":"text","LastModifiedDate":"2022-10-04T17:56:51.889Z","Name":"/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2","Selector":null,"SourceResult":null,"Type":"String","Value":"ami-0fb16641312307fa9","Version":49},"ResultMetadata":{}}
END RequestId: bb5bcc53-38cc-42d7-9dc5-xxxxxxxxxxxx
REPORT RequestId: bb5bcc53-38cc-42d7-9dc5-xxxxxxxxxxxx	Duration: 796.05 ms	Billed Duration: 797 ms	Memory Size: 128 MB	Max Memory Used: 76 MB	Init Duration: 324.74 ms

```
## 個人的つまずきポイント

### ローカル HTTP サーバーの起動ポート
デフォルトで `2773` で起動します。当初 AppConfig の拡張機能を参考にして、2772 を指定してリクエスト失敗していました。起動ポートは関数のログにも出力されます。

### HTTP ヘッダーの指定
リクエストの検証のため `X-Aws-Parameters-Secrets-Token` ヘッダーの設定が必須です。指定していないと `bad session token or header key` で `401 unauthorized` になります。

必須なのですが、2022/10/19 現在でドキュメントには記載されていませんでした。不備だと思うので今後修正されるかと思います。私は AWS 社員の方のツイートで設定が必要なことを知りました。

---

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">2. Make HTTP requests to localhost:2773/systemsmanager/parameters/get/?name=[parameter_name] <br> a. Note: add a header &#39;X-Aws-Parameters-Secrets-Token&#39; that is set to the AWS_SESSION_TOKEN environment variable<br><br>4/ <a href="https://t.co/FXc7f9FA9q">pic.twitter.com/FXc7f9FA9q</a></p>&mdash; Oren Nachman (@OrenNachman) <a href="https://twitter.com/OrenNachman/status/1582530677425131520?ref_src=twsrc%5Etfw">October 19, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

---
  
以上です。
参考になれば幸いです。