Lambda 関数で Secrets Manager シークレットを使用する - AWS Lambda

Lambda 関数で Secrets Manager シークレットを使用する

AWS Secrets Manager は、Lambda 関数に必要な認証情報、API キー、およびその他のシークレットを管理するのに役立ちます。Lambda 関数でシークレットを取得するには、次に示すとおり、主に 2 つのアプローチがあり、AWS SDK を使用してシークレットを直接取得するよりもパフォーマンスが向上し、コストを削減できます。

  • AWS Parameters and Secrets Lambda Extension - シークレットを取得するためのシンプルな HTTP インターフェイスを提供するランタイムに依存しないソリューション

  • Powertools for AWS Lambda パラメータユーティリティ - 組み込み変換で複数のプロバイダー (Secrets Manager、Parameter Store、AppConfig) をサポートするコード統合ソリューション

いずれのアプローチもシークレットのローカルキャッシュを保持します。このため、呼び出しのたびに関数が Secrets Manager を呼び出す必要がなくなります。関数でシークレットをリクエストすると、最初にそのキャッシュがチェックされます。シークレットが利用可能で、有効期限が切れていない場合は、すぐに返されます。それ以外の場合は、Secrets Manager から取得、キャッシュしたのちに、返されます。このキャッシュメカニズムにより、API コールが最小限に抑えられます。その結果、応答時間が短縮し、コストが減少します。

アプローチの選択

拡張機能と PowerTools のいずれかを選択するときは、次の要素を考慮してください。

以下の場合、AWS Parameters and Secrets Lambda Extension を使用する:
  • 任意の Lambda ランタイムで動作するランタイムに依存しないソリューションが必要な場合

  • 関数にコードの依存関係を追加したくない場合

  • Secrets Manager または Parameter Store からシークレットを取得するだけで済む場合

以下の場合、Powertools for AWS Lambda Parameters を使用する:
  • アプリケーションコードと統合された開発エクスペリエンスが必要な場合

  • 複数のプロバイダー (Secrets Manager、Parameter Store、AppConfig) のサポートが必要な場合

  • 組み込みのデータ変換 (JSON 解析、base64 デコード) が必要な場合

  • Python、TypeScript、Java、または .NET ランタイムを使用している場合

Lambda で Secrets Manager を使用する状況

Lambda で Secrets Manager を使用する一般的なシナリオは次のとおりです。

  • 関数が Amazon RDS または他のデータベースに接続するために使用するデータベース認証情報を保存する

  • 関数が呼び出す外部サービスの API キーを管理する

  • 暗号化キーやその他の機密設定データを保存する

  • 関数コードの更新を必要とせずに認証情報を自動的にローテーションする

AWS Parameters and Secrets Lambda Extension の使用

AWS Parameters and Secrets Lambda Extension は、すべての Lambda ランタイムと互換性のあるシンプルな HTTP インターフェイスを使用します。デフォルトでは、シークレットを 300 秒 (5 分) キャッシュし、最大 1,000 個のシークレットを保持できます。これらの設定は環境変数でカスタマイズできます。

Lambda 関数で Secrets Manager を使用する

このセクションでは、Secrets Manager シークレットが既に存在していることを前提としています。シークレットを作成するには、「AWS Secrets Manager シークレットの作成」を参照してください。

好みのランタイムを選択し、Secrets Manager からシークレットを取得する関数の作成手順に従います。サンプル関数は、Secrets Manager からシークレットを取得するもので、アプリケーション内のデータベース認証情報、API キー、またはその他の機密設定データにアクセスするために使用できます。

Python
Python 関数を作成するには
  1. 新しいプロジェクトディレクトリを作成し、そこに移動します。例:

    mkdir my_function cd my_function
  2. lambda_function.py という名前のファイルを作成し、次のコードを記述します。secret_name には、シークレットの名前または Amazon リソースネーム (ARN) を使用します。

    import json import os import requests def lambda_handler(event, context): try: # Replace with the name or ARN of your secret secret_name = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME" secrets_extension_endpoint = f"http://localhost:2773/secretsmanager/get?secretId={secret_name}" headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get('AWS_SESSION_TOKEN')} response = requests.get(secrets_extension_endpoint, headers=headers) print(f"Response status code: {response.status_code}") secret = json.loads(response.text)["SecretString"] print(f"Retrieved secret: {secret}") return { 'statusCode': response.status_code, 'body': json.dumps({ 'message': 'Successfully retrieved secret', 'secretRetrieved': True }) } except Exception as e: print(f"Error: {str(e)}") return { 'statusCode': 500, 'body': json.dumps({ 'message': 'Error retrieving secret', 'error': str(e) }) }
  3. requirements.txt という名前のファイルを作成し、次の内容を記述します。

    requests
  4. 依存関係をインストールします。

    pip install -r requirements.txt -t .
  5. すべてのファイルを含む .zip ファイルを作成します。

    zip -r function.zip .
Node.js
Node.js 関数を作成するには
  1. 新しいプロジェクトディレクトリを作成し、そこに移動します。例:

    mkdir my_function cd my_function
  2. index.mjs という名前のファイルを作成し、次のコードを記述します。secret_name には、シークレットの名前または Amazon リソースネーム (ARN) を使用します。

    import http from 'http'; export const handler = async (event) => { try { // Replace with the name or ARN of your secret const secretName = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME"; const options = { hostname: 'localhost', port: 2773, path: `/secretsmanager/get?secretId=${secretName}`, headers: { 'X-Aws-Parameters-Secrets-Token': process.env.AWS_SESSION_TOKEN } }; const response = await new Promise((resolve, reject) => { http.get(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { resolve({ statusCode: res.statusCode, body: data }); }); }).on('error', reject); }); const secret = JSON.parse(response.body).SecretString; console.log('Retrieved secret:', secret); return { statusCode: response.statusCode, body: JSON.stringify({ message: 'Successfully retrieved secret', secretRetrieved: true }) }; } catch (error) { console.error('Error:', error); return { statusCode: 500, body: JSON.stringify({ message: 'Error retrieving secret', error: error.message }) }; } };
  3. index.mjs ファイルを含む .zip ファイルを作成します。

    zip -r function.zip index.mjs
Java
Java 関数を作成するには
  1. Maven プロジェクトを作成します。

    mvn archetype:generate \ -DgroupId=example \ -DartifactId=lambda-secrets-demo \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
  2. プロジェクトディレクトリに移動します。

    cd lambda-secrets-demo
  3. pom.xml を開き、内容を次に置き換えます。

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>example</groupId> <artifactId>lambda-secrets-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <finalName>function</finalName> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
  4. /lambda-secrets-demo/src/main/java/example/App.java の名前を、Lambda のデフォルトの Java ハンドラ名 (example.Hello::handleRequest) と一致するように Hello.java に変更します。

    mv src/main/java/example/App.java src/main/java/example/Hello.java
  5. Hello.java ファイルを開き、内容を次に置き換えます。secretName には、シークレットの名前または Amazon リソースネーム (ARN) を使用します。

    package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class Hello implements RequestHandler<Object, String> { private final HttpClient client = HttpClient.newHttpClient(); @Override public String handleRequest(Object input, Context context) { try { // Replace with the name or ARN of your secret String secretName = "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME"; String endpoint = "http://localhost:2773/secretsmanager/get?secretId=" + secretName; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(endpoint)) .header("X-Aws-Parameters-Secrets-Token", System.getenv("AWS_SESSION_TOKEN")) .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); String secret = response.body(); secret = secret.substring(secret.indexOf("SecretString") + 15); secret = secret.substring(0, secret.indexOf("\"")); System.out.println("Retrieved secret: " + secret); return String.format( "{\"statusCode\": %d, \"body\": \"%s\"}", response.statusCode(), "Successfully retrieved secret" ); } catch (Exception e) { e.printStackTrace(); return String.format( "{\"body\": \"Error retrieving secret: %s\"}", e.getMessage() ); } } }
  6. テストディレクトリを削除します。Maven はこれをデフォルトで作成しますが、この例では必要ありません。

    rm -rf src/test
  7. プロジェクトをビルドします。

    mvn package
  8. JAR ファイル (target/function.jar) を後で使用するためにダウンロードします。

  1. Lambda コンソールの [関数] ページを開きます。

  2. [関数の作成] を選択してください。

  3. [一から作成] を選択します。

  4. [関数名] に「secret-retrieval-demo」と入力します。

  5. 好みの [ランタイム] を選択します。

  6. [関数の作成] を選択してください。

デプロイパッケージをアップロードするには
  1. 関数の [コード] タブで、[アップロード元] を選択し、[.zip ファイル] (Python および Node.js の場合) または [.jar ファイル] (Java の場合) を選択します。

  2. 前に作成したデプロイパッケージをアップロードします。

  3. [保存] を選択します。

AWS パラメータとシークレット Lambda 拡張機能をレイヤーとして追加するには
  1. 関数の [コード] タブで、[レイヤー] まで下にスクロールします。

  2. [レイヤーを追加] を選択します。

  3. [AWS レイヤー] を選択します。

  4. [AWS-Parameters-and-Secrets-Lambda-Extension] を選択します。

  5. 最新バージョンを選択します。

  6. [Add] (追加) を選択します。

実行ロールに Secrets Manager アクセス許可を追加するには
  1. [設定] タブを開き、次に [アクセス権限] をクリックします。

  2. [実行ロール] で、実行ロールのリンクを選択します。このリンクを選択すると、IAM コンソールでロールが開きます。

    実行ロールのリンク
  3. [アクセス権限を追加][インラインポリシーを作成] の順に選択します。

    IAM コンソールでポリシーのアタッチ
  4. [JSON] タブを選択して次のポリシーを追加します。Resource には、シークレットの ARN を入力します。

    JSON
    { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:SECRET_NAME" } ] }
  5. [次へ] を選択します。

  6. ポリシーの名前を入力します。

  7. [Create policy] (ポリシーの作成) を選択します。

関数をテストするには
  1. Lambda コンソールに戻ります。

  2. [テスト] タブを選択します。

  3. [テスト] を選択します。以下のようなレスポンスが表示されます。

    成功した場合のテスト結果

環境変数

AWS パラメータとシークレット Lambda 拡張機能では、次のデフォルト設定が使用されます。これらの設定は、対応する環境変数を作成して上書きできます。関数の現在の設定を表示するには、PARAMETERS_SECRETS_EXTENSION_LOG_LEVELDEBUG に設定します。この拡張機能によって、各関数の呼び出し開始時にその設定情報が CloudWatch Logs に記録されます。

設定 デフォルト値 有効値 環境変数 詳細
HTTP ポート 2773 1 - 65535 PARAMETERS_SECRETS_EXTENSION_HTTP_PORT ローカル HTTP サーバーのポート。
キャッシュ有効 TRUE、FALSE PARAMETERS_SECRETS_EXTENSION_CACHE_ENABLED キャッシュを有効または無効にします。
キャッシュサイズ 1,000 0 ~ 1000 PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE キャッシュを無効にするには 0 に設定します。
Secrets Manager の TTL 300 秒 0 ~ 300 秒 SECRETS_MANAGER_TTL キャッシュされたシークレットの有効期限。キャッシュを無効にするには 0 に設定します。PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE の値が 0 の場合、この変数は無視されます。
Parameter Store の TTL 300 秒 0 ~ 300 秒 SSM_PARAMETER_STORE_TTL キャッシュされたパラメータの有効期限。キャッシュを無効にするには 0 に設定します。PARAMETERS_SECRETS_EXTENSION_CACHE_SIZE の値が 0 の場合、この変数は無視されます。
ログレベル 情報 デバッグ | 情報 | 警告 | エラー | なし PARAMETERS_SECRETS_EXTENSION_LOG_LEVEL 拡張機能のログに報告された詳細レベル
最大接続数 3 1 以上 PARAMETERS_SECRETS_EXTENSION_MAX_CONNECTIONS Parameter Store または Secrets Manager へのリクエストに使用される HTTP クライアントの最大接続数
Secrets Manager のタイムアウト 0 (タイムアウトなし) すべての整数 SECRETS_MANAGER_TIMEOUT_MILLIS Secrets Manager へのリクエストのタイムアウト (ミリ秒単位)
Parameter Store のタイムアウト 0 (タイムアウトなし) すべての整数 SSM_PARAMETER_STORE_TIMEOUT_MILLIS Parameter Store へのリクエストのタイムアウト (ミリ秒単位)

シークレットローテーションの使用

シークレットを頻繁にローテーションする場合、デフォルトの 300 秒のキャッシュ期間が原因で関数が古いシークレットを使用する可能性があります。関数が最新のシークレット値を使用するようにするには、次の 2 つのオプションを使用できます。

  • SECRETS_MANAGER_TTL 環境変数をより低い値 (秒単位) に設定してキャッシュの TTL を短縮する。例えば、60 に設定すると、関数は 1 分以上経過したシークレットを使用しなくなります。

  • シークレットリクエストで AWSCURRENT または AWSPREVIOUS ステージングラベルを使用して、必要な特定のバージョンを確実に取得できるようにする。

    secretsmanager/get?secretId=YOUR_SECRET_NAME&versionStage=AWSCURRENT

パフォーマンスと鮮度のニーズのバランスを維持するのに最適なアプローチを選択してください。TTL を短くすると、Secrets Manager の呼び出し頻度が高くなりますが、最新のシークレット値が使用されるようになります。

Powertools for AWS Lambda のパラメータユーティリティの使用

Powertools for AWS Lambda のパラメータユーティリティは、Secrets Manager、パラメータストア、AppConfig などの複数のプロバイダーからシークレットを取得するための統合インターフェイスを提供します。キャッシュ、変換を処理し、拡張アプローチに比べ、より統合された開発エクスペリエンスを提供します。

パラメータユーティリティの利点

  • 複数のプロバイダー - 同じインターフェイスを使用して Secrets Manager、Parameter Store、AppConfig からパラメータを取得する

  • 組み込み変換 - 自動 JSON 解析、base64 デコード、およびその他のデータ変換

  • 統合キャッシュ - API コールを減らすための TTL サポートによる設定可能なキャッシュ

  • 型の安全性 - TypeScript およびその他のサポートされているランタイムで型付けを強力にサポート

  • エラー処理 - 組み込みの再試行ロジックとエラー処理

コードの例

次の例は、さまざまなランタイムで Parameters ユーティリティを使用してシークレットを取得する方法を示しています。

Python (パイソン)
注記

詳細な例とセットアップ手順については、「Parameters ユーティリティのドキュメント」を参照してください。

Powertools for AWS Lambda Parameters ユーティリティを使用して Secrets Manager からシークレットを取得します。

from aws_lambda_powertools import Logger from aws_lambda_powertools.utilities import parameters logger = Logger() def lambda_handler(event, context): try: # Get secret with caching (default TTL: 5 seconds) secret_value = parameters.get_secret("my-secret-name") # Get secret with custom TTL secret_with_ttl = parameters.get_secret("my-secret-name", max_age=300) # Get secret and transform JSON secret_json = parameters.get_secret("my-json-secret", transform="json") logger.info("Successfully retrieved secrets") return { 'statusCode': 200, 'body': 'Successfully retrieved secrets' } except Exception as e: logger.error(f"Error retrieving secret: {str(e)}") return { 'statusCode': 500, 'body': f'Error: {str(e)}' }
TypeScript
注記

詳細な例とセットアップ手順については、「Parameters ユーティリティのドキュメント」を参照してください。

Powertools for AWS Lambda Parameters ユーティリティを使用して Secrets Manager からシークレットを取得します。

import { Logger } from '@aws-lambda-powertools/logger'; import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; import type { Context } from 'aws-lambda'; const logger = new Logger(); export const handler = async (event: any, context: Context) => { try { // Get secret with caching (default TTL: 5 seconds) const secretValue = await getSecret('my-secret-name'); // Get secret with custom TTL const secretWithTtl = await getSecret('my-secret-name', { maxAge: 300 }); // Get secret and transform JSON const secretJson = await getSecret('my-json-secret', { transform: 'json' }); logger.info('Successfully retrieved secrets'); return { statusCode: 200, body: 'Successfully retrieved secrets' }; } catch (error) { logger.error('Error retrieving secret', { error }); return { statusCode: 500, body: `Error: ${error}` }; } };
Java
注記

詳細な例とセットアップ手順については、「Parameters ユーティリティのドキュメント」を参照してください。

Powertools for AWS Lambda Parameters ユーティリティを使用して Secrets Manager からシークレットを取得します。

import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.parameters.SecretsProvider; import software.amazon.lambda.powertools.parameters.ParamManager; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; public class SecretHandler implements RequestHandler<Object, String> { private final SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); @Logging @Override public String handleRequest(Object input, Context context) { try { // Get secret with caching (default TTL: 5 seconds) String secretValue = secretsProvider.get("my-secret-name"); // Get secret with custom TTL (300 seconds) String secretWithTtl = secretsProvider.withMaxAge(300).get("my-secret-name"); // Get secret and transform JSON MySecret secretJson = secretsProvider.get("my-json-secret", MySecret.class); return "Successfully retrieved secrets"; } catch (Exception e) { return "Error retrieving secret: " + e.getMessage(); } } public static class MySecret { // Define your secret structure here } }
.NET
注記

詳細な例とセットアップ手順については、「Parameters ユーティリティのドキュメント」を参照してください。

Powertools for AWS Lambda Parameters ユーティリティを使用して Secrets Manager からシークレットを取得します。

using AWS.Lambda.Powertools.Logging; using AWS.Lambda.Powertools.Parameters; using Amazon.Lambda.Core; [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] public class Function { private readonly ISecretsProvider _secretsProvider; public Function() { _secretsProvider = ParametersManager.SecretsProvider; } [Logging] public async Task<string> FunctionHandler(object input, ILambdaContext context) { try { // Get secret with caching (default TTL: 5 seconds) var secretValue = await _secretsProvider.GetAsync("my-secret-name"); // Get secret with custom TTL var secretWithTtl = await _secretsProvider.WithMaxAge(TimeSpan.FromMinutes(5)) .GetAsync("my-secret-name"); // Get secret and transform JSON var secretJson = await _secretsProvider.GetAsync<MySecret>("my-json-secret"); return "Successfully retrieved secrets"; } catch (Exception e) { return $"Error retrieving secret: {e.Message}"; } } public class MySecret { // Define your secret structure here } }

セットアップとアクセス許可

Parameters ユーティリティを使用するには、以下を行う必要があります。

  1. ランタイム用の Powertools for AWS Lambda をインストールする。詳細については、「Powertools for AWS Lambda」を参照してください。

  2. 関数の実行ロールに必要な IAM アクセス許可を追加する。詳細については、「AWS Lambda アクセス許可の管理」を参照してください。

  3. 環境変数を使用してオプションの設定を構成する。

必要な IAM アクセス許可は、拡張アプローチの場合と同じです。このユーティリティは、設定に基づいて Secrets Manager へのキャッシュと API コールを自動的に処理します。