Aurora Serverless v2 の料金を節約するために、自動停止・開始スケジュールを CloudFormation で構成する

## 何のために？

Amazon Aurora Serverless v2 の、使われない時間の料金を節約するためです。

v1 が終了するため、今後の Aurora Serverless は v2 にするしかありません。
[AWS CLI スクリプトで Aurora Serverless v1 (MySQL) を v2 にアップグレードする - Qiita](https://qiita.com/3244/items/59b52407639ddc00683d)

Serverless v1 はアイドル中のキャパシティーユニットが自動的に 0 になったので、使われない時間が多い開発・検証環境の料金を簡単に節約することができました。
ところが Serverless v2 のキャパシティーユニットは 0.5 までしか下がらないため、停止しないと料金が発生し続けてしまいます。
[Aurora Serverless v2 と Aurora Serverless v1 のスケーリングと可用性の比較 - Amazon Aurora](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html#aurora-serverless.comparison-scaling)
[料金 - Amazon Aurora | AWS](https://aws.amazon.com/jp/rds/aurora/pricing/)

使い終わったら手動で停止する運用にしても、停止してから 7 日後に自動的に起動してしまいます。
[Amazon Aurora DB クラスターの停止と開始 - Amazon Aurora](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/aurora-cluster-stop-start.html#aurora-cluster-start-stop-overview)

## 方針

### スケジュール設定

例えば一定時間おきに停止コマンドを呼び出すようなスケジュールでもいいのですが、今回は週に一度のメンテナンスの間だけ Aurora クラスターを実行する例です。

### 利用する AWS サービス

AWS 公式のサンプルでは Lambda を使ってクラスターを停止しています。
[7 日間を超える期間にわたって Amazon Aurora クラスターを停止する | AWS re:Post](https://repost.aws/ja/knowledge-center/aurora-mysql-stop-cluster-seven-days)

しかし今回はよりシンプルに、Amazon EventBridge に設定したトリガーで Systems Manager Automation を実行する方式にします。以下の記事を参考にさせていただきました。
[SSM AutomationでRDS Clusterの自動停止起動設定（Cloudformation） #AWS - Qiita](https://qiita.com/takaahi/items/0d6b41fb388619461ecc)

## CloudFormation の実装

### パラメータファイル

以下の 3 つのパラメータを JSON ファイルで定義します。

- Aurora の DB クラスター ID
- 開始スケジュール (cron 式)
- 停止スケジュール (cron 式)

```json:sample.json
[
  {
    "ParameterKey": "MyClusterId",
    "ParameterValue": "my-aurora-cluster"
  },
  {
    "ParameterKey": "MyStartSchedule",
    "ParameterValue": "cron(45 17 ? * MON *)"
  },
  {
    "ParameterKey": "MyStopSchedule",
    "ParameterValue": "cron(15 19 ? * MON *)"
  }
]
```

cron 式の書式が Linux の crontab と少し違うので注意が必要です。
[Cron-based schedules - Schedule types on EventBridge Scheduler - EventBridge Scheduler](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html#cron-based)

- 曜日か日のフィールドのどちらかを指定する場合は、他方のフィールドを「?」にする (「*」は不可)
- 曜日フィールドは英文字のみ (数値は不可)
- 日時は UTC なので、日本標準時より 9 時間前を指定する

上のサンプルで、日本時間の毎週火曜日午前 2 時 45 分に開始し、4 時 15 分に停止します。

### テンプレート

この章の以下のコードを連結して CloudFormation テンプレートにします。

#### パラメータ

JSON ファイルで定義した 3 つのパラメータを `Parameters` セクションで宣言します。

```yaml:sample.cfn.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: Aurora DB cluster start/stop schedule

Parameters:
  MyClusterId:
    Type: String
  MyStartSchedule:
    Type: String
  MyStopSchedule:
    Type: String
```

#### リソース 1 (開始と停止のイベントルール)

`Resources` セクションで Aurora クラスター開始用と停止用の EventBridge ルールを定義し、`ScheduleExpression` プロパティにパラメータの cron 式を 指定します。
[AWS::Events::Rule - AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html)

```yaml
Resources:
  MyStartRule:
    Type: AWS::Events::Rule
    Properties:
      Name: my-start-rule
      Description: Start my Aurora cluster
      ScheduleExpression: !Ref MyStartSchedule
      State: ENABLED
      Targets:
        - Arn: !Sub arn:aws:ssm:${AWS::Region}::automation-definition/AWS-StartStopAuroraCluster:$DEFAULT
          Id: my-start-rule-targets
          Input: !Sub '{"Action": ["Start"], "ClusterName": ["${MyClusterId}"]}'
          RoleArn: !GetAtt MyScheduleRole.Arn
      EventBusName: default

  MyStopRule:
    Type: AWS::Events::Rule
    Properties:
      Name: my-stop-rule
      Description: Stop my Aurora cluster
      ScheduleExpression: !Ref MyStopSchedule
      State: ENABLED
      Targets:
        - Arn: !Sub arn:aws:ssm:${AWS::Region}::automation-definition/AWS-StartStopAuroraCluster:$DEFAULT
          Id: my-stop-rule-targets
          Input: !Sub '{"Action": ["Stop"], "ClusterName": ["${MyClusterId}"]}'
          RoleArn: !GetAtt MyScheduleRole.Arn
      EventBusName: default
```

`Targets` プロパティに SSM Automation の AWS-StartStopAuroraCluster ランブックを指定し、入力値の `ClusterName` に DB クラスター ID を指定します。
[AWS-StartStopAuroraCluster - AWS Systems Manager オートメーションランブックリファレンス](https://docs.aws.amazon.com/ja_jp/systems-manager-automation-runbooks/latest/userguide/start-stop-aurora-cluster.html)

`RoleArn` には、下で定義する IAM ロールの ARN を指定します。

#### リソース 2 (IAM ロールとポリシー)

EventBridge ルールから SSM Automation を実行することと、アカウント内の DB クラスターを操作することを許可します。

```yaml
  MyScheduleRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: my-schedule-role
      Path: /
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: events.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole

  MySchedulePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - rds:DescribeDBInstances
              - rds:DescribeDBClusters
              - rds:StartDBCluster
              - rds:StopDBCluster
            Resource: !Sub arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:*
        Version: "2012-10-17"
      PolicyName: my-schedule-policy
      Roles:
        - !Ref MyScheduleRole
```

許可対象の DB クラスターを限定する場合は、以下の行の「*」を変更してください。

```yaml
            Resource: !Sub arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:*
```

[AWS::IAM::Role - AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html)
[AWS::IAM::Policy - AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html)

## デプロイ

AWS CLI をインストール済みの環境なら、以下のコマンドでテンプレートをデプロイすることができます。

```bash
aws cloudformation deploy --stack-name sample --template-file path/to/sample.cfn.yaml \
  --parameter-overrides file://path/to/sample.json \
  --capabilities CAPABILITY_NAMED_IAM
```

[deploy — AWS CLI v2 Command Reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudformation/deploy.html)
[AWS CLIの最新バージョンのインストールまたは更新 - AWS Command Line Interface](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)