[Tips]CloudFormationで文字列の繰り返しやめたいと思ったのでAWS Systems Manager パラメータストアに保存することを思いついた話

2020.02.28

AWS事業本部の梶原@福岡オフィスです。 出落ち感がすごいですが、CloudFormationの実行時に変数を保持する方法を検討したのですが、ちょっといい方法が浮かばず、AWS Systems Manager パラメータストアのパラメータに保存して対応しました。

共通のJSONをいくつか書く必要があったのですが、ちょっと共通の記述が長くまた修正するたびに複数個所の修正が必要になるのでちょっと困ってました 社内で検討したところ、パラメータに保持する方法や、Servales Framework を使用し、アンカーとエリアスをしようする方法などいくつか候補にあがったのですが結局

AWS Systems Manager パラメータストアのパラメータに保存して対応しました。(結構自分の中ではひらめいた感があるのですが、他にいい案があれば)

利点としては、CloudFormatinoの入力パラメータのように固定文字列である必要がない(実行時に!Sub等で${AWS::AccountId}を埋め込める) CloudFormationの実行時に値がきめれる(パラメータを組み込むことができる)などがあるかと思います。 また、ちょっと工夫すれば、複数のパラメータを使用して文字列を変換することも可能ですので、使い方次第では困ったときに思い出してもらえるといいかと思います。

変数の定義をサポートしてほしいなぁとは思いますが、あくまで宣言型のIaCソリューションの1つになるかと思いますので もっと繰り返したいとか条件分岐をする必要があれば、AWS CDK等での対応も検討ください

実例としては、以下のような感じになります。

Resources:
  IAMRole1:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: !Sub |
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                     省略(10行くらいある) ${AWS::AccountId} 含む
                }
            ]
        }
    ManagedPolicyArns:
        - !Ref "IAMManagedPolicy1"
  IAMRole2:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: !Sub |
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                     省略(10行くらいある) ${AWS::AccountId} 含む
                }
            ]
        }
    ManagedPolicyArns:
        - !Ref "IAMManagedPolicy2"

    IAMRole3:
        ・・・ 同じAssumeRolePolicyDocument がつづく
    IAMRole4:
        ・・・ 同じAssumeRolePolicyDocument がつづく

な感じのながーーーいテンプレートファイルが

Resources:
  SSMParam1:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Value: !Sub |
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                     省略(10行くらいある) ${AWS::AccountId} 含む
                }
            ]
        }
  IAMRole1:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        !GetAtt SSMParam1.Value
      ManagedPolicyArns:
        - !Ref "IAMManagedPolicy1"
  IAMRole2:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        !GetAtt SSMParam1.Value
      ManagedPolicyArns:
        - !Ref "IAMManagedPolicy2"
  IAMRole3:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        !GetAtt SSMParam1.Value
      ManagedPolicyArns:
        - !Ref "IAMManagedPolicy2"
  IAMRole4:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        !GetAtt SSMParam1.Value
      ManagedPolicyArns:
        - !Ref "IAMManagedPolicy2"

AssumeRolePolicyDocument の共通部分のJSON文字列を AWS::SSM::Parameter としてAWS Systems Manager パラメータストアに保存し(名前はSSMParam1にしてますが任意です) !Ref でSSMParam1.Value に書き換えることができます。

デメリットとしては、一部とはいえ、AWS Systems Manager パラメータストアに値が保存されてしまうといったところでしょうか? 気になる方はPoliciesを設定して有効期限を短くして削除してしまうことも可能かと思います(

また、パラメータストアに保存した文字列を更新した際は、依存関係が効いてきますので依存しているリソースも更新されます。

本当にセキュアな情報の場合は、そもそも保存する事がNGなのですが、SecretsManagerに保存したバージョンも書きました
ただ。こちらは依存関係がうまくうごかず(文字列を更新しても依存するリソースが変更検知できずに更新されません)
参考程度にしていただければ幸いです

Resources:
  SecretsManagerSecret1:
    Type: AWS::SecretsManager::Secret
    Properties:
      SecretString: !Sub |
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                     省略(10行くらいある) ${AWS::AccountId} 含む
                }
            ]
        }
  IAMRole1:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        !Sub "{{resolve:secretsmanager:${SecretsManagerSecret1}:SecretString:::}}"
      ManagedPolicyArns:
        - !Ref "IAMManagedPolicy1"
  IAMRole2:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        !Sub "{{resolve:secretsmanager:${SecretsManagerSecret1}:SecretString:::}}"
      ManagedPolicyArns:
        - !Ref "IAMManagedPolicy2"