AWS Systems Manager Automationで単一アカウントの複数リージョンEC2へAutomationを実行する

# はじめに

カジキさんは運用でSystems Manager(以下SSM) AutomationでS3上に置いたスクリプトをEC2上で実行しています。これを複数リージョンEC2で同時に処理を実行させるような環境の構築が必要になったため、Automationを使って実装してみました。

# 設計

複数リージョンでのAutomationに関する設定方法は下記の公式ドキュメントにありますが、

[Running automations in multiple AWS Regions and accounts \- AWS Systems Manager](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/running-automations-multiple-accounts-regions.html#setup-management-account-iam-roles)

ちょっとわかりにくいので補足です。

SSMのAutomationは、単一リージョン内での実行の場合は、要求をSubmitするIAMが権限を持っていれば機能します。

しかし、複数アカウントやリージョンに対してAutomationを実行する場合は、

1. 要求をSubmitするIAM権限から、Automationを実行するRoleに対してAssume Roleを実行
2. 1の権限でAutomationの処理を実行する

という形になります。

なので、少しまわりくどいですが、

- Automationを実行するRoleを別途作成
- 実行をSubmitするRoleからAutomationを実行するRoleへAssume Roleをする許可を入れておく

という必要が出てきます。

# 実装

## 前提

- 複数リージョンへのEC2、またスクリプトを配置するS3はデプロイ済みとします。
- また、環境はSSMを利用する通信要件を満たしているものとします。

## EC2 IAM Role

- EC2に対するRoleには、下記のポリシーを設定しておきます。
    - マネージドポリシー`AmazonSSMManagedInstanceCore`を付与
    - 追加で下記ポリシーを設定し、スクリプトを配置するS3からのGETを許可しておきます。

```python
{
"Version": "2012-10-17",
      "Statement": [
          {
              "Effect": "Allow",
              "Action": "s3:ListAllMyBuckets",
              "Resource": "arn:aws:s3:::*"
          },
          {
              "Effect": "Allow",
              "Action": "s3:ListBucket",
              "Resource": [
                "arn:aws:s3:::${bucket_name}"
              ]
          },
          {
              "Effect": "Allow",
              "Action": "s3:GetObject",
              "Resource": "arn:aws:s3:::${bucket_name}/*"
          }
      ]
  }
```

## Automation IAM Role

- 今回は公式で案内されている`AWS-SystemsManager-AutomationExecutionRole`という名前のRoleを作成し、そこへ**自分のIAM UserでAssume Roleする形で実行**するのを前提にします。
    - 作成するロール名は必ずしも上記である必要はありません。しかし、理由がなければ`AWS-SystemsManager-AutomationExecutionRole`という名前でRoleを作っておくのがいいです。例えばAWSコンソールから「複数リージョン」の実行する際のデフォルトロール名に指定されているので何かと便利です。

このIAMのデプロイには、公式のCloudformation設定をそのまま使用するのが簡単です。

[Running automations in multiple AWS Regions and accounts \- AWS Systems Manager](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/running-automations-multiple-accounts-regions.html#setup-management-account-iam-roles)

上記から対象Role名のzipファイルをダウンロードし解凍すると、Cloudformationのjsonになります。(同じページに、AWS Organizations用の酷似した名前のRoleのファイルもあるので、注意です。)

IAMがデプロイされたら、自分のIAM UserをRoleの信頼関係に追加しましょう。

```bash
{
      "Version": "2012-10-17",
      "Statement": [
          {
              "Effect": "Allow",
              "Principal": {
                  "AWS": [
                      "arn:aws:iam::${account_id}:user/${user_name}"
                  ]
              },
              "Action": "sts:AssumeRole"
          },
          {
              "Effect": "Allow",
              "Principal": {
                  "Service": "ssm.amazonaws.com"
              },
              "Action": "sts:AssumeRole"
          }
      ]
  }
```

## Automation Document

今回は下記のような定義を利用します。

- ドキュメント名は、`ExecShellScript`としておきます。

ポイントはステップのinputsのParametersで、S3をSourceとする一連の設定を記載することです。

- `sourceType: S3`とします。
- `path`に指定されているスクリプトは所定のS3に配置しておきます。
- `commandLine`では実際の実行コマンドを指定します。

紛らわしいのですが、pathのS3指定は必ずhttps://〜で表記されている必要があります。s3://で記載すると機能しないようです。https://〜での記載は必須ですが対象バケットは公開バケットである必要はなく、上記EC2のIAM Roleで許可しておけば問題はありません。

```yaml
description: 'The shell script remote execution.'
parameters:
  TargetId:
    type: StringList
    description: "(Required) The ID of the Target: Instance ID."
    maxItems: 1
    minItems: 1
schemaVersion: '0.3'
mainSteps:  
  - name: ExecuteShellScript
    action: 'aws:runCommand'
    timeoutSeconds: 3600
    onFailure: Abort
    inputs:
      DocumentName: AWS-RunRemoteScript
      InstanceIds:
        - "{{ TargetId }}"
      Parameters:
        sourceType: S3
        sourceInfo:
          path: 'https://${bucket_name}.s3.ap-northeast-1.amazonaws.com/ssm_script/test.sh'
        commandLine: 'sh test.sh'
```

## S3

上記のAutomation Documentで指定した場所に、実行させたいスクリプトを配置しておきます。

```bash
s3://${bucket_name}/ssm_script/test.sh
```

スクリプトはこんな感じで良いでしょう。

```bash
#!/bin/sh
touch /root/test.text
```

## EC2への設定

- EC2へRoleを付与しておきます。
- リソースタグでAutomation実行対象を指定するので、EC2にタグを設定しておきます。今回は下記を指定しておきます。
    - `TestExec: true`

# コンソールからの実行

ではコンソールから実行してみましょう。

ここでのポイントとしては、Automation実行のSubmitは、**Automation Documentをデプロイしているリージョンで実行する必要がある**というところです。今回はDocumentを東京リージョンに作成しているので、Submitも東京リージョンから実行します。

まず、SSMの画面からオートメーションの画面に行き、「オートメーションの実行」を押下します。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/c59daf3d-0866-f127-a565-bb149f4875a4.png)

先程作成したドキュメント名を選択し、「次へ」を押下します。

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/830540db-9759-420f-46cf-4a1ece4a1a7b.png)

「実行」画面を入力していきます。

- アカウントと組織単位: アカウント番号
- AWSリージョン: 対象のリージョンをすべて選択
- オートメーション実行ロール名: 作成したAutomation実行ロール名
    - 未入力で`AWS-SystemsManager-AutomationExecutionRole`が指定される

![Untitled 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/9d63eeff-54e2-6c5e-537e-6b77aeda5c25.png)

- 複数のリージョンとアカウントレート制御
    - 今回の場合は「単一アカウントの複数リージョンでの実行」なので、ここにはリージョン間での実行レートを指定します。例えば同時実行数を100%とすれば、実行指定したすべてのリージョンで同時にAutomationが実行されます。

![Untitled 3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/ddd21560-94ca-4b14-3fa3-9290847a61a2.png)

- 「ターゲット」
    - パラメータには「TargetID」を指定(Automation Documentの内容参照)
    - ターゲット: 「タグ」を選択
        - 先程EC2に設定したタグのKeyとValueを入力して「Add」を押下

![Untitled 4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/9dc8a79e-1772-b733-6797-9e19dfbe62ef.png)

- Rate control
    - こちらはリージョン**内の同時実行数の指定です。**
    - 考え方は先程の「複数のリージョンとアカウントレート制御」と同じで、例えば同時実行数を100%とすれば、実行指定したすべてのリージョン内の対象リソースで同時にAutomationが実行されます。

![Untitled 5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/50ec5fb7-5e6f-3e37-ed15-c4575c1dcd43.png)

上記を指定後、「実行」を押します。

## 実行結果

![Untitled 6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2631819/67326b9d-9120-caf3-5c22-50317157f0ce.png)

# Tips: CLIでも実行できます

実行する回数や実行対象のリージョンが増えてくると、コンソールでの指定は結構手間だったりします。下記のように、Cloudshell等で実行をCLIからSubmitすると早いです。

コマンドを作るのは手間ですが1度作成すればラクです。下記に例を記載しておきます。

```bash
aws ssm start-automation-execution \
        --document-name ExecShellScript \
        --target-parameter-name TargetId \
        --targets Key=TestExec,Values=true \
        --target-locations Accounts=123456789012,Regions=ap-southeast-1,ap-southeast-2,ca-central-1,eu-west-1,ap-south-1,me-south-1,sa-east-1,af-south-1,ap-northeast-1,us-east-1,ExecutionRoleName=AWS-SystemsManager-AutomationExecutionRole,TargetLocationMaxConcurrency=100%,TargetLocationMaxErrors=100% \
        --max-concurrency 2 \
        --max-errors 2
```

# おわりに(無関係)

Automationでの実行に関しては、複数のアカウントでの実行に関する実装の情報は多いですが、単純に単一アカウント内で複数リージョンの実行を扱った情報が少なく、やりたいことのために必要な設定と不要な設定を切り分けていくのに多少苦労しました。

概念を掴んでしまえばなあんだという感じではあるんですが、1つのアカウント内での実行でAssume Roleを実行するというのはカジキさんとしてはあまり直感的ではなかったです。AWS側も実装とその概念をドキュメント化するのに苦労したと見えてドキュメントに苦悩が見えますね。

# 参考文献

[Running automations in multiple AWS Regions and accounts \- AWS Systems Manager](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/running-automations-multiple-accounts-regions.html#setup-management-account-iam-roles)