RDS & RDS Proxy (MySQL)を作るCloudFormationテンプレート チートシート

# はじめに

RDS Proxyを手軽に試すためにテンプレート化した際のメモです。
極力管理コンソールの入力項目に合わせてコメントを入れてあります。漏れてたり間違ってたらごめんなさい。

## 方針

- CloudFormation Nested Stackは使わないこと
    - ルートスタック更新から子スタックの変更セット(差分)確認が難しいため
- 作成や削除が柔軟に行えること
- 環境名は以下とすること、ただし他の環境名も追加可能であること
    - prod(本番)
    - stg(検証)
    - dev(開発)

## 前提

- 最低限必要なIAM ロール、セキュリティグループの作成は同一アカウントの別スタックで既に作成済みでエクスポートされている
- VPCは既に作成済みのリソースがある
- Subnetは既に作成済みのリソースがある
- 作成先のAWSアカウントは環境や用途毎に分かれているが同じアカウントに複数作成することもあるし、異なるアカウントに１つ作成することもある

## テンプレート

テンプレート内では他スタックから以下のエクスポート値を読み込んでいます。
事前に別スタックからエクスポートが必要です。

- { "Fn::ImportValue": !Sub "\${SysName}-${Env}-rds-monitoring-role-arn" } RDSモニタリングIAMロールARN
- { "Fn::ImportValue": !Sub "\${SysName}-${Env}-private-subnet-a-id" } プライベートサブネットID A
- { "Fn::ImportValue": !Sub "\${SysName}-${Env}-private-subnet-c-id" } プライベートサブネットID C
- { "Fn::ImportValue": !Sub "\${SysName}-${Env}-rds-proxy-sg-id" } RDSProxy用セキュリティグループID
- { "Fn::ImportValue": !Sub "\${SysName}-${Env}-rds-secret-arn" } RDS用シークレットARN

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: RDS & RDS Proxy for MySQL Create
Parameters:
  SysName:
    Type: String
    Default: "customapp"
  Env:
    Type: String
    Default: "stg"
    AllowedValues:
      - dev
      - stg
      - prod
  RDSInstanceType:
    Type: String
    Default: "db.t2.small"
    AllowedValues:
      - "db.t2.small"
  DBName:
    Default: appdb
    Description: "database name"
    Type: String
    MinLength: '1'
    MaxLength: '64'
Resources:
  # --------------------------------------------------------------------------------
  # RDS インスタンス
  # --------------------------------------------------------------------------------
  DBInstance:
    Type: "AWS::RDS::DBInstance"
    Properties:
      # --------------------------------------------------------------------------------
      # エンジンのオプション
      # --------------------------------------------------------------------------------
      Engine: MySQL
      EngineVersion: "5.7.30"

      # --------------------------------------------------------------------------------
      # 設定
      # --------------------------------------------------------------------------------
      # DBインスタンス識別子
      DBInstanceIdentifier: !Sub "${SysName}-${Env}-rds"
      # --------------------------------------------------------------------------------
      # 設定 認証情報の設定
      # --------------------------------------------------------------------------------
      # マスターユーザー名
      MasterUsername: !Sub '{{resolve:secretsmanager:${SysName}-${Env}-secret:SecretString:username}}'
      # マスターパスワード
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${SysName}-${Env}-secret:SecretString:password}}'
      # --------------------------------------------------------------------------------
      # DB インスタンスサイズ
      # --------------------------------------------------------------------------------
      # DBインスタンスクラス
      DBInstanceClass: !Ref RDSInstanceType
      # --------------------------------------------------------------------------------
      # ストレージ
      # --------------------------------------------------------------------------------
      # ストレージタイプ 汎用 SSD(gp2), プロビジョンド IOPS(io1)
      StorageType: gp2
      # ストレージ割り当て
      AllocatedStorage: 50

      # --------------------------------------------------------------------------------
      # 可用性と耐久性
      # --------------------------------------------------------------------------------
      MultiAZ: false

      # --------------------------------------------------------------------------------
      # 接続
      # --------------------------------------------------------------------------------
      # サブネットグループ
      DBSubnetGroupName: !Ref DBSubnetGroup
      # パブリックアクセス可能
      PubliclyAccessible: false
      # VPCセキュリティグループ
      VPCSecurityGroups:
        - { "Fn::ImportValue": !Sub "${SysName}-${Env}-rds-sg-id" }
      # アベイラビリティーゾーン
      # AvailabilityZone: ''
      # データベースポート
      # Port: 3306

      # --------------------------------------------------------------------------------
      # データベース認証
      # --------------------------------------------------------------------------------
      # データベース認証オプション
      EnableIAMDatabaseAuthentication: false

      # --------------------------------------------------------------------------------
      # 追加設定 データベースの選択肢
      # --------------------------------------------------------------------------------
      # 最初のデータベース名
      DBName: !Ref DBName
      # DBパラメータグループ
      DBParameterGroupName: !Ref DBParameterGroup

      # --------------------------------------------------------------------------------
      # 追加設定 バックアップ
      # --------------------------------------------------------------------------------
      # バックアップ保持期間
      BackupRetentionPeriod: 7
      # バックアップウィンドウ
      PreferredBackupWindow: "18:00-18:30"
      # スナップショットにタグをコピー
      CopyTagsToSnapshot: true

      # --------------------------------------------------------------------------------
      # 追加設定 暗号化
      # --------------------------------------------------------------------------------
      # 暗号を有効化
      StorageEncrypted: true
      # マスターキー
      # KmsKeyId: ''

      # --------------------------------------------------------------------------------
      # 追加設定 モニタリング
      # --------------------------------------------------------------------------------
      # 詳細度
      MonitoringInterval: "60"
      # モニタリングロール
      MonitoringRoleArn: { "Fn::ImportValue": !Sub "${SysName}-${Env}-rds-monitoring-role-arn" }

      # --------------------------------------------------------------------------------
      # 追加設定 ログのエクスポート
      # --------------------------------------------------------------------------------
      EnableCloudwatchLogsExports:
        - audit
        - error
        - general
        - slowquery

      # --------------------------------------------------------------------------------
      # 追加設定 メンテナンス
      # --------------------------------------------------------------------------------
      # マイナーバージョン自動アップグレードの有効化
      AutoMinorVersionUpgrade: true
      # メンテナンスウィンドウ
      PreferredMaintenanceWindow: "sat:19:00-sat:19:30"
      # --------------------------------------------------------------------------------
      # 追加設定 削除保護
      # --------------------------------------------------------------------------------
      # 削除保護
      DeletionProtection: false
      # --------------------------------------------------------------------------------
      # タグ
      # --------------------------------------------------------------------------------
      Tags:
        - Key: "Name"
          Value: !Sub "${SysName}-${Env}-rds"
        - Key: "Env"
          Value: !Ref Env
    DeletionPolicy: "Delete"

  # --------------------------------------------------------------------------------
  # DBパラメータグループ
  # --------------------------------------------------------------------------------
  DBParameterGroup:
    Type: "AWS::RDS::DBParameterGroup"
    Properties:
      Family: "MySQL5.7"
      Description: !Sub "${SysName}-${Env}-db-param-group"
      Parameters:
        time_zone: "Asia/Tokyo"
        character_set_client: utf8mb4
        character_set_connection: utf8mb4
        character_set_database: utf8mb4
        character_set_results: utf8mb4
        character_set_server: utf8mb4
        collation_connection: utf8mb4_bin
        collation_server: utf8mb4_bin
        skip-character-set-client-handshake: 1
        innodb_file_format: Barracuda
        innodb_large_prefix: 1
        innodb_purge_threads: 4
      Tags:
        - Key: "Name"
          Value: !Sub "${SysName}-${Env}-db-param-group"
        - Key: "Env"
          Value: !Ref Env

  # --------------------------------------------------------------------------------
  # DBサブネットグループ
  # --------------------------------------------------------------------------------
  DBSubnetGroup:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      DBSubnetGroupName: !Sub "${SysName}-${Env}-private-db-subnet-group"
      DBSubnetGroupDescription: "-"
      SubnetIds:
        - { "Fn::ImportValue": !Sub "${SysName}-${Env}-private-subnet-a-id" }
        - { "Fn::ImportValue": !Sub "${SysName}-${Env}-private-subnet-c-id" }
      Tags:
        - Key: "Name"
          Value: !Sub "${SysName}-${Env}-db-subnet-group"
        - Key: "Env"
          Value: !Ref Env
        
  # --------------------------------------------------------------------------------
  # RDS Proxy
  # --------------------------------------------------------------------------------
  RDSProxy:
    Type: AWS::RDS::DBProxy
    Properties:
      # --------------------------------------------------------------------------------
      # プロキシ設定
      # --------------------------------------------------------------------------------
      # プロキシ識別子
      DBProxyName: !Sub "${SysName}-${Env}-rds-proxy"
      # エンジンの互換性
      EngineFamily: MYSQL
      # Transport Layer Security が必要
      RequireTLS: false
      # アイドルクライントの接続タイムアウト
      IdleClientTimeout: 300
      # --------------------------------------------------------------------------------
      # 接続
      # --------------------------------------------------------------------------------
      # Secrets Manager シークレット
      Auth:
        - AuthScheme: SECRETS
          SecretArn: !Ref RDSSecretAttachment
          # IAM 認証
          IAMAuth: DISABLED
      # IAMロール
      RoleArn: !GetAtt RDSProxyRole.Arn
      # サブネット
      VpcSubnetIds:
        - { "Fn::ImportValue": !Sub "${SysName}-${Env}-private-subnet-a-id" }
        - { "Fn::ImportValue": !Sub "${SysName}-${Env}-private-subnet-c-id" }
      # VPCセキュリティグループ
      VpcSecurityGroupIds:
        - { "Fn::ImportValue": !Sub "${SysName}-${Env}-rds-proxy-sg-id" }
  # --------------------------------------------------------------------------------
  # RDS Proxy ターゲットグループ
  # --------------------------------------------------------------------------------
  RDSProxyTargetGroup:
    Type: AWS::RDS::DBProxyTargetGroup
    DependsOn:
      - DBInstance
    Properties:
      # --------------------------------------------------------------------------------
      # ターゲットグループの設定
      # --------------------------------------------------------------------------------
      # ターゲットグループ識別子
      TargetGroupName: default
      # データベース
      DBInstanceIdentifiers:
        - !Ref DBInstance
      # 接続情報
      ConnectionPoolConfigurationInfo:
        # 接続プールの接続最大数
        MaxConnectionsPercent: 25
      # RDS Proxyの識別子
      DBProxyName: !Ref RDSProxy
  # --------------------------------------------------------------------------------
  # RDS Proxy ロール
  # --------------------------------------------------------------------------------
  RDSProxyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SysName}-${Env}-rds-proxy-role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: rds.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: AllowGetSecretValue
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:GetResourcePolicy
                  - secretsmanager:GetSecretValue
                  - secretsmanager:DescribeSecret
                  - secretsmanager:ListSecretVersionIds
                Resource: !Ref RDSSecretAttachment
  RDSSecretAttachment:
    Type: AWS::SecretsManager::SecretTargetAttachment
    Properties:
      SecretId: { "Fn::ImportValue": !Sub "${SysName}-${Env}-rds-secret-arn" }
      TargetId: !Ref DBInstance
      TargetType: AWS::RDS::DBInstance
Outputs:
  DBInstanceEndpoint:
    Value: !GetAtt DBInstance.Endpoint.Address
    Export:
      Name: !Sub "${SysName}-${Env}-rds-endpoint"
  DBName:
    Value: !Ref DBName
    Export:
      Name: !Sub "${SysName}-${Env}-rds-dbname"
  RDSProxyEndpoint:
    Value: !GetAtt RDSProxy.Endpoint
    Export:
      Name: !Sub "${SysName}-${Env}-rds-proxy-endpoint"
```