AWS Step Functionsを利用してAWSリソースの自動起動停止を行う


## 概要

本記事ではStep Functionsを利用して、AWSリソースを自動で起動停止する方法について記載します。
主にコスト削減のために、開発環境を夜間停止するなどで利用することを想定しています。

今回は以下のようなことを実施する方法について説明します

* Step Functionsの以下のような起動用と停止用のワークフローを実行する
    * AWS Systems Manager Change Calendarの予定を取得する
    * Change Calendarの状態がOPEN(CLOSE)だった場合はAWSリソースの起動(停止)処理を行う
* 上記のワークフローをEventBridgeを利用して30分ごとに実行することで、カレンダーの予定に従ってAWSリソースの状態を変更する。

## Step Functionsでリソースの起動停止

Step Functionsはワークフローから各AWSサービスのSDKを呼び出し、AWSサービスを操作することができます。

例えば　EC2を停止したいときはStep FunctionsのStateを以下のように定義することで実現できます。

```json
{
  "StartAt": "StopEC2Instance",
  "States": {
      "StopEC2Instance": {
        "Type": "Task",
        "Parameters": {
          "InstanceIds": ["i-xxxxx"]
        },
        "Resource": "arn:aws:states:::aws-sdk:ec2:stopInstances",
        "End": true
      }
  }
}
```

起動したい場合も`arn:aws:states:::aws-sdk:ec2:stopInstances`の部分を`arn:aws:states:::aws-sdk:ec2:startInstances`に変更するだけで同様に実現できます。

## Change CalendarでCalendarの予定を取得し、起動停止を実行するか判断する

AWS Systems Manager Change CalendarはカレンダーにOPENかCLOSEの予定を設定できるサービスです。


![スクリーンショット 2023-12-17 20.14.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526312/dda54d6a-087e-d40d-6079-a9e51360aed6.png)

詳しくは以下のドキュメントを参照してください。
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-change-calendar.html

Change Calendarを利用することでAWSリソースの起動したい時間帯に応じて予定を入れることで、起動停止を制御することができます。

```
aws ssm get-calendar-state --calendar-names sample           
{
    "State": "CLOSED", # カレンダーの予定に応じてOPEN,CLOSEDが取得できる
    "AtTime": "2023-12-01T00:00:00Z",
    "NextTransitionTime": "2023-12-01T00:00:00Z"
}
```

こちらもStep FunctionsのStateで定義することができます。

```json
"GetCalendarState": {
  "Type": "Task",
  "Parameters": {
    "CalendarNames": "sample"
  },
  "Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState",
  "ResultSelector": {
    "State.$": "$.State"
  },
  "Next": "CheckCalendar"
}
```

カレンダーの状態からEC2インスタンスの起動停止を判断したい場合はStep FunctionsのChoiceを利用することで実現することができます。

```json
"CheckCalendar": {
  "Type": "Choice",
  "Choices": [
    {
      "Variable": "$.State",
      "StringEquals": "CLOSED",
      "Next": "StopEC2Instance"
    }
  ],
  "Default": "Succeed"
},
```

ここまでをまとめると以下のStep Functionsのワークフローを作成することができます。

![スクリーンショット 2023-12-17 20.49.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526312/85778cc4-2298-34bc-c3f2-64e33243088a.png)

<details><summary>ワークフロー</summary><div>

```json
{
  "StartAt": "GetCalendarState",
  "States": {
    "GetCalendarState": {
      "Type": "Task",
      "Parameters": {
        "CalendarNames": ["sample"]
      },
      "Resource": "arn:aws:states:::aws-sdk:ssm:getCalendarState",
      "ResultSelector": {
        "State.$": "$.State"
      },
      "Next": "CheckCalendar"
    },
    "CheckCalendar": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.State",
          "StringEquals": "CLOSED",
          "Next": "StopEC2Instance"
        }
      ],
      "Default": "Succeed"
    },
    "StopEC2Instance": {
      "Type": "Task",
      "Parameters": {
        "InstanceIds": ["i-xxx"]
      },
      "Resource": "arn:aws:states:::aws-sdk:ec2:stopInstances",
      "Next": "Succeed"
    },
    "Succeed": {
      "Type": "Succeed"
    }
  }
}

```

</div></details>

## EventBridgeを利用して30分ごとに実行

上記で作成したStep Functionsを30分ごとに実行するようにします。
こちらはEventBridgeのルールで30分ごとにStepFunctionsをターゲットとするルールを作成すればできます。

terraformのコードでは以下のようになります。

```terraform
resource "aws_cloudwatch_event_rule" "stop" {
  name        = "stop-rule"
  description = "stop rule"

  is_enabled          = true
  schedule_expression = "cron(*/30 * * * ? *)"
}

resource "aws_cloudwatch_event_target" "stop" {
  target_id = "stop-rule"
  arn       = "{上記のstepfunctionsのarn}"
  rule      = aws_cloudwatch_event_rule.stop.name
  role_arn  = aws_iam_role.eventbridge.arn
}
```



## まとめ

Step Functionsを利用してAWSリソースをChange Calendarの値に応じで自動で起動停止する方法を説明しました。
上記で説明したものは簡易版で実際に運用する場合は他にも以下のようなことを考慮する必要があります。また書く機会があれば、書きたいと思います。
* AWSリソースの取得方法(describe-instancesで取得する、S3に起動停止したいリソースを列挙しておいてStep Functionsから取得するなど)
* EC2以外のリソース(RDS, AutoScaligGroup, ECS..)
* RDSやEC2を起動するときに「RDS起動→RDS起動完了→EC2起動」など順序制御を行う