SSMドキュメントからSSMパラメーターストアの値を参照

# やりたかったこと
以前投稿した [SSMドキュメントでCloudFormation Stackを作成してみた](https://qiita.com/nkrk/items/03995b41a024a38bb38e) の続きです。
作成済みのSSMドキュメントを複数アカウントに展開するにあたり、各アカウント用にSSMドキュメントを書き換える手間を減らすために環境固有の情報を外部化することになりました。

[AWSのユーザーガイド](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/automation-variables.html) を見ても分かりづらい部分があったので手順をメモしておきます。

# もともとのSSMドキュメントの内容
一時サーバ用EC2インスタンスのCFn Stackを作成することで、自動で起動するためのSSMドキュメントです。
定期的に一時サーバ起動の処理が必要になるため、SSMドキュメント化することで同じ処理を繰り返し実行しやすくしています。

以下のSSMドキュメントのyamlファイルのうち、①「サービスロールのARNに含まれるアカウントID」②「作成したいCFn Stackの名前」③「CFn Template格納先のS3バケットのURL」の3つをアカウントごとに変える必要がありました。

```yaml
description: Launch temporary server by creating CloudFormation stack
schemaVersion: '0.3'
#assumeRoleではドキュメント実行時に利用するサービスロールのARNを指定。ARNの「account-id」の箇所はドキュメントを作成するAWSアカウントごとに変更する必要がある（①）
assumeRole: 'arn:aws:iam::account-id:role/SSMServiceRole'　
mainSteps:
  - name: CreateStackForTemporaryServer
    action: 'aws:createStack'
    inputs:
#StackNameでは作成したいCFn Stackの名前を指定。アカウントの識別子（Devなど）を含めたいため、ドキュメントを作成するAWSアカウントごとに変更する必要がある（②）
      StackName: TempServerForDev
#TemplateURLでは作成したいCFn Template格納先のS3バケットのURLを指定。格納先バケットはSSMドキュメントを実行するアカウントで作成するため、ドキュメントを作成するAWSアカウントごとに変更する必要がある（③）
      TemplateURL: 'https://bucket-name.s3.ap-northeast-1.amazonaws.com/test/temporary-server.yaml'
      Capabilities:
        - CAPABILITY_IAM
```


# 環境固有の情報の外部化の手順
##1. SSMパラメーターストアの値を参照
②「作成したいCFn Stackの名前」③「CFn Template格納先のS3バケットのURL」の値はSSMパラメーターストアに保管して、SSMドキュメント側から参照できるようにします。

##1-1. SSMパラメーストアに値を登録

②「作成したいCFn Stackの名前」用に、Stringタイプのパラメーター `/tmp-server/stack-name` を作成し、値 `TempServerForDev` を保管します。
③「CFn Template格納先のS3バケットのURL」用にStringタイプのパラメーター `/tmp-server/cfn-template-url` を作成し、値 `https://bucket-name.s3.ap-northeast-1.amazonaws.com/test/temporary-server.yaml` を保管します。

SSMパラメーターストアへの値の登録手順詳細は [CloudFormationを使ってSystems Managerのパラメータストアの値を登録し、取得する](https://qiita.com/okubot55/items/e5d81216d730eb2619cc) を参照ください。

##1-2. SSMドキュメントにパラメーターを追加
SSMドキュメント自体のパラメーターを追加して、許容される値として1-1で追加したSSMのパラメーター名を指定します。

②「作成したいCFn Stackの名前」用に、1-1で作成したパラメーター `/tmp-server/stack-name` を参照するためにSSMドキュメントのパラメーター `StackNameForTempServer` を追加します。
SSMドキュメントをGUIで編集する場合は以下のようなイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/2bf09314-058f-c3cf-bdbe-a579163973d0.png)

「Allowed values」のSSMパラメーターの値は `{{ssm:parameter-name}}` のように指定してあげます。
ドキュメントのパラメーターのデータタイプを「String」にした場合は `['{{ssm:parameter-name}}']` と入力しないと怒られます。
入力後は画像のように ` - '{{ssm:parameter-name}}'` と表示されるようになります。 

③「CFn Template格納先のS3バケットのURL」用にも、1-1で作成したパラメーター `/tmp-server/cfn-template-url` を参照するためにSSMドキュメントのパラメーター `CFnTemplateURL` を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/e0586eaf-8ad8-5ac6-109e-f92ad1e5998c.png)


##1-3. SSMドキュメントの該当箇所にパラメーターを指定
SSMドキュメントの該当箇所に、1-2で追加したSSMドキュメント自体のパラメーター名を指定します。

 `StackNameForTempServer` を「Stack name」に、 `CFnTemplateURL` を「Template URL」に指定します。
以下のようなイメージです。
パラメーター名を `{{` と `}}` で囲うのが注意点です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/a5bd0f56-a8e9-cdfb-3bda-40448fb2fc1f.png)

##2. Automation system variablesを利用
①「サービスロールのARNに含まれるアカウントID」は、SSMドキュメント用に提供されているAutomation system variables利用して外部化します。
SSMドキュメント実行時のIAMユーザーまたはIAMロールのアカウントIDを参照できる変数 `global:ACCOUNT_ID` を使います。

Automation system variablesの一覧は、冒頭にも掲載した [AWSのユーザーガイド](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/automation-variables.html) に載っています。

##2-1. SSMドキュメントにパラメーターを追加
SSMドキュメント自体のパラメーター `AutomationAssumeRole` を追加して、許容される値としてサービスロールのARNを指定します。
1-2の手順とほぼ同じで、以下のようなイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/386c45a7-2323-8cf0-3a3b-be548a1d554e.png)

「Allowed values」で指定するサービスロールのARNは `arn:aws:iam::{{global:ACCOUNT_ID}}:role/SSMServiceRole` のように指定してあげます。
2-2と同じで、ドキュメントのパラメーターのデータタイプを「String」にした場合は `['arn:aws:iam::{{global:ACCOUNT_ID}}:role/SSMServiceRole']` と入力しないと怒られます。 

##2-2. SSMドキュメントの該当箇所にパラメーターを指定
2-1で追加したドキュメントのパラメーター  `AutomationAssumeRole`  をSSMドキュメントの該当箇所「ロールを想定」に指定します。
1-3と同じで、パラメーター名を `{{` と `}}` で囲うのが注意点です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/42354f13-f530-adca-1a1d-878c5e987e6c.png)

これで①「サービスロールのARNに含まれるアカウントID」②「作成したいCFn Stackの名前」③「CFn Template格納先のS3バケットのURL」の3つ全ての外部化が完了です。

#更新後のSSMドキュメントの内容
更新後のSSMドキュメントのyamlは以下のようになります。
このように書き換えておけば、他のアカウントにSSMドキュメントを展開する際もこのyamlをそのままコピーできます。

```yaml
description: Launch temporary server by creating CloudFormation stack
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  StackNameForTempServer:
    type: String
    allowedValues:
      - '{{ssm:/tmp-server/stack-name}}'
    description: ' (Required) CFn Stack name for temporary server.'
  CFnTemplateURL:
    type: String
    allowedValues:
      - '{{ssm:/tmp-server/cfn-template-url}}'
    description: (Required) CFn Template URL for temporary server.
  AutomationAssumeRole:
    type: String
    allowedValues:
      - 'arn:aws:iam::{{global:ACCOUNT_ID}}:role/SSMServiceRole'
    description: (Required) The ARN of the role that allows Automation to perform the actions on your behalf.
mainSteps:
  - name: CreateStackForTemporaryServer
    action: 'aws:createStack'
    inputs:
      StackName: '{{ StackNameForTempServer }}'
      TemplateURL: '{{ CFnTemplateURL }}'
      Capabilities:
        - CAPABILITY_IAM

```

# 後日追記

環境固有の情報をパラメーターとして与える場合、パラメーターの値を固定値としていいのであれば、 `allowedValues` より `default` を使用するのが適していることに気づきました。
`allowedValues` の場合、実行時に毎回パラメーターの値を `allowedValues` のままとするか、上書きするかを明示指定する必要がありますが、`default` は毎回明示指定する必要がないのでちょっと楽です。
ただ、2020年8月時点ではSSMコンソールのドキュメントビルダーでは `default` の指定はできないので、yamlファイルを用意して作成する必要があります。