CloudFormationのSSM Parameter動的参照でversion指定が不要に! 

# はじめに

　　Cloudformationを使用してコード化していくと、コード量が増えたきたり、あるいはCIのデプロイフローに組み込みたくないコードを分離するなど、スタックを分割するケースが存在します。そしてスタックを分割すると、スタック間での名称やArn等の値の受け渡しを行いたいケースが存在するかと思います。
そういったケースでは`SSM Parameterの動的参照(dynamic reference)`を使用すると、値の受け渡しが可能になりますが、今回はその際の参照方法に変更があり、今まで必須だったversionの指定が不要になったため、そちらについて紹介したいと思います。
　また、あわせて`クロススタック参照(cross-stack reference)`を使用した値の受け渡しと、どのような挙動の違いがあるかについても記載できればと思います。

# SSM Parameterの動的参照とは？
　`SSM`とは、`SystemManager`を指しAWSでインフラストラクチャを表示および制御するために使用できます。
　そして`Parameter`とは、SSMの一機能であるParameterStoreに保存された値のことで、この値はスタック間でやり取りすることが出来ます。ParameterStoreとは設定データ管理と機密管理のための安全な階層型ストレージを提供します。今回は触れませんが、値はプレーンテキストだけでなく、暗号化されたデータとしても保存できるため、パスワードやアクセストークンの管理にも使用可能です。
　そして動的参照をを使用すると、上記`SSM`の`ParameterStore`に格納された値をテンプレートに格納します。

　それでは以下にCloudformationを使用した定義例と参照例を記載します。
定義側のスタックと参照側のスタックは以下の構成になっております。

**定義側スタック:** S3Bucketに関する定義 
**参照側スタック:** GlueのDatabase,Tableに関する定義。 TableのLocationにS3Bucket名を指定しているためそこで参照する

## 定義側

SSM Parameterに関する定義はそれぞれ以下のようになっています。

* (a)Type:`AWS::SSM::Parameter`を指定
* (b)ParameterStore上に保存するキー名
* (c)型を指定. 他に`StringList,SecureString `が指定可能です。
* (d)保存したい値を指定。今回はS3のBucket名を保存しています。

```yaml
Resources:
  S3:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Delete # cfn delete時にS3Bucketを消す
    Properties:
      BucketName: !Sub "${ProjectName}-bucket"
      # ----------- S3に関する定義は省略 -----------

  S3FirehoseBucketNameSSM:
    Type: "AWS::SSM::Parameter"                         # (a)
    Properties:
      Name: !Sub "/${ProjectName}/S3FirehoseBucketName" # (b)
      Type: String                                      # (c)
      Value: !Ref "S3"                                  # (d)
```

上記をaws cliでdeployすると以下のように、キー名と値が保存されます。

![pm_1_store-value.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63356/cf67e126-75de-a2b3-7586-acdcb3bbd89e.png)



## 参照側


以下(a)部分が参照に関する定義です。
ちなみに`!Sub`は`${ProjectName}`参照用のため今回の動的参照には関係ありません。
以前なら`{{resolve:ssm:/${ProjectName}/S3FirehoseBucketName:3}}`のように、versionの指定が必須でしたが、今回のアップデートで不要になりました。
そしてversionを指定しない場合Parameterの最新versionを参照します。

```yml
  
  GlueTable:
    Type: "AWS::Glue::Table"
    Properties:
        # ----------  一部省略 -------------
        StorageDescriptor:
          # ----------  一部省略 -------------
          Location: !Sub
            - "s3://${S3Bucket}/${S3Dir}"
            - S3Bucket: !Sub "{{resolve:ssm:/${ProjectName}/S3FirehoseBucketName}}" # (a)
              S3Dir: !Ref "S3FirehoseDir"
```


### 参照時のルール
参照時のルールは以下のようになっています。
`{{resolve:service-name:reference-key}}`
**service-name:** 
　値が保管および管理されるサービスを指定します。

 * `ssm`: Systems Manager パラメータストアのプレーンテキストパラメータ。
 * `ssm-secure`: Systems Manager パラメータストアの安全な文字列パラメータ。

**reference-key:**
動的参照におけるリファレンスキーです。
以前までは、以下のようにparameter-nameとversionが必要でした。
`{{resolve:ssm:parameter-name:version}}`

今回から以下のようにversionを省略できます。そして省略した場合はParameterStoreに保存された値の最新を参照します。
`{{resolve:ssm:parameter-name}}`

（注) 2021/7/16現在日本語版の公式ドキュメントだと、項目versionについて必須となっていますが不要です。
英語版のドキュメントではRequiredからOptionalに変わっていることを確認できます。

参考: [公式ドキュメント- SSM parameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-ssm)

# 参照値更新時のクロススタック参照との挙動の比較

　少し余談にはなりますが、Cloudformationのスタック間の値のやり取りについて`クロススタック参照`でも可能です。こちらを使用した場合と比較して、参照値更新時の挙動の違いを以下で紹介します。

## クロススタック参照例

先ほどと同じ様にに定義側では「S3Bucketに関する定義」を、参照側では「GlueのDatabase,Tableに関する定義」をしてTableのLocationでS3Bucket名を参照します。

### 定義側

上記に記載した`S3FirehoseBucketName`がスタック上にExportされる際のキー名となります。

```yml
Resources:
  S3:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Delete
    Properties:
      # --------  S3に関する定義は省略 -----------
      BucketName: !Sub "${ProjectName}-bucket"

Outputs:
  S3FirehoseBucketName:
    Value: !Ref "S3"
    Export:
      Name: S3FirehoseBucketName
```

### 参照側

参照側では`!ImportValue`を使用してキー名を指定して参照します。


```yml
  # Glue table
  GlueTable:
    Type: "AWS::Glue::Table"
    Properties:
      # ----------  一部省略 -------------
      StorageDescriptor:
          # ----------  一部省略 -------------
          Location: !Sub
            - "s3://${S3Bucket}/${S3Dir}"
            - S3Bucket: !ImportValue "S3FirehoseBucketName"
              S3Dir: !Ref "S3FirehoseDir"
```

## 参照値更新の比較

以下の様にS3のBucket名を`${ProjectName}-bucket`から`${ProjectName}-bucket-updated`に変更します。

```yml
  S3:
    Type: "AWS::S3::Bucket"
    DeletionPolicy: Delete
    Properties:
      # --------  S3に関する定義は省略 -----------
      BucketName: !Sub "${ProjectName}-bucket-updated"
```

これをaws cliを使用してdeployします。


### クロススタック参照の場合

　定義側(スタックS3)のdeployすると以下のように、`Export S3FirehoseBucketName cannot be updated as it is in use by dummy-cross-ref-project` というエラーが発生しています。
既に参照されている場合そのまま更新することはできません。更新する際には参照側で削除した上で、再度更新する必要があります。

![cross_4_s3-update-log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63356/54a3c815-923d-c197-49d8-b73f69cbe926.png)

### 動的参照の場合

　定義側(スタックS3)のdeployします。すると以下のように、問題なくdeployができます。

![pm_4_s3-update-log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63356/eea73ba0-82f5-ba30-979b-f33859b329a9.png)

SystemManagerのParameterStoreを確認すると、以下のように値が更新されていることが確認できます。

![pm_6_resolve-value-updated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63356/c33168c5-8f42-fdde-722f-739ac661684e.png)


そして参照側を更新すると、、以下のように参照側(Glue)のLocation情報が
最新の値に更新されていることが確認できます。

![pm_7_glue-updated.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63356/3e4d682d-a4f0-fc37-c652-0065a0070da1.png)

# 最後に

いかがでしたでしょうか？
動的な参照はスタックテンプレートに対して最大60個と制限はありますが、変更頻度が高い値をスタック間で受け渡ししたい場合、非常に便利かと思います。
もしよければ使ってみてください。

また、今回のサンプルに使用した定義ファイルや実行コマンドについては以下レポジトリに載せています。

https://github.com/kohei789/aws-ssm-parameter-store-example

# 参考文献

 - [Using dynamic references to specify template values](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-ssm)
 
 - [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html)

 - [Fn::ImportValue](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html)
 - [System Managerとは](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/what-is-systems-manager.html)