AWS上級 #6 Secrets Manager / Parameter Store
DB パスワード、外部 API キー、OAuth client secret — どこに置くか。コード / git は絶対 NG、環境変数に平文も危険。AWS には 2 つの選択肢があります — Secrets Manager と SSM Parameter Store。
この記事は両方の違い、自動ローテーション、コードから取得するパターン、ECS / Lambda との統合、IaC 接続、費用比較まで — シークレット / 設定管理の基準を一気に整理します。
シークレットが入らない場所 #
基礎 #6 セキュリティ基本 の延長。絶対にやらないこと:
- コードの中の平文 (
PASSWORD = "abc123") - git の .env (削除した後も history に永遠)
- README、Slack、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 の自動ローテーションテンプレート | 広範囲 (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 は高い + 遅い。
グローバルで 1 回 + キャッシング パターン:
import boto3, json
# Lambda コンテナの INIT 段階で 1 回
_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()
...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 をテンプレートで提供 (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 ユーザーのパスワードを差し替え。ローテーション瞬間に短い接続切れの可能性
- 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"] だけ見れば OK。
必要な権限 (#1 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 がローテーションで管理。
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 も自動同期。
よく出会う落とし穴 #
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 が暴走、費用 + 遅延。モジュールグローバルで 1 回 + キャッシング。
3) JSON フィールド抽出 ARN の形のタイポ #
arn:...:db-AbCdEf:password:: — コロンの数 / 位置が 1 文字でも間違えば 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 またはキャッシング。
まとめ #
今回つかんだもの:
- シークレットが入らない場所 — コード、git、README、Dockerfile、平文環境変数
- Secrets Manager — 自動ローテーション (RDS テンプレート)、常に KMS、secret 当たり $0.40
- Parameter Store — 設定 + シークレット。Standard 無料、SecureString も無料
- 選択ガイド — 自動ローテーション必要 → Secrets Manager。それ以外 → Parameter Store SecureString
- boto3 + キャッシング — モジュールグローバルで 1 回 + Powertools の
parameters - 自動ローテーション 4 段階 — createSecret → setSecret → testSecret → finishSecret。multi-user が無停止
- ECS 統合 — Task Definition の
secretsで環境変数を自動注入。Execution Role 権限が必須 - JSON シークレットの特定フィールド — ARN 末尾の
:fieldName:: - Lambda Extension — Parameters and Secrets Lambda Extension でキャッシング sidecar
- IaC —
random_password生成、CloudFormation の{{resolve:...}} - 秘密 / 設定分離 — ローテーション必要な核心だけ Secrets Manager、残りは Parameter Store
- Cross-Account / Cross-Region — Resource Policy / Replication
- 落とし穴 — Execution Role 権限、呼び出し暴走、ARN タイポ、IaC 平文、ローテーション中切断、throughput 上限
次回 — Step Functions #
このシリーズの最後。複数の Lambda / ECS / 外部 API 呼び出しを束ねて 1 つのワークフローに する方式です。
#7 Step Functions 入門 では State machine / Task / Choice / Parallel、Standard vs Express、Lambda オーケストレーション、エラー処理と retry まで — AWS ワークフローを一気に整理します。