CloudWatch Events と Systems Manager で EC2の起動/停止をスケジュール化する

# はじめに
開発環境等のインスタンスは休日や夜間は停止しておくことが多いと思います。
CloudWatch Events と Lambda で実装する例も多くみかけますが、
Systems Managerと組み合わせてノンプログラミングで実装する方法もあります。

Systems Manager 単体でも Maintenance Windows を使用して設定することができますが、
これは別の記事でご紹介できればと考えています。

# 設定する
IAM ロールの設定と CloudWatch Events のルール作成が必要です。

## IAMロールを作成する
EC2 の起動停止は SSM Automation の機能を利用します。
そのため CloudWatch Events が SSM を呼び出すための IAM ロールを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/a9f6afe1-2078-03af-5d64-f2ac6a08ce3d.png)


ここでは AWS 管理ポリシーの AmazonSSMAutomationRole をアタッチします。
EC2 起動停止するために必要な権限以外も含まれますので、必要に応じてカスタムポリシーを作成してください。
KMSで暗号化されたEBSボリュームを含むEC2インスタンスを起動する場合は、カスタムポリシーに
kms:CreateGrant を Allow するように設定しないと起動に失敗しますのでご注意ください。
作成後、信頼関係から信頼されたエンティティに events.amazonaws.com を追加します。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "events.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/eab8ecdc-adf5-ff81-d047-9819213f339c.png)

## CloudWatch Events を設定する
### 起動用ルールの作成
イベントソースの設定でスケジュールを選択し、Cron式でイベントスケジュールを設定します。
例えば平日の午前9時に指定したEC2を起動したい場合は、以下のように設定します。

```
0 0 ? * 2-6 *
```
※UTC で設定しますので、日本標準時との時差9時間を考慮する必要があります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/5c2525f4-fb5d-4c1f-1a36-aa299753eb62.png)

ルールのスケジュール式ついては以下ドキュメントにも詳細が記載されています。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html

次にターゲットの追加から SSM Automation 選択し、以下のように設定します。

* Document: **AWS-StartEC2Instance**
* Configure automation parameter(s)
    * InstanceId: **起動したいインスタンスのインスタンスID**
* 既存のロールの使用: **作成した IAM ロールを指定**
 
ロールの作成手順で信頼されたエンティティに events.amazonaws.com を追加していないと、
既存のロールの選択肢に出てきませんので注意してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/0e02fe68-3a66-a093-ed8c-7a4f5603c90a.png)

設定の詳細ボタンからステップ 2:に進み、

* ルール名称(必須)
* 説明(任意)

を入力し、ルールを作成すれば完了です。

### 停止用ルールの作成
起動用ルールと同じ流れですので、画面コピーは割愛します。

ここでは平日の18時に停止を行うと想定し、UTC で以下のように設定します。

```
0 9 ? * 2-6 *
```

ターゲットの追加から SSM Automation 選択し、以下のように設定します。

* Document: **AWS-StopEC2Instance**
* Configure automation parameter(s)
    * InstanceId: **停止したいインスタンスのインスタンスID**
* 既存のロールの使用: **作成した IAM ロールを指定**

設定の詳細ボタンからステップ 2:に進み、

* ルール名称(必須)
* 説明(任意)

を入力し、ルールを作成します。

### 動作確認
設定した時刻に指定したインスタンスが起動および停止することを確認します。
Automation の実行結果については、コンソールでの確認方法は現時点で2通りあり、
EC2コンソールの 自動化 または AWS Systems ManagerコンソールのAutomation から確認することができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/21b2e8c0-7846-4c76-a36b-5cf903c1e5e4.png)

### 2018/5/17 補足追記
InstanceIdに複数インスタンスを同時に指定することができます。
その場合はカンマ区切りではなく、以下のようなリスト型で値を渡します。

```
["i-xxxxxxxxxxxxxxxxx", "i-xxxxxxxxxxxxxxxxx"]
```

単純なカンマ区切りでも設定はできてしまいますが、
Automation 実行時に以下のようなエラーが発生します。

```
Step fails when it is validating and resolving the step inputs. Input InstanceIds String pattern validation fails. Expected regex pattern: (^i-(\w{8}|\w{17})$)|(^op-\w{17}$). Actual value: i-xxxxxxxxxxxxxxxxx, i-xxxxxxxxxxxxxxxxx. Please refer to Automation Service Troubleshooting Guide for more diagnosis details.
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/c5affc49-9619-e2c6-aefe-7f3e996e8e22.png)

## CloudWatch Events で結果を監視する
せっかくなので Automation の実行結果についても CloudWatch Events で設定してみます。
新規にルールを作成し、スケジュールではなく、イベントパターンを選択します。
ここでは Automation の失敗またはタイムアウトを監視するため、以下のように詳細を設定します。

* サービス名: **EC2 Simple Systems Manager (SSM)**
* イベントタイプ: **Automation**
* **Specific detail type(S)**
    * **EC2 Automation Execution Status-change Notification**
* **特定のステータス**
    * **Failed, TimedOut**

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/81cf5aa0-c76b-0818-c78c-accca5e1de14.png)

コンソールで設定できるのは上記項目のみであるため、このままの設定だと今回設定した EC2 起動停止用の
Automation だけでなく、SSM で実行される全ての Automation が監視対象となってしまいます。
条件を更に絞りたい場合は、イベントパターンのプレビューから直接 JSON の編集を行うことができます。

ここでは EC2 の起動停止を行う Automation のみを監視するため、
"detail.Definition" フィールドに Automation ドキュメント名を設定しました。
"resources" フィールドに対象ドキュメントの ARN を指定した場合も同様の結果になります。

```json
{
  "source": [
    "aws.ssm"
  ],
  "detail-type": [
    "EC2 Automation Execution Status-change Notification"
  ],
  "detail": {
    "Definition": [
      "AWS-StartEC2Instance",
      "AWS-StopEC2Instance"
    ],
    "Status": [
      "Failed",
      "TimedOut"
    ]
  }
}
```

その他にどのような項目を指定できるかについては、以下のドキュメントの
Automation 実行ステータス変更の通知例をご参照ください。

**サポートされている各サービスからの CloudWatch イベント イベントの例**
AWS Systems Manager イベント
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/EventTypes.html#ssm_event_types

ターゲットの設定では検知した内容の通知方法を設定します。
ここでは SNS の Topic に登録した Email アドレスに通知するよう設定しました。
必要に応じて入力の設定で通知内容を定義します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/e807d5b4-84b7-3f19-258d-e511ecad250c.png)

設定の詳細ボタンからステップ 2:に進み、

* ルール名称(必須)
* 説明(任意)

を入力し、ルールを作成すれば完了です。
参考になれば幸いです。

## 参考: イベントパターンにおける実行ロールの指定
前述のとおり、ドキュメントに記載されているイベント例に記載されている内容であれば
イベントパターンの手動編集で条件を絞り込むことができます。

Automation 実行ステータス変更の通知例をドキュメントから抜粋すると以下のとおりです。

```json
{
  "version": "0",
  "id": "d290ece9-1088-4383-9df6-cd5b4ac42b99",
  "detail-type": "EC2 Automation Execution Status-change Notification",
  "source": "aws.ssm",
  "account": "123456789012",
  "time": "2016-11-29T19:43:35Z",
  "region": "us-east-1",
  "resources": ["arn:aws:ssm:us-east-1:123456789012:automation-execution/333ba70b-2333-48db-b17e-a5e69c6f4d1c", 
    "arn:aws:ssm:us-east-1:123456789012:automation-definition/runcommand1:1"],
  "detail": {
    "ExecutionId": "333ba70b-2333-48db-b17e-a5e69c6f4d1c",
    "Definition": "runcommand1",
    "DefinitionVersion": 1.0,
    "Status": "Success",
    "StartTime": "Nov 29, 2016 7:43:20 PM",
    "EndTime": "Nov 29, 2016 7:43:26 PM",
    "Time": 5753.0,
    "ExecutedBy": "arn:aws:iam::123456789012:user/userName"
  }
}
```

最後の "detail.ExecutedBy" フィールドは実行されたユーザの ARN になっています。
今回のEC2起動停止では、"スケジュール実行で SSM Automation を実行するルール"を定義しているため、
この箇所はイベントルールに設定した実行ロールの assumed-role ARN が含まれる形になります。
これを指定すれば実行元のロールも監視条件に含めることができます。
ただし、実際の assumed-role ARN は以下の形式になります。

```
"arn:aws:sts::[account-id]:assumed-role/[role-name]/[role-session-name]"
```


ロールセッション名については、イベントルールの実行ロール設定時に AWS側で設定されるようです。
ロールセッション名がルールの実行毎に変わってしまうと、正常に監視できないことになりますが、
現状の挙動を観察する限りでは、実行ロールの変更を行わない限りは(時間によらず)変化しないようです。

ただしドキュメント等に記載されている仕様ではないため、今後予告なく挙動が変わる可能性が大いにあります。
プロダクション環境等での設定はおすすめできませんので参考情報として最後に記載させていただきました。