検証の狙い
検証する主張は次の4点です。
- ランタイムとデプロイパッケージ(
.so)は一体であり、ランタイム単独変更(または自動アップグレード)はimport失敗を起こす - ローテーション失敗専用の CloudWatch メトリクスは存在せず、CloudTrail
RotationFailed+ LambdaErrorsでのみ追える -
Found credentials in environment variables以降で無音になるのはランタイムではなくネットワーク起因である(切り分けの分岐) - 正規の更新パス(コンソールで新しいローテーション関数を作成)でローテーションが復旧する
設計判断・思想(「更新責任はユーザー側にある」「Runtimeを常に指定すべきではない」等)は観測できないため検証対象から除外します。
本検証は払い出しに SAR (Serverless Application Repository) の
SecretsManagerRDSPostgreSQLRotationSingleUserテンプレートを用いる構成で実施します。
AWS::SecretsManagertransform 固有の挙動検証(旧版のデフォルトランタイムが Python 3.10 か、CFNRuntime
追加 /2024-09-16移行での復旧)は、Terraform 管理のローテーション紐付けと競合しないSAR採用の都合上スコープ外とします。
主張→実験の対応づけ表
| # | 検証する主張(詳細版由来) | 再現方法(何を起こすか) | 証拠(観測すべきログ行 / メトリクス) |
|---|---|---|---|
| 1 | ランタイム単独変更/自動アップグレードで同梱.soと非互換になり壊れる |
SAR(SecretsManagerRDSPostgreSQLRotationSingleUser)で関数を払い出し、LambdaのRuntime設定だけを別バージョンに手動変更してローテーション実行 |
CloudWatch Logsに Unable to import module 'lambda_function' または pg module not found
|
| 2 | ローテーション失敗専用CloudWatchメトリクスは無い/CloudTrail+Errorsで追う | 主張1で失敗させた状態でメトリクスとイベントを確認 | Lambda Errors > 0、CloudTrailに RotationFailed イベント(Secrets Manager名前空間にローテーション成否のネイティブメトリクスが無いこと) |
| 3 |
Found credentials...後の無音はネットワーク起因でランタイムとは別軸 |
VPC内のLambdaからSecrets Managerエンドポイントに到達できない状態(エンドポイント未設定/SG遮断)でローテーション実行 | Logsが Found credentials in environment variables 以降で無音(importエラーは出ない)/関数タイムアウト |
| 4 | 正規パスでローテーションが復旧する | 失敗状態のシークレットに対し、コンソールで新しいローテーション関数を作成して適用 | CloudTrailに RotationSucceeded、Logsで4ステップ(createSecret/setSecret/testSecret/finishSecret)完走、AWSCURRENTが更新される |
検証環境
hosted rotation Lambda は Terraform 単体では生成できない ため、本検証では SAR(Serverless Application Repository)の公式アプリ で払い出します。Terraform はシークレットとローテーションのスケジュール紐付けまでを担い、ローテーション Lambda 本体は SAR で払い出した関数 ARN を参照させます。SAR アプリは Lambda 本体・実行ロール・invoke permission のみを作成し RotationSchedule を作らないため、Terraform 側のローテーション紐付けと競合しません。
使用したリソースは以下のGitHubにて公開しています。
aws-secrets-rotation-lambda-failure-lab
構成要素
- 検証用シークレット(RDS資格情報を模したJSON)
- ローテーション用 Lambda(SAR アプリ
SecretsManagerRDSPostgreSQLRotationSingleUserで払い出し、またはコンソールで新規作成) - 対象DB(最小構成の RDS。主張1〜2・4の再現には接続テスト=
testSecretを通すために必要。主張1の import 失敗の観測だけならcreateSecret到達前後で十分な場合もある) - IAM(ローテーションLambda実行ロール。SAR テンプレートが自動付与)
- 主張3用に VPC / サブネット / セキュリティグループ(エンドポイント到達不可を意図的に作る)
Terraform 概要(抜粋)
完全な一式ではなく、Terraform が担う範囲の抜粋です。
# シークレット本体
resource "aws_secretsmanager_secret" "rds" {
name = "verify/rds-rotation-runtime"
}
resource "aws_secretsmanager_secret_version" "rds" {
secret_id = aws_secretsmanager_secret.rds.id
secret_string = jsonencode({
username = "appuser"
password = "INITIAL_PLACEHOLDER"
engine = "postgres"
host = aws_db_instance.verify.address
dbname = "appdb"
port = 5432
})
}
# ローテーションのスケジュール紐付け。
# rotation_lambda_arn には SAR で払い出した hosted rotation Lambda の ARN を渡す
# (Terraform 単体では Lambda 本体は vend できない)。
resource "aws_secretsmanager_secret_rotation" "rds" {
secret_id = aws_secretsmanager_secret.rds.id
rotation_lambda_arn = var.rotation_lambda_arn
rotation_rules {
automatically_after_days = 30
}
}
hosted rotation Lambda 本体は、検証では SAR アプリ SecretsManagerRDSPostgreSQLRotationSingleUser で vend し、生成された関数 ARN を上記 rotation_lambda_arn に渡します。
コスト注意と Teardown 手順
- RDS インスタンスは課金されます。 検証完了後は速やかに削除してください。検証中も最小インスタンスクラス・シングルAZ・ストレージ最小で構成します。
- CloudWatch Logs / CloudTrail のログ保存も少額ながら課金対象です。検証後はロググループも削除します。
- Teardown は次の順で確実に実施します。
- ローテーションスケジュールを無効化(
aws_secretsmanager_secret_rotationの削除) - hosted rotation Lambda を vend した CloudFormation スタックを削除(
aws cloudformation delete-stack/ マネジメントコンソール) - Terraform 管理分を破棄:
terraform destroy - 残存リソース確認: Secrets Manager のシークレット(強制削除する場合は復元待機期間に注意)、Lambda 関数、RDS インスタンス・サブネットグループ、VPC / サブネット / セキュリティグループ、ロググループ
- ローテーションスケジュールを無効化(
- 検証後、対象リソースが残っていないことを必ず確認すること。
セキュリティ注意
- 検証用シークレットの初期パスワードはプレースホルダ(
INITIAL_PLACEHOLDER)とし、実運用の資格情報は使わない。 - 検証環境は本番から分離したアカウント/VPCで実施する。
- ローテーションLambdaの実行ロールは検証対象シークレット・DBに限定し、過剰な権限を付与しない。
- 主張3でエンドポイント到達不可を意図的に作る際も、検証用VPCに閉じて行い、本番経路に影響を与えない。
シナリオ別検証
以下の実測結果は、検証用 AWS 環境で実際に観測したログ・メトリクス・スクリーンショットです。ログの抜粋はコンソール表示をそのまま転記しています。
検証1: ランタイム単独変更/自動アップグレードで.so非互換により壊れる
構成図
仮説
ランタイムとデプロイパッケージ(依存・.so)は一体であり、LambdaのRuntime設定だけを書き換える(または廃止に伴う自動アップグレードでランタイムだけが上がる)と、同梱.soのABIと不一致になり import に失敗する。
再現手順
- AWS Serverless Application Repository (SAR) の
SecretsManagerRDSPostgreSQLRotationSingleUserで hosted rotation Lambda を払い出す(同梱依存は PyGreSQL)。起点ランタイムはaws lambda get-function-configurationで確認しておく。 - Lambda コンソールで対象関数の
Runtime設定だけを別バージョンに手動変更する(同梱.soは変えない)。 - Secrets Manager で「Rotate secret immediately」を実行する。
- 対象関数の CloudWatch Logs を確認する。
観測すべき証拠
CloudWatch Logs に Unable to import module 'lambda_function' または pg module not found(あるいは .so ロード失敗)が出力されれば、ランタイムと同梱依存の不一致が実証される。
実測結果
-
ランタイム変更
-
エラーログチェック(CloudWatch Logs のエラー文抜粋)
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': Cannot import shared library for PyGreSQL, probably because no libpq.so is installed. No module named 'pg._pg' Traceback (most recent call last):
考察
ランタイムだけを別バージョンへ手動変更し、同梱の .so(PyGreSQL)はそのままで手動ローテーションを実行したところ、CloudWatch Logs に Runtime.ImportModuleError: Unable to import module 'lambda_function' が出力された。エラー本文も Cannot import shared library for PyGreSQL / No module named 'pg._pg' と、同梱ネイティブ拡張(libpq.so / pg._pg)のロード失敗を直接示している。仮説どおり、ランタイムと同梱依存(.so)は一体であり、ランタイム単独変更で import 失敗が起きることが実証された。
検証2: ローテーション失敗専用CloudWatchメトリクスは無い/CloudTrail+Errorsで追う
構成図
仮説
ローテーション失敗専用の CloudWatch メトリクスは存在しない。失敗は CloudTrail の RotationFailed イベントと Lambda の Errors の併用でしか追えない。
再現手順
- 検証1で失敗状態を作る。
- CloudWatch メトリクス側で Secrets Manager 名前空間にローテーション成否のネイティブメトリクスが無いことを確認する。
- Lambda メトリクスで
Errorsを確認する。 - CloudTrail のイベント履歴で
RotationFailedを確認する。
観測すべき証拠
Lambda Errors > 0 かつ CloudTrail に RotationFailed イベントが記録され、一方で Secrets Manager 側にローテーション成否専用メトリクスが見当たらなければ、主張が実証される。
実測結果
考察
Secrets Manager 名前空間のメトリクスは SecretCount のみで、ローテーション成否を示す専用メトリクスは存在しなかった。一方、失敗そのものは Lambda の Errors(>0)と CloudTrail の RotationFailed イベントの両方で確認できた。したがって、ローテーション失敗の監視は Secrets Manager のネイティブメトリクスではなく、Lambda Errors と CloudTrail RotationFailed の併用で行う必要がある、という主張が実証された。
検証3: Found credentials...後の無音はネットワーク起因(ランタイムとは別軸)
構成図
仮説
CloudWatch Logs が Found credentials in environment variables 以降で無音になるのは、Secrets Manager エンドポイント到達不可(ネットワーク)の典型であり、ランタイム更新では直らない。import エラーとは別の切り分けに進むべきである。
再現手順
- 正常に動くローテーション Lambda を VPC 内に配置する。
- Secrets Manager への到達経路を意図的に塞ぐ(本検証では Secrets Manager の VPC エンドポイントを一時的に削除)。
- ローテーションを実行し CloudWatch Logs を確認する。
観測すべき証拠
Logs が Found credentials in environment variables の後で無音になり(import エラーや pg module not found は出ない)、関数がタイムアウトすれば、ネットワーク起因でありランタイムとは別軸であることが実証される。
実測結果
-
CloudWatch Logs を確認(Lambda はタイムアウト 30 秒で設定しており、ログ上で
Duration: 30000.00 msとなっていることからタイムアウトが発生したことが確認できる)

考察
VPC エンドポイントを削除して Secrets Manager への到達経路を断った状態でローテーションを実行すると、CloudWatch Logs は Found credentials in environment variables 以降で無音となり、検証1で見られた import エラー(Unable to import module / pg module not found)は出力されなかった。最終的に Duration: 30000.00 ms でタイムアウト(設定値30秒)しており、無音はランタイム不整合ではなくネットワーク到達不可が原因であることが実証された。同じ「ローテーション失敗」でも、検証1(import エラーが出る)とは切り分けの軸が異なる点が対比できる。
検証4: 正規パスでローテーションが復旧する
構成図
仮説
失敗状態のローテーションに対し、正規パス(壊した構成を同梱依存と互換のある正常な状態へ戻す/コンソールで新しいローテーション関数を作成する)を取れば、ローテーションが復旧する。
再現手順
- 検証1・検証3で作った失敗要因を正常な状態へ戻す。
- Lambda のランタイムを
Python 3.12(同梱依存と互換のあるバージョン)に戻す。 - 検証3で削除した Secrets Manager の VPC エンドポイントを再作成する。
- 「Rotate secret immediately」を実行し、CloudWatch Logs と CloudTrail、シークレットのステージングラベル(パスワード)を確認する。
観測すべき証拠
4ステップ(createSecret / setSecret / testSecret / finishSecret)が完走し、AWSCURRENT が新パスワードに更新されれば復旧が実証される。
実測結果
考察
ランタイムを同梱依存と互換のある Python 3.12 に戻し、検証3で削除した VPC エンドポイントを再作成したうえで手動ローテーションを実行すると、CloudWatch Logs に4ステップ(createSecret / setSecret / testSecret / finishSecret)が出力され、AWSCURRENT のパスワードが新しい値に更新された。これにより、壊した構成を正常な状態へ戻す正規パスでローテーションが復旧することが実証された。なお本実測では「新しいローテーション関数の作成」ではなく、検証1・検証3で意図的に壊した要因(ランタイム・ネットワーク)の復元によって復旧を確認している。いずれも「正常な構成へ戻す」という同じ正規パスの一形態である。
まとめ
検証用 AWS 環境での実測により、4つの主張はいずれも観測された証拠で裏付けられました。
| # | 主張 | 結果 | 主な証拠 |
|---|---|---|---|
| 1 | ランタイム単独変更で同梱.soと非互換になり壊れる |
✅ | CloudWatch Logs の Runtime.ImportModuleError: Unable to import module 'lambda_function' / Cannot import shared library for PyGreSQL / No module named 'pg._pg'
|
| 2 | ローテーション失敗専用 CloudWatch メトリクスは無い | ✅ | Secrets Manager 名前空間は SecretCount のみ/Lambda Errors > 0/CloudTrail RotationFailed
|
| 3 |
Found credentials... 後の無音はネットワーク起因 |
✅ | VPC エンドポイント削除後、import エラーは出ず Duration: 30000.00 ms でタイムアウト |
| 4 | 正規パスでローテーションが復旧する | ✅ | ランタイム復元・VPC エンドポイント再作成後、4ステップ完走+AWSCURRENT のパスワード更新 |
- 検証1と検証3は、どちらも「ローテーション失敗」でありながら切り分けの軸が異なります(import エラーが出る=ランタイム/同梱依存の不整合、無音でタイムアウト=ネットワーク到達不可)。ログに import エラーが出るか否かが分岐の決め手になります。
- 検証2のとおり、ローテーション失敗を監視する専用メトリクスは存在しないため、Lambda
Errorsと CloudTrailRotationFailedを併用した監視設計が必要です。 - 検証4は「新しいローテーション関数の作成」そのものではなく、検証1・検証3で意図的に壊した構成(ランタイム・ネットワーク)を正常状態へ戻すことで復旧を確認しました。いずれも「正常な構成へ戻す」正規パスの一形態です。















