はじめに
こんにちは!KDDIアイレット株式会社の水野 永遠です。この記事は「2026 Japan AWS Jr. Champions 真夏のQiitaリレー」の23日目の記事となります。過去の投稿(リンク集)はこちらからご覧ください。
アプリの設定値、たとえば外部APIのエンドポイントやタイムアウト値、APIキーのような値をParameter Storeに置くことになりました。ここで手が止まりました。置き方の選択肢が複数あって、どれを選んでも動いてしまうからです。
まず、パラメータの分け方です。「まとめる」と言っても、どの単位で1つにするかで選択肢は3つあります。
# その1. サービス全体で1つにまとめる(全アプリが同じパラメータを読む)
/myservice/config = {"api_endpoint":"https://...","timeout":"30","api_key":"..."}
# その2. 読むアプリ(リソース)ごとにまとめる
/myservice/web-ecs/config = {"api_endpoint":"https://...","timeout":"30"}
/myservice/batch-lambda/config = {"api_endpoint":"https://...","retry":"3"}
# その3. 値の種類ごとに分ける
/myservice/api_endpoint = https://...
/myservice/timeout = 30
/myservice/api_key = ...
パラメータにはStandardとAdvancedという2つの階層もあります。違いはサイズ上限(4KBと8KB)や料金ですが、アプリの設定値はたいてい4KBに収まるため、これもどちらを選んでも登録できてしまいます。
どれを選んでも動くものは、比較記事を読んでも自分の環境での設計判断の決め手がつかめませんでした。そこで、判断材料になりそうな制約を検証用のAWSアカウントで実際に確かめ、どう決めたかを過程ごと残すことにします。
※実測は2026年8月、東京リージョン、aws-cli 2.36.27で行いました。挙動は今後変わる可能性があります。
結論から
先に、私がたどり着いた決め方を書きます。前提が違えば結論も変わるので、あくまで一つの整理として読んでください。
パラメータの分け方は、次の観点で判断しました。
- 値ごとにIAMで権限を分けたい事情がある → 種類ごとに分ける
- 複数のアプリで同じ値を共有し、重複管理を避けたい → 種類ごとに分ける
- アプリごとに設定が閉じていて、上記の事情がない → 読むアプリの単位でまとめる
- アプリ間で設定の共有が多く、権限を分ける必要もない → サービス全体で1つも成り立つ
私の状況では、読むアプリの単位でまとめる形を選びました。理由は7章に書きます。
取得回数の差は、判断材料としては優先度を下げました。取得回数が気になる理由を分解すると、料金、起動や処理にかかる時間、呼び出し回数制限(スロットリング)の3つですが、料金は後述のとおりリクエスト回数ではなく「返ってきた個数」で数えられるため、分け方で差がつきません。残る2つも、実行中に何度も取得する構成でなければ効いてきません。私の取得経路はECSのタスク定義からの環境変数注入で、取得はタスク起動時の1回だけなので、この論点は事実上消えました。SDKやLambda Extensionで実行中に都度取得する構成なら、頻度とキャッシュの設計として考える必要があります。
StandardかAdvancedかは、サイズの見通しで決めました。
- 4KBに収まる見通しがある → Standardを明示して作る(超えたら登録エラーになるので必ず気づける)
- 4KBを超えることが分かっている → 最初からAdvanced
- 超えそうで迷う → まずStandardで作る。超過エラーが出てからAdvancedに変えても、上書き1回で済む。分け方を細かくして4KBに収める設計もある
逆方向だけは事情が違い、AdvancedからStandardには戻せません。戻すには削除して作り直します。作り直し自体は大きな手間ではありませんが、この非対称だけは選ぶ時点で知っておくと迷いが減ります。
1. この記事の範囲
対象は、アプリが使う設定値(外部APIのエンドポイント、タイムアウト値、秘匿すべき認証情報など)をParameter Storeに置くケースです。呼び出す側としてECS(タスク定義のsecretsによる環境変数注入)とLambda(Parameters and Secrets Lambda Extension)を扱います。Secrets Managerとの使い分け、ローテーション機能の話は範囲外です。
2. どの単位でまとめるか。3つの選択肢の性質を並べる
まず、呼び出す側を考えない段階での性質の違いです。分け方はどれも「粒度をどこに置くか」の違いなので、粗い順に並べます。
| 観点 | サービス全体で1つ | 読むアプリごとに1つ | 値の種類ごとに分ける |
|---|---|---|---|
| 値の置き場所 | 1箇所 | 複数アプリで同じ値を使うと重複して持つことになる(直し漏れのもと) | 1箇所 |
| 値の更新 | 1回のPutで全アプリに効く。記述ミスも全アプリに波及する | 影響はそのアプリに閉じる | 値ごとに独立。影響範囲が最も狭い |
| IAMでの権限分離 | 全アプリが同じパラメータを読む。分離できない | アプリ単位で分離できる。値単位はできない | 値単位・パス単位で分離できる |
| サイズ上限(Standardの場合) | 全設定の合計で4KB。最も超えやすい | そのアプリの設定合計で4KB | 1値ごとに4KBで最も余裕がある |
| アプリ側の実装 | JSONをパースし、自分に関係ある値を拾う | JSONをパースする | 複数パラメータを取得する |
JSONの中のキー単位でIAM権限を分けることはできないため、権限の粒度はパラメータの分け方がそのまま上限になります。
サイズ上限は実測で境界を確かめました。4,096文字ちょうどの値は登録でき、4,097文字にすると登録自体が次のエラーで失敗します。
$ aws ssm put-parameter --name /myservice/config --type String \
--value "$VALUE_4097" --tier Standard
An error occurred (ValidationException) when calling the PutParameter operation:
Standard tier parameters support a maximum parameter value of 4096 characters.
この表だけでは決まりません。どれにも筋があるからです。決め手は次の3章と4章、呼び出す側の制約にありました。
3. 呼び出す側がECSのとき。環境変数の注入は「起動時の1回だけ」
ECSのタスク定義にはsecretsという書き方があり、Parameter Storeの値をコンテナの環境変数として注入できます。アプリはただ環境変数を読むだけでよく、SDKの実装が不要になる便利な仕組みですが、実測して2つの性質を確認しました。
注入はタスク起動時の1回だけ
パラメータを更新しても、動いているタスクの環境変数は変わりません。さらにその状態でスケールアウトすると、新しく起動したタスクだけが新しい値を持つため、同じサービスの中に古い値のタスクと新しい値のタスクが同居します。実測の流れはこうでした。
| 操作 | 既存タスクの環境変数 | 新規タスクの環境変数 |
|---|---|---|
| v1で起動 | v1 | (なし) |
| パラメータをv2に更新 | v1のまま | (なし) |
| タスクを1つ追加 | v1のまま | v2 |
| サービスを再デプロイ | v2 | v2 |
全タスクを新しい値に揃えるには、サービスの再デプロイ(タスクの入れ替え)が必要です。運用開始後に変えたい値をsecretsで注入する場合は、値の変更にデプロイ操作が伴うことを知っておく必要があります。
JSONの中のキーだけ取り出す書き方は使えない
Secrets Managerでは、valueFromのARNに :キー名:: を付けるとJSONの1項目だけを環境変数にできます。Parameter Storeで同じ書き方を試しました。タスク定義のsecretsに次のように書きます。
"secrets": [
{
"name": "DB_PASS",
"valueFrom": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/myservice/web-ecs/config:db_pass::"
}
]
このタスク定義を登録しようとすると、タスクの起動時ではなく登録の時点で拒否されます。
$ aws ecs register-task-definition --cli-input-json file://taskdef.json
An error occurred (ClientException) when calling the RegisterTaskDefinition operation:
The Systems Manager parameter name specified for secret DB_PASS is invalid.
The parameter name can be up to 2048 characters and include the following
letters and symbols: a-zA-Z0-9_.-,/
注目したいのはエラーの中身です。「この機能はSecrets Manager専用です」とは言ってくれず、コロンがパラメータ名に使えない文字だという名前の書式エラーとして返ってきます。ECSはParameter Storeの指定を丸ごと1つのパラメータ名として扱っていて、キーを取り出すという解釈自体を持っていないようです。事情を知らずにこのエラーを見ると、真因にたどり着くまで遠回りしそうです。
つまりECSでまとめる方式(サービス全体で1つでも、アプリごとに1つでも)を選ぶと、環境変数にはJSON文字列が丸ごと入り、パースはアプリの仕事になります。種類ごとに分ける方式なら環境変数も値ごとに分かれます。ここは種類ごとに分ける方式の分かりやすい利点です。
4. 呼び出す側がLambdaのとき。Extensionはキャッシュが切れれば追いつく
Lambdaには公式のParameters and Secrets Lambda Extensionがあり、関数内からlocalhost経由でパラメータを取得すると、Extensionが結果をキャッシュしてくれます。キャッシュの保持時間は環境変数SSM_PARAMETER_STORE_TTLで変えられます(既定は300秒)。
TTLを30秒にして実測しました。3回とも同じ実行環境(同じログストリーム)です。
| 操作 | パラメータの実値 | Extensionが返した値 |
|---|---|---|
| 更新前に呼び出し | v1 | v1 |
| v2に更新して直後に呼び出し | v2 | v1(キャッシュ) |
| 35秒待って呼び出し | v2 | v2 |
ECSのsecrets注入と並べると、値の更新が反映されるタイミングの考え方がまったく違います。ECSはタスクを入れ替えるまで変わらず、LambdaのExtensionはTTLが切れれば勝手に追いつきます。
同じParameter Storeを使っていても、「値を変えたらいつ反映されるか」は呼び出す側の仕組みで決まります。分け方を考える前に、まず自分のアプリがどの経路で取得するのかを固めるのが先でした。
なおSDKで直接取得する構成は、secretsやExtensionのような仕組みを挟まず、アプリのコードが取りに行きます。起動時に1回だけ読む、リクエストのたびに読む、読んだ値を一定時間持ち回す、のどれにするかをコードで書くので、反映のタイミングは仕組みの制約ではなく実装の選択になります。
5. StandardかAdvancedか。戻れないことだけ知っておく
先に2つの階層の違いを整理します。
| Standard | Advanced | |
|---|---|---|
| サイズ上限 | 4KB | 8KB |
| 料金 | 無料 | 1個あたり月0.05USD(月の途中は時間按分) |
| パラメータポリシー(有効期限など) | 使えない | 使える |
| アカウントあたりの個数上限 | 10,000個 | 100,000個 |
たいていの設定値はどちらにも入るので、ここでは選び直しの自由度を実測しました。
StandardからAdvancedへは、--tier Advanced を付けて上書きするだけで変更できました。逆に、Advancedのパラメータを --tier Standard で上書きしようとすると失敗します。
$ aws ssm put-parameter --name /myservice/config --value "small" \
--tier Standard --overwrite
An error occurred (ValidationException) when calling the PutParameter operation:
This parameter uses the advanced-parameter tier. You can't downgrade a parameter
from the advanced-parameter tier to the standard-parameter tier. If necessary,
you can delete the advanced parameter and recreate it as a standard parameter.
エラー文が親切で、削除して作り直すという代替手順まで案内してくれます。あわせて、階層を指定せずに上書きした場合は今の階層が維持されることも確かめました。一度Advancedにしたパラメータは、日々の値更新を繰り返しても自動でStandardに戻ることはなく、意識して作り直すまで課金が続きます。
Intelligent-Tieringも試した
「Standardで作れば超過は登録エラーで気づける」と書くためには、階層を明示しなかったときの挙動も確かめておく必要がありました。アカウント設定にはIntelligent-Tiering(階層を指定しなかったときにリクエスト内容から自動で階層を選ばせる設定)があり、ユーザーガイドのこのページには、4KBを超える値の登録では自動でAdvancedとして作成されるという趣旨の例が挙げられています。もし本当にそうなら、超過してもエラーは出ず、気づかないままAdvancedが増えることになります。
実測では逆でした。Intelligent-Tieringに設定しても4KB超の登録はStandardと同じValidationExceptionになり、自動でAdvancedにはなりませんでした。同様の報告はGitHubのissue(aws-sdk-net#3157、terraform-provider-aws#18521)にもあります。一方で、パラメータポリシー(Advanced専用機能)を付けて登録すると、こちらは黙ってAdvancedで作られました。昇格の条件によって挙動が揃っていないので、階層は自動に任せず明示的に指定するのが安心だと考えています。
明示的にStandardで作っておけば、サイズ超過は登録エラーとして必ず表面化します。気づけない失敗にならないという点が、Standardから始める安心材料です。細かい話ですが、作成後の階層はGetParameterのレスポンスには含まれず、DescribeParametersで確認します。
6. 料金は「リクエスト回数」ではなく「返ってきたパラメータの数」で数える
分ける方式にすると、パス配下をGetParametersByPathでまとめて取れます。1回のリクエストで10個返ってくるので、一見「取得1回」です。実測でも、10個ちょうどの取得はHTTPリクエスト1回で完結し、CloudTrailにもイベント1件として記録されました。
ただし料金の数え方は別です。料金ページには、1回のGetで10個のパラメータが返れば10回分として数えると明記されています。
if a Get request returns ten parameters, that counts as ten Parameter Store API interactions
Standardパラメータを既定のスループットで使う分には無料なのでこの差は表に出ませんが、Advancedや高スループット設定(1万カウントあたり0.05USD)では、リクエスト回数ではなく返した個数で課金されます。まとめる方式は何個の値を持っていても1パラメータなので、この数え方の影響を受けにくい形です。
SecureStringの場合はKMSの復号も関わります。パラメータは1個ずつ暗号化されるため、分ける方式では取得のたびにパラメータ数分の復号が走る想定です(KMSは月2万リクエストまで無料枠があるので、多くの場合は誤差だと思います)。
実測ではもう1つ面白い違いが見えました。SecureStringをAdvancedで登録すると、そのときだけCloudTrailにGenerateDataKeyイベントが現れます。
$ aws ssm put-parameter --name /myservice/adv-param --type SecureString \
--value "secret" --tier Advanced --key-id <KMSキーID>
このPutに対応して記録されたイベントの抜粋です。
{
"eventSource": "kms.amazonaws.com",
"eventName": "GenerateDataKey",
"requestParameters": {
"encryptionContext": {
"PARAMETER_ARN": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/myservice/adv-param",
"aws-crypto-public-key": "A5RL..."
}
}
}
同じ操作をStandardで行ったとき(11回試行)にはこのイベントは現れません。KMSデベロッパーガイドにあるとおりAdvancedだけがAWS Encryption SDKによるエンベロープ暗号化(データキーを作って値を暗号化し、データキー自体も暗号化する方式)で、その痕跡が aws-crypto-public-key としてCloudTrailの記録からも確認できます。
7. 私の場合
私のケースでは、値ごとにIAMで権限を分ける要件がなく、値の更新はアプリのデプロイと同時にしか起きない想定で、取得はECSのsecrets注入でした。この条件だと、種類ごとに分ける方式の利点(個別更新、個別の権限、環境変数が分かれる)はどれも効いてきません。あわせて、複数のアプリで同じ値を共有する場面が少なく、アプリ単位でまとめても値の重複がほぼ生じない状況でした。そこで読むアプリ単位でまとめる方式にしました。迷った時間のわりに、条件を並べたら答えは軽かったです。
階層は、サイズの見通し次第だと考えています。総量が読めるならStandardで始めて、超過エラーが出てから上げれば足ります。超過が見込まれる、または総量が読めないなら、最初からAdvancedにするのも自然な判断です。超えないように値の持ち方や分け方を調整して4KBに収めるのも、設計の1つだと思います。どれを選ぶにしても、「AdvancedからStandardへは作り直しでしか戻れない」ことを選ぶ時点で知っているかどうかが大事だと考えています。
おわりに
どちらでも動く選択肢で迷ったとき、機能の一覧表を見比べるより、「あとで選び直せるか」と「間違えたときに気づけるか」を先に確かめる方が早く決まる、というのが今回の学びです。Parameter Storeでいえば、分け方は呼び出す側の仕組みが決め、階層は戻れない方向だけ意識する。同じところで手が止まった方の参考になれば幸いです。
参考
- AWS Systems Manager 料金: https://aws.amazon.com/jp/systems-manager/pricing/
- パラメータ階層の管理(ユーザーガイド): https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/parameter-store-advanced-parameters.html
- デフォルトのパラメータ階層の指定(ユーザーガイド): https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/ps-default-tier.html
- ECS 環境変数を通じたSystems Managerパラメータの受け渡し(開発者ガイド): https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/secrets-envvar.html
- Parameter StoreがAWS KMSを使用する方法(KMSデベロッパーガイド): https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/services-parameter-store.html
- Parameters and Secrets Lambda Extension(ユーザーガイド): https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/ps-integration-lambda-extensions.html
- Intelligent-Tieringのサイズ超過時の挙動に関する報告: https://github.com/aws/aws-sdk-net/issues/3157 / https://github.com/hashicorp/terraform-provider-aws/issues/18521