【テクニカル・上級編】CloudFormationテンプレートでの動的リファレンス活用術:SSM Parameter StoreとSecrets Managerを組み合わせた安全な機密情報管理 – インフラ構成管理(IaC)活用バイブル

秘匿情報の葬送:CloudFormation動子(Dynamic References)によるゼロ・ハードコーディング設計論

インフラストラクチャ・アズ・コード(IaC)の歴史において、最も罪深いアンチパターンは何か。それは、Gitリポジトリの片隅に静かに眠る、平文のデータベースパスワードやAPIシークレットである。

「ローカル開発用だから」「後でパラメータストアに移すから」――その言い訳が生み出したセキュリティインシデントの数々を、我々SREは幾度となくその手で収束させてきた。CloudFormationのテンプレートファイル(JSON/YAML)に機密情報を直書きする時代は、すでに終わった。

本稿では、AWS Systems Manager(SSM)Parameter StoreおよびAWS Secrets ManagerをCloudFormationの動的リファレンス(Dynamic References)によって完全に統合し、メモリ、API制限、そしてデプロイパイプラインのライフサイクルに至るまで最適化し尽くす、極限のプラクティスを提示する。

—

1. 動的リファレンスの内部アーキテクチャと挙動

多くのエンジニアは、`{{resolve:ssm:name:version}}` という構文を単なる「テンプレート展開時のマクロ」程度に捉えている。しかし、その認識では大規模なエンタープライズ環境で必ず足元をすくわれる。

デプロイメント時のフェッチメカニズム

動的リファレンスを使用した場合、CloudFormationサービスはスタックの作成・更新オペレーションの初期フェーズ(Validation / Plan段階)において、指定されたAPI(`GetParameter` または `GetSecretValue`)をAWS内部のコントロールプレーンから実行する。

ここで重要なのは、機密情報はCloudFormationのスタックテンプレート自体のメタデータ(Stored Template)には永続化されないという点だ。AWSはプレースホルダーとして参照ポインタのみを保持し、リソースプロバイダー(CloudFormationの内部ワーカー)が各リソースを作成・更新する瞬間にのみ、メモリ上で復号化された値を取り扱う。

[ Git / S3 ]
│ (平文を含まないテンプレート)
▼
[ CloudFormation Service ] ──(セキュアAPI)──> [ SSM / Secrets Manager ]
│ │
└──────────────(メモリ上で値を取得してリソースへ適用)──────┘

パフォーマンスとAPIスロットリングの罠

数百個のマイクロサービスが同時にCD(継続的デプロイ)パイプラインを走らせる環境では、動的リファレンスの乱用がAWSのAPIレートリミット(`GetParameter` のデフォルト制限)を容易に撃ち抜く。

  • 対策: スタック内の同じパラメータを何度も参照する場合、同一テンプレート内であれば動的リファレンスは毎回APIコールを発生させる可能性がある(CloudFormationのバージョンやプロバイダーの実装依存)。したがって、頻繁に参照される値は、一度 `Mappings` や `Conditions` ではなく、AWSの内部キャッシュ特性を考慮し、共通のベーススタックでSecrets Managerから引き受けてOutputs経由でクロススタック参照するか、動的リファレンス自体の利用を厳選する必要がある。

—

2. 構文の極意:SSM vs Secrets Manager の適切な使い分け

どちらを使うべきか。答えは明快である。

  • SSM Parameter Store (Standard / Advanced): 非構造化データ、数ヶ月単位でローテーションしない静的設定、コストを抑えたい場合。
  • Secrets Manager: 自動ローテーションが必須のDB認証情報、JSON構造を持つ複数の接続情報、KMSカスタマーマネージドキー(CMK)での厳格な暗号化境界が必要な場合。

正確な動的リファレンス構文

① SSM Parameter Store (SecureString) の参照

バージョン番号を明示的に指定(推奨:予期せぬ値の変動を防ぐ)
DatabasePassword: !Ref “{{resolve:ssm:/prod/database/password:1}}”

最新バージョンを参照(ローテーションを即時反映させたい場合)
ApiKey: !Ref “{{resolve:ssm-secure:/prod/api/key:latest}}”

② Secrets Manager (JSONキー指定) の参照

Secrets Managerに格納されたシークレットがJSON形式である場合、JSONPathを用いて特定のキーのみをピンポイントで抽出できる。これはLambdaの環境変数やRDSのマスターパスワード設定において極めて強力である。

Secrets Managerの構造化JSONから特定のキー(password)を抽出
DBMasterPassword: !Ref “{{resolve:secretsmanager:prod/rds/aurora:SecretString:password}}”

—

3. 実践:セキュアなRDSインスタンス構築テンプレート

上記の知見を統合し、動的リファレンスを用いて完全なゼロ・ハードコーディングを実現したCloudFormationテンプレートの断片を示す。

AWSTemplateFormatVersion: ‘2010-09-09’
Description: ‘Production Grade RDS Instance with Dynamic References for Secrets’

Parameters:
EnvironmentType:
Type: String
Default: ‘prod’
AllowedValues: [‘prod’, ‘staging’]

Resources:
# DBサブネットグループ(省略)

# RDSインスタンス
ProductionDatabase:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: !Sub ‘app-db-${EnvironmentType}’
Engine: postgres
EngineVersion: ‘15.4’
DBInstanceClass: db.m6i.large
AllocatedStorage: 100
StorageType: gp3

# マスターユーザー名もSSMから動的取得
MasterUsername: !Ref “{{resolve:ssm:/prod/rds/master_username:latest}}”

# 【核心】パスワードはSecrets Managerから動的リファレン스で直接流し込む
# テンプレートやスタックパラメータには一切平文が露出しない
MasterUserPassword: !Ref “{{resolve:secretsmanager:prod/rds/credentials:SecretString:password}}”

MultiAZ: true
PubliclyAccessible: false
AutoMinorVersionUpgrade: true
StorageEncrypted: true
KmsKeyId: !Sub “arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/your-cmk-uuid”

Tags:

  • Key: Environment

Value: !Ref EnvironmentType

  • Key: SecurityClassification

Value: Restricted

Outputs:
DatabaseEndpoint:
Description: ‘Endpoint of the production database’
Value: !GetAtt ProductionDatabase.Endpoint.Address
Export:
Name: !Sub ‘${AWS::StackName}-DBEndpoint’

—

4. 運用・パイプライン上の落とし穴と極限ハック

動的リファレンスを導入したパイプラインにおいて、シークレットの値が変更された際のデザインパターンを誤ると、デプロイメント地獄に陥る。

落とし穴1: シークレット更新とCloudFormationの「変更なし」検知

Secrets Manager側のパスワードがローテーションされたとする。しかし、CloudFormationのテンプレート自体(コード)は1文字も変わっていない。
この状態で `aws cloudformation deploy` を実行しても、CloudFormationは「リソースの定義に変更はない」と判断し、スタックの更新を行わない。結果として、古いパスワードを持ったリソースのまま放置されるか、手動でのスタック更新(`–no-execute-changeset` を使わない強制更新など)が必要になる。

解決策:デプロイメントトリガーとしてのパラメータハッシュ

シークレットのバージョンやハッシュ値をパイプラインのビルドフェーズで動的に取得し、テンプレートに環境変数やダミーパラメータとして注入する。

パイプライン実行スクリプト(例: GitHub Actions / AWS CodeBuild)
SECRET_VERSION=$(aws secretsmanager describe-secret –secret-id prod/rds/credentials –query “LastChangedDate” –output text)

aws cloudformation deploy \
–template-file template.yaml \
–stack-name production-db-stack \
–parameter-overrides SecretLastChanged=”${SECRET_VERSION}” \
–capabilities CAPABILITY_IAM

テンプレート側では、この `SecretLastChanged` をダミーのタグやコメント、あるいはリソースのプロパティ(例えばLambdaの環境変数など)に組み込んでおく。これにより、シークレットが更新された瞬間にテンプレートの「差分」が生まれ、CloudFormationが確実に動的リファレンスを再評価してリソースを更新(またはローリングアップデート)する。

—

5. 権限管理(Least Privilege)の極み

動的リファレンスを使用する際、忘れてはならないのがCloudFormationの実行ロール(Execution Role)の権限設計である。

CloudFormationは、ユーザーの代わりにSSMやSecrets Managerにアクセスして値を取得する。したがって、デプロイを実行するIAMロールには、以下の権限が厳密にスコープダウンされて付与されていなければならない。

{
“Version”: “2012-10-17”,
“Statement”: [
{
“Sid”: “AllowDynamicReferenceSSMandSecrets”,
“Effect”: “Allow”,
“Action”: [
“ssm:GetParameter”,
“ssm:GetParameters”,
“secretsmanager:GetSecretValue”
],
“Resource”: [
“arn:aws:ssm:ap-northeast-1:123456789012:parameter/prod/”,
“arn:aws:secret:ap-northeast-1:123456789012:secret:prod/rds/”
]
},
{
“Sid”: “AllowKMSDecryptForSecrets”,
“Effect”: “Allow”,
“Action”: [
“kms:Decrypt”
],
“Resource”: “arn:aws:kms:ap-northeast-1:123456789012:key/your-cmk-uuid”,
“Condition”: {
“StringEquals”: {
“kms:ViaService”: [
“ssm.ap-northeast-1.amazonaws.com”,
“secretsmanager.ap-northeast-1.amazonaws.com”
]
}
}
}
]
}

このKMSの `Condition`(`kms:ViaService`)に注目せよ。この設定を入れることで、たとえそのIAMロールが何らかの脆弱性で悪用されたとしても、取得したKMSキーをSSMやSecrets Manager経由以外(例えば直接APIを叩くなど)で復号化することは不可能になる。セキュリティの多層防御とは、ここまで突き詰めて初めて「設計されたインフラ」と呼べる。

—

結び

インフラストラクチャ・アズ・コードの本質は、単にサーバーの構築を自動化することではない。「人手によるミス」「機密情報の漏洩」「追跡不可能な構成変更」という、システム運用の根源的なリスクをコードの力で完全に封殺することにある。

CloudFormationの動的リファレンスは、そのための最も強力で、最も洗練された武器の一つだ。今すぐ、あなたのGitリポジトリにあるテンプレートからハードコードされたシークレットを消し去り、コントロールプレーンの深淵へと委ねよ。

タイトルとURLをコピーしました