目次
20 章

Secrets Manager / Parameter Store

AWS のシークレット / 設定管理を整理します。Secrets Manager と SSM Parameter Store の役割の違い、自動回転、コードから取得(boto3 / キャッシング / Powertools)、ECS と Lambda 統合、IaC 連携、秘密と設定の分離、コスト比較までを扱います。

DB パスワード、外部 API キー、OAuth client secret をどこに置くのでしょうか。コードと git は絶対にだめで、環境変数に平文も危険です。AWS には2つの選択肢があります — Secrets ManagerSSM 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 ManagerSSM Parameter Store
正体シークレット専用のマネージド設定 + シークレット兼用
自動回転あり (Lambda ベース)なし (手動)
Crypto常に KMS 暗号化標準 (平文) / SecureString (KMS 暗号化)
バージョン管理自動 (ステージ: AWSCURRENT、AWSPREVIOUS)自動 (整数バージョン)
サイズの上限64 KBStandard 4 KB / Advanced 8 KB
コストsecret あたり $0.40 / 月 + API callStandard 無料 / Advanced 有料
統合RDS / Redshift の自動回転 templates広範囲 (CloudFormation、ECS、Lambda)

一行決定ガイド #

決定ツリー
DB パスワード / 外部 API キー + 自動回転 必要 → Secrets Manager
一般設定値 (DB ホスト, region, feature flag) → Parameter Store
回転不要なシークレット (例: 外部 API キー) → Parameter Store SecureString (安い)

Secrets Manager #

作る #

JSON シークレット (DB 認証情報)
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-AbCdEf

ARN 末尾の 6文字ランダム (AbCdEf) はシークレットの無断 enumeration 防止用です。IAM ポリシーでは arn:.../myapp/prod/db-* のようにワイルドカードを使います。

読む — boto3 #

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 は高くて遅いです。

グローバルで一度 + キャッシングのパターンを使います。

Lambda モジュールのグローバルで一度
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行で処理します。

aws-lambda-powertools
from aws_lambda_powertools.utilities import parameters

# TTL 5 分キャッシング
creds = parameters.get_secret("myapp/prod/db", transform="json",
                              max_age=300)

自動回転 #

Secrets Manager の最も大きな長所です。一定の周期ごとに新しいパスワードを生成し、DB のユーザーパスワードを自動で更新します。

回転の有効化 (RDS 統合)
aws secretsmanager rotate-secret \
  --secret-id myapp/prod/db \
  --rotation-lambda-arn arn:aws:lambda:ap-northeast-2:...:SecretsManagerRDSPostgreSQLRotationSingleUser \
  --rotation-rules AutomaticallyAfterDays=30

AWS がマネージド回転 Lambda を templates として提供します (RDS / Redshift / DocumentDB / Aurora)。30日ごとに新しいパスワードに回転します。

回転の4段階 (理解の次元) #

rotation の流れ
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 #

StandardAdvanced
サイズ4 KB8 KB
ポリシー (有効期限、通知)なしあり
Throughput40 / 秒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 String
暗号化 (シークレット)
aws ssm put-parameter \
  --name /myapp/prod/external-api-key \
  --value sk_live_abc123 \
  --type SecureString \
  --key-id alias/aws/ssm

SecureString は KMS で暗号化します。AWS 管理キー (alias/aws/ssm) が無料です。

読む #

boto3
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"])
Powertools
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) --overwrite

ECS / Lambda との統合 #

最もよく出会う方式です。Task Definition / 関数の環境変数にシークレットを直接注入します。

ECS Task Definition #

Task Definition の secrets 項目
{
  "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)。

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 で呼び出します。

Lambda Extension の使用 (HTTP)
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 #

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 #

CloudFormation の中でシークレットを参照
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 #

別のアカウントのユーザーにシークレットへのアクセスを許可します。

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-1

primary が回転されると 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 またはキャッシングを使います。

練習問題 #

  1. 自分のアプリが持っている秘密と設定をすべて書き、§「秘密と設定の分離」のパターンに沿ってどれを Secrets Manager に、どれを Parameter Store SecureString に置くかを分けてみましょう。自動回転が必要な項目とそうでない項目を分ける基準を1行で書きます。
  2. ECS Task にシークレットが注入されない “ResourceInitializationError” が出たとき、点検すべき2つの権限が何かを §「ECS / Lambda との統合」と 第15章 ECS と Fargate の Execution Role を根拠に書いてみましょう。
  3. §「コスト」の数値を根拠に、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 のワークフローエンジンを整理します。

X