【AWS Lambda】Secrets Manager 経由で DB に接続してみた
はじめに
こんにちは、フニです。
本記事では、VPC に配置した Lambda から DB (RDS/Aurora) への接続を、Secrets Manager 経由で実施する方法についてご紹介します。
Lambda から DB への接続イメージ
Lambda から DB (RDS/Aurora) への接続が必要な場合、VPC に Lambda を配置するソリューションがあります。
例えば、以下画像のように Lambda を VPC 内のサブネットに配置すると、DB への接続が可能になります。

しかし、Lambda のみで DB 接続を行う場合は、認証情報を環境変数等に保存する必要があり、Lambda 関数の中身を確認できる人は誰でもパスワードを把握できてしまうため、セキュリティ上の懸念があります。

よりセキュアに DB の認証情報を管理したい場合、Secrets Manager にパスワードを保存するソリューションがあります。
しかし、VPC に Lambda を配置しているため、Secrets Manager には届かなくなってしまいます。

ネットワーク経路として、Endpoint もしくは NAT Gateway のような通信経路を確保することで、
VPC 内の Lambda からも Secrets Manager 経由で DB 接続が可能になります。
本記事では、VPC Endpoint での方法をご紹介します。

設定内容
前提条件
- VPC 構築済み
- DB 構築済み(RDS/Aurora)
- Secrets Manager 構築済み
- Lambda 関数構築済み (Python3.14)
AWS Lambda
- VPC 設定から、DB が配置されているサブネットと同じサブネットに設定します。

- 実行ロール側に、secretsmanager:GetSecretValue 権限を付与します。
以下のサンプルポリシーを参考に、GetSecretValue 権限を付与してください。
※ 本番環境では、特定のシークレット ARN に限定することを推奨します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:ap-northeast-1:<AWSAccountID>:secret:*"
}
]
}

- 接続テストまでに3秒以上かかる場合はタイムアウトとなる場合があります。
画像のように十分な時間に変更してください。

- MySQL 操作のために、PyMySQL モジュールを zip としてアップロードします。
以下はサンプルコマンドです。
mkdir mysql && cd mysql
pip install PyMySQL -t .
zip -r pymysql-layer.zip .


画像のように、アップロードされたことを確認し、lambda_function.py ファイルの作成ボタンを押下します。

- 環境変数には、Secrets Manager ARN と DB のエンドポイントを入力し、以下のサンプルコードをデプロイします。

import json
import os
import boto3
import pymysql
SECRET_ARN = os.environ.get(
"SECRET_ARN",
)
SECRET_USERNAME_KEY = os.environ.get("SECRET_USERNAME_KEY", "username")
SECRET_PASSWORD_KEY = os.environ.get("SECRET_PASSWORD_KEY", "password")
DB_HOST = os.environ.get(
"DB_HOST",
)
DB_PORT = int(os.environ.get("DB_PORT", "3306"))
# Cache the secret across warm invocations to avoid repeated API calls
_cached_credentials = None
def get_db_credentials():
global _cached_credentials
if _cached_credentials is None:
client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId=SECRET_ARN)
secret = json.loads(response["SecretString"])
try:
_cached_credentials = {
"username": secret[SECRET_USERNAME_KEY],
"password": secret[SECRET_PASSWORD_KEY],
}
except KeyError as error:
# Expose key names only (never values) to help diagnose mismatches
raise RuntimeError(
f"Secret is missing key {error}. "
f"Available keys: {sorted(secret.keys())}"
) from error
return _cached_credentials
def lambda_handler(event, context):
try:
credentials = get_db_credentials()
except Exception as error:
message = f"Failed to fetch secret: {type(error).__name__}: {error}"
print(message)
return {"statusCode": 500, "body": json.dumps({"result": "failure", "error": message})}
connection = None
try:
connection = pymysql.connect(
host=DB_HOST,
port=DB_PORT,
user=credentials["username"],
password=credentials["password"],
connect_timeout=5,
)
with connection.cursor() as cursor:
cursor.execute("SHOW DATABASES")
databases = [row[0] for row in cursor.fetchall()]
print(f"Connection succeeded. Databases: {databases}")
return {
"statusCode": 200,
"body": json.dumps({"result": "success", "databases": databases}),
}
except Exception as error:
message = f"Connection failed: {type(error).__name__}: {error}"
print(message)
return {
"statusCode": 500,
"body": json.dumps({"result": "failure", "error": message}),
}
finally:
if connection:
connection.close()
VPC Endpoint
画像のように、VPC Endpoint を作成します。
サブネットは Lambda・DB が配置されているプライベートサブネットにしています。





Security Group
- DB のセキュリティグループのインバウンドルール側に、Lambda のセキュリティグループを追加します。

- VPC Endpoint のセキュリティグループのインバウンドルール側に、HTTPS(443)通信の許可を追加します。
※ 本記事では、配置されている VPC の CIDR を許可しています。環境に合わせてご変更ください。

結果
Lambda 側でテストを実施すると、以下のように SHOW DATABASES のクエリ結果が表示されていました。
これで、Secrets Manager 経由で DB に接続が成功していることがわかります。


さいごに
今回は Lambda から DB (RDS/Aurora) への接続を、Secrets Manager 経由で実施してみました。
VPC に Lambda を配置すると、別途ネットワーク経路が必要となる点はハマりポイントでもあると思います。
この記事が、誰かの助けになれば幸いです。
参考







