Secrets Manager / Parameter Store
AWS のシークレット / 設定管理を整理します。Secrets Manager と SSM Parameter Store の役割の違い、自動回転、コードから取得(boto3 / キャッシング / Powertools)、ECS と Lambda 統合、IaC 連携、秘密と設定の分離、コスト比較までを扱います。
DB パスワード、外部 API キー、OAuth client secret をどこに置くのでしょうか。コードと git は絶対にだめで、環境変数に平文も危険です。AWS には2つの選択肢があります — Secrets Manager と SSM Parameter Store です。
本章は2つの違い、自動回転、コードから取得するパターン、ECS / Lambda との統合、IaC 連携、コスト比較まで、シークレット / 設定管理の基準を一度に整理します。直前の 第19章 EventBridge / SQS / SNS までがコンポーネント間の通信だったとすれば、本章はそれらのコンポーネントが安全に持っていなければならない秘密を扱います。ここで固めるパターンは 第15章 ECS と Fargate の Task Definition への secret 注入、4部 第22章 ECS Fargate デプロイの骨格、第23章 RDS 連携 でそのまま使われます。
シークレットを入れてはいけない場所 #
第6章 セキュリティ基本 の延長です。絶対にしてはいけないことは次のとおりです。
- コードの中の平文 (
PASSWORD = "abc123") - git に .env (消したあとも history に永遠に残る)
- README、メッセンジャー、Wiki、メールに貼り付け
- Dockerfile の中の環境変数 / ビルド引数 (イメージ layer に刻まれる)
- ECS Task Definition の平文 environment variables (コンソール / IaC / ログ露出)
代わりに使う場所が Secrets Manager または SSM Parameter Store です。
2つの違い #
| Secrets Manager | SSM Parameter Store | |
|---|---|---|
| 正体 | シークレット専用のマネージド | 設定 + シークレット兼用 |
| 自動回転 | あり (Lambda ベース) | なし (手動) |
| Crypto | 常に KMS 暗号化 | 標準 (平文) / SecureString (KMS 暗号化) |
| バージョン管理 | 自動 (ステージ: AWSCURRENT、AWSPREVIOUS) | 自動 (整数バージョン) |
| サイズの上限 | 64 KB | Standard 4 KB / Advanced 8 KB |
| コスト | secret あたり $0.40 / 月 + API call | Standard 無料 / Advanced 有料 |
| 統合 | RDS / Redshift の自動回転 templates | 広範囲 (CloudFormation、ECS、Lambda) |
一行決定ガイド #
DB パスワード / 外部 API キー + 自動回転 必要 → Secrets Manager
│
一般設定値 (DB ホスト, region, feature flag) → Parameter Store
│
回転不要なシークレット (例: 外部 API キー) → Parameter Store SecureString (安い)Secrets Manager #
作る #
aws secretsmanager create-secret \
--name myapp/prod/db \
--description "Postgres for myapp prod" \
--secret-string '{
"username": "myapp",
"password": "very-strong-secret",
"host": "myapp-prod.abc123.ap-northeast-2.rds.amazonaws.com",
"port": 5432,
"dbname": "myapp"
}'ARN が返されます。
arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:myapp/prod/db-AbCdEfARN 末尾の 6文字ランダム (AbCdEf) はシークレットの無断 enumeration 防止用です。IAM ポリシーでは arn:.../myapp/prod/db-* のようにワイルドカードを使います。
読む — boto3 #
import boto3, json
client = boto3.client("secretsmanager")
resp = client.get_secret_value(SecretId="myapp/prod/db")
creds = json.loads(resp["SecretString"])
print(creds["username"], creds["password"])キャッシング — 毎回呼び出しごとに取得するとコスト #
API call あたりで課金されるため、毎リクエストごとの get_secret_value は高くて遅いです。
グローバルで一度 + キャッシングのパターンを使います。
import boto3, json
# Lambda コンテナの INIT 段階で一度
_secrets = None
def get_db_creds():
global _secrets
if _secrets is None:
resp = boto3.client("secretsmanager").get_secret_value(
SecretId="myapp/prod/db")
_secrets = json.loads(resp["SecretString"])
return _secrets
def handler(event, context):
creds = get_db_creds()
...第17章 Lambda 基礎 のグローバル変数パターンと同じ文脈です。Powertools の Parameters がキャッシング / TTL / 多重シークレットを1行で処理します。
from aws_lambda_powertools.utilities import parameters
# TTL 5 分キャッシング
creds = parameters.get_secret("myapp/prod/db", transform="json",
max_age=300)自動回転 #
Secrets Manager の最も大きな長所です。一定の周期ごとに新しいパスワードを生成し、DB のユーザーパスワードを自動で更新します。
aws secretsmanager rotate-secret \
--secret-id myapp/prod/db \
--rotation-lambda-arn arn:aws:lambda:ap-northeast-2:...:SecretsManagerRDSPostgreSQLRotationSingleUser \
--rotation-rules AutomaticallyAfterDays=30AWS がマネージド回転 Lambda を templates として提供します (RDS / Redshift / DocumentDB / Aurora)。30日ごとに新しいパスワードに回転します。
回転の4段階 (理解の次元) #
1. createSecret → 新しいパスワード生成、AWSPENDING として保存
2. setSecret → DB に新しいパスワードを適用
3. testSecret → 新しいパスワードで接続テスト
4. finishSecret → AWSPENDING → AWSCURRENT、古いもの → AWSPREVIOUSコードは常に AWSCURRENT (デフォルト) を取得します。回転の途中、しばらく AWSPREVIOUS も有効です。
Single user vs Multi-user 回転 #
- Single user: 1人のユーザーのパスワードを差し替えます。回転の瞬間に短い connection の切断が起きうります。
- Multi-user: 2人のユーザーを交互に使います。無停止回転が可能です。運用推奨です。
Parameter Store (SSM) #
Standard vs Advanced #
| Standard | Advanced | |
|---|---|---|
| サイズ | 4 KB | 8 KB |
| ポリシー (有効期限、通知) | なし | あり |
| Throughput | 40 / 秒 | 1,000 / 秒 (オプション) |
| コスト | 無料 | パラメータ $0.05 / 月 |
基本は Standard で始めます。
作る #
aws ssm put-parameter \
--name /myapp/prod/aws-region \
--value ap-northeast-2 \
--type String
# 階層 (自由 — '/' で構造化)
aws ssm put-parameter \
--name /myapp/prod/feature/new-checkout \
--value enabled \
--type Stringaws ssm put-parameter \
--name /myapp/prod/external-api-key \
--value sk_live_abc123 \
--type SecureString \
--key-id alias/aws/ssmSecureString は KMS で暗号化します。AWS 管理キー (alias/aws/ssm) が無料です。
読む #
import boto3
ssm = boto3.client("ssm")
# 単一
resp = ssm.get_parameter(
Name="/myapp/prod/external-api-key",
WithDecryption=True
)
api_key = resp["Parameter"]["Value"]
# 多重 (階層)
resp = ssm.get_parameters_by_path(
Path="/myapp/prod/",
Recursive=True,
WithDecryption=True
)
for p in resp["Parameters"]:
print(p["Name"], p["Value"])from aws_lambda_powertools.utilities import parameters
# 単一
api_key = parameters.get_parameter(
"/myapp/prod/external-api-key", decrypt=True, max_age=300)
# 階層
all_params = parameters.get_parameters(
"/myapp/prod/", decrypt=True, max_age=300)バージョン管理 #
同じ名前に新しい値を put するたびにバージョンが 1, 2, 3 … 累積されます。古い値にロールバックできます。
# バージョン 5 の値を見る
aws ssm get-parameter-history --name /myapp/prod/db-host
# バージョン 3 に戻す (コピーして新しいバージョン)
aws ssm put-parameter --name /myapp/prod/db-host \
--value $(aws ssm get-parameter --name /myapp/prod/db-host:3 \
--query 'Parameter.Value' --output text) --overwriteECS / Lambda との統合 #
最もよく出会う方式です。Task Definition / 関数の環境変数にシークレットを直接注入します。
ECS Task Definition #
{
"containerDefinitions": [
{
"name": "web",
"image": "...",
"environment": [
{"name": "ENV", "value": "production"}
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:myapp/prod/db-AbCdEf"
},
{
"name": "STRIPE_KEY",
"valueFrom": "arn:aws:ssm:ap-northeast-2:123456789012:parameter/myapp/prod/stripe-key"
}
]
}
]
}ECS が Task の開始直前にシークレットを取得してコンテナの環境変数として注入します。コードの中では os.environ["DATABASE_URL"] だけ見ればよいです。
必要な権限です (第15章 ECS と Fargate の Execution Role)。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue"],
"Resource": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:myapp/prod/*"
},
{
"Effect": "Allow",
"Action": ["ssm:GetParameters"],
"Resource": "arn:aws:ssm:ap-northeast-2:123456789012:parameter/myapp/prod/*"
},
{
"Effect": "Allow",
"Action": ["kms:Decrypt"],
"Resource": "arn:aws:kms:ap-northeast-2:123456789012:key/*"
}
]
}JSON シークレットの特定フィールドだけ #
Secrets Manager のシークレットが JSON のとき、ECS が特定のフィールドだけを環境変数として渡せます。
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:...:myapp/prod/db-AbCdEf:password::"
}ARN 末尾の :password:: が JSON の password フィールドだけを抽出します。最初の : と最後の : の間が JSON key で、2番目の : の後ろは version です。
Lambda のシークレット #
Lambda も同じパターンです。関数の environment variables に直接シークレットを刻まず、コードから boto3 で取得します。または AWS Parameters and Secrets Lambda Extension でキャッシング sidecar を使います — 関数の中から localhost:2773 で呼び出します。
import urllib.request, json, os
def get_secret(name):
url = f"http://localhost:2773/secretsmanager/get?secretId={name}"
req = urllib.request.Request(url,
headers={"X-Aws-Parameters-Secrets-Token": os.environ["AWS_SESSION_TOKEN"]})
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read())
return json.loads(data["SecretString"])Extension が自動でキャッシングします (TTL の設定が可能) — boto3 のキャッシングコードを書かなくて済みます。
IaC との連携 #
Terraform #
resource "aws_secretsmanager_secret" "db" {
name = "myapp/prod/db"
}
resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = jsonencode({
username = "myapp"
password = random_password.db.result
host = aws_db_instance.main.address
})
}
resource "random_password" "db" {
length = 32
special = false
}パスワード自体を IaC に直接刻みません。
random_passwordで生成するか、AWS が回転で管理します。Terraform 自体は 第25章 Terraform 入門 で扱います。
CloudFormation の dynamic reference #
Resources:
MyDB:
Type: AWS::RDS::DBInstance
Properties:
MasterUsername: '{{resolve:secretsmanager:myapp/prod/db:SecretString:username}}'
MasterUserPassword: '{{resolve:secretsmanager:myapp/prod/db:SecretString:password}}'{{resolve:...}} パターンが stack の中でシークレット / パラメータを直接取得します。平文が stack テンプレート / ログに残りません。
コスト #
Secrets Manager #
- シークレットあたり $0.40 / 月
- API call 10,000 あたり $0.05
10個のシークレット + 毎日 100呼び出し = $4 + ほぼ 0 = $4 / 月 です。
Parameter Store (Standard) #
- 無料 (Throughput 40 / 秒まで)
- KMS 使用時に KMS API call コスト (10,000 あたり $0.03)
10個の SecureString + 毎日 100呼び出し = ほぼ 0 です。
価格差のポイント #
回転 / マネージド統合が不要なら Parameter Store SecureString が圧倒的に安いです。100個のシークレットなら Secrets Manager $40 / 月 vs Parameter Store ~$0 です。
秘密と設定の分離 #
運用推奨パターンです。
Secrets Manager
├── /myapp/prod/db (自動回転)
└── /myapp/prod/jwt-signing (自動回転)
Parameter Store
├── /myapp/prod/db-host
├── /myapp/prod/aws-region
├── /myapp/prod/feature/new-checkout (feature flag)
├── /myapp/prod/log-level
└── /myapp/prod/external-api-key (SecureString — 回転しないシークレット)回転が必要な核心の秘密だけ Secrets Manager に置きます。残りは Parameter Store でコストを節約します。
Cross-Account / Cross-Region #
Resource Policy #
別のアカウントのユーザーにシークレットへのアクセスを許可します。
aws secretsmanager put-resource-policy \
--secret-id myapp/prod/db \
--resource-policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::222222222222:root"},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}]
}'Replication #
別のリージョンに自動複製します (Multi-region 運用)。
aws secretsmanager replicate-secret-to-regions \
--secret-id myapp/prod/db \
--add-replica-regions Region=us-east-1primary が回転されると replica も自動同期されます。マルチリージョン · 災害復旧は 第30章 災害復旧 · バックアップ で扱います。
よく出会う落とし穴 #
1) シークレットが ECS Task に注入されない #
Task の開始時に “ResourceInitializationError … not authorized to perform secretsmanager:GetSecretValue” が出ます。Execution Role の権限漏れです。または SecureString の KMS key に kms:Decrypt の漏れです。
2) 毎リクエストごとに boto3 呼び出し #
コードのハンドラの中で毎回 get_secret_value すると API call が暴走し、コスト + 遅延が生じます。モジュールのグローバルで一度 + キャッシングします。
3) JSON フィールド抽出 ARN の形の打ち間違い #
arn:...:db-AbCdEf:password:: でコロンの個数 / 位置が一文字でも違うと silently 空文字列になります。コンソールからそのままコピーして使います。
4) IaC の平文の秘密 #
Terraform secret_string = "abc123" は state ファイルに平文で残ります。常に random_password または外部入力を使い、state は暗号化します (S3 + KMS)。
5) 回転後に古いパスワードで呼び出し失敗 #
回転の途中、短い時間の間、古いパスワードで作った connection が切れることがあります。multi-user 回転 + connection retry を使います。
6) シークレット名の意図しない公開 #
ARN の 6文字ランダム (-AbCdEf) が CloudTrail / ログに残っても推測は難しいですが、シークレット名自体は (myapp/prod/db) コンソール権限を持つ人なら誰でも見られます。権限を減らし (シークレットごとに別の IAM) 露出面を狭めます。
7) Parameter Store throughput の上限 #
Standard の 40 req/s の上限にぶつかると throttling が発生します。ホットパスで頻繁に呼び出すなら Advanced またはキャッシングを使います。
練習問題 #
- 自分のアプリが持っている秘密と設定をすべて書き、§「秘密と設定の分離」のパターンに沿ってどれを Secrets Manager に、どれを Parameter Store SecureString に置くかを分けてみましょう。自動回転が必要な項目とそうでない項目を分ける基準を1行で書きます。
- ECS Task にシークレットが注入されない “ResourceInitializationError” が出たとき、点検すべき2つの権限が何かを §「ECS / Lambda との統合」と 第15章 ECS と Fargate の Execution Role を根拠に書いてみましょう。
- §「コスト」の数値を根拠に、100個のシークレットをすべて Secrets Manager に置くときと、回転が必要な5個だけを Secrets Manager に置いて残りの95個を Parameter Store SecureString に置くときの月のコスト差を計算してみましょう。
一行まとめ: シークレットはコード · git · README · Dockerfile · 平文の環境変数に置かず Secrets Manager または Parameter Store に置く。自動回転(RDS templates)と常に KMS が必要なら Secrets Manager(secret あたり $0.40)を、回転が不要な設定 · シークレットは無料の Parameter Store SecureString を使う。コードからはモジュールのグローバルで一度 + Powertools キャッシングで取得し、ECS は Task Definition の
secretsで環境変数に自動注入するが Execution Role の権限が必須。IaC には平文の秘密を刻まずrandom_passwordや{{resolve:...}}を使う。
次の章 #
次の 第21章 Step Functions 入門 は3部の最後です。複数の Lambda / ECS / 外部 API 呼び出しをまとめて1つのワークフローにする方式を扱います。State machine / Task / Choice / Parallel / Map、Standard vs Express、Lambda オーケストレーション、エラー処理と retry まで AWS のワークフローエンジンを整理します。