Amazon RDS は一時停止した場合、最大7日間までしか停止できない
しかし停止させたままにしたい状況が発生したため調査を行なった
その際のまとめを備忘録として残しておく

解決案

実際の手順

Lambda 関数を使用する

  1. IAM > ポリシー を選択

  2. 以下でポリシーを作成

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "rds:StartDBCluster",
                    "rds:StopDBCluster",
                    "rds:ListTagsForResource",
                    "rds:DescribeDBInstances",
                    "rds:StopDBInstance",
                    "rds:DescribeDBClusters",
                    "rds:StartDBInstance"
                ],
                "Resource": "*"
            }
        ]
    }
    
  3. IAM > ロール を選択

  4. 以下でロールを作成

    • 信頼できるエンティティタイプを選択: AWS サービス
    • またはサービスを選択してユースケースを表示: Lambda
    • ポリシー: 手順 2 で作成したポリシー
  5. RDS > データベース > «該当の DB インスタンス» を選択

  6. 以下の様にタグを設定

    • autostart: yes
    • autostop: yes
  7. Lambda > 関数 を選択

  8. 以下の設定で関数を作成

    • オプション: 一から作成
    • 関数名: 任意
    • ランタイム: Python 3.*
    • アーキテクチャ: x86_64
    • デフォルトの実行ロールの変更: 手順 4 で作成したロール
  9. コード > [コードソース]エディタでサンプルコードを削除して次のコードを入力

    import boto3
    rds = boto3.client('rds')
    
    def lambda_handler(event, context):
    
        #Start DB Instances
        dbs = rds.describe_db_instances()
        for db in dbs['DBInstances']:
            #Check if DB instance stopped. Start it if eligible.
            if (db['DBInstanceStatus'] == 'stopped'):
                try:
                    GetTags=rds.list_tags_for_resource(ResourceName=db['DBInstanceArn'])['TagList']
                    for tags in GetTags:
                    #if tag "autostart=yes" is set for instance, start it
                        if(tags['Key'] == 'autostart' and tags['Value'] == 'yes'):
                            result = rds.start_db_instance(DBInstanceIdentifier=db['DBInstanceIdentifier'])
                            print ("Starting instance: {0}.".format(db['DBInstanceIdentifier']))
                except Exception as e:
                    print ("Cannot start instance {0}.".format(db['DBInstanceIdentifier']))
                    print(e)
    
    
    if __name__ == "__main__":
        lambda_handler(None, None)
    
  10. デプロイする

  11. 関数の概要 > トリガーを追加 を選択

  12. EventBridge (CloudWatch Events) を選択し、[新しいルールを作成] を作成

    • ルール名: 任意
    • スケジュール式: RDS メンテナンス時間の 30 分前等を指定(cron 式)
      • 例. cron(30 21 ? * SUN *)
  13. 手順 7 〜 12 を行い RDS 自動停止用の関数を作成

    import boto3
    rds = boto3.client('rds')
    
    def lambda_handler(event, context):
    
        #Stop DB instances
        dbs = rds.describe_db_instances()
        for db in dbs['DBInstances']:
            #Check if DB instance is not already stopped
            if (db['DBInstanceStatus'] == 'available'):
                try:
                    GetTags=rds.list_tags_for_resource(ResourceName=db['DBInstanceArn'])['TagList']
                    for tags in GetTags:
                    #if tag "autostop=yes" is set for instance, stop it
                        if(tags['Key'] == 'autostop' and tags['Value'] == 'yes'):
                            result = rds.stop_db_instance(DBInstanceIdentifier=db['DBInstanceIdentifier'])
                            print ("Stopping instance: {0}.".format(db['DBInstanceIdentifier']))
                except Exception as e:
                    print ("Cannot stop instance {0}.".format(db['DBInstanceIdentifier']))
                    print(e)
    
    if __name__ == "__main__":
        lambda_handler(None, None)
    

EventBridge を使用する

  1. 以下の CloudFormation テンプレートを使用して stack を作成

    ---
    AWSTemplateFormatVersion: '2010-09-09'
    Description:  EventBridge Scheduler to stop RDS instance
    
    Parameters:
      InstanceId:
        Type: String
      ScheduleStopTime:
        Type: String
        Default: "cron(0 20 * * ? *)"
      ScheduleTimezone:
        Type: String
        Default: Japan
    
    Resources:
      # EventBridgeScheduler???
      ScheduleRDSStop:
        Type: AWS::Scheduler::Schedule
        Properties:
          Name: !Sub 'RDS-Stop-${InstanceId}'
          Description: Stop RDS Instance
          ScheduleExpression: !Ref ScheduleStopTime 
          ScheduleExpressionTimezone: !Ref ScheduleTimezone
          FlexibleTimeWindow:
            Mode: "OFF"
          State: ENABLED
          Target:
            Arn: arn:aws:scheduler:::aws-sdk:rds:stopDBInstance
            Input: !Sub |-
              {
                "DbInstanceIdentifier": "${InstanceId}"
              }
            RoleArn:
              Fn::GetAtt:
              - SchedulerRDSStopRole
              - Arn
      # EventBridgeScheduler?????IAM??????     
      SchedulerRDSStopRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Principal:
                Service:
                - scheduler.amazonaws.com
              Action:
              - sts:AssumeRole
          Path: "/"
          Policies:
            - PolicyName: rdsstop
              PolicyDocument:
                Version: "2012-10-17"
                Statement:
                  - Effect: Allow
                    Action:
                      - rds:StopDBInstance
                    Resource:
                      - "*"
    
  2. 以下のパラメータを指定

    • 対象のインスタンス(DB 識別子)
      • 例. sample-db
    • 停止のスケジュール(cron 式)
      • 例. cron(0 20 * * ? *)
    • タイムゾーン
      • 例. japan
  3. 作成されたリソースを確認

    3-1. Amazon EventBridge > スケジュール > «対象のスケジュール»