CloudFormationでLambda(Python) + RDS Proxy + Auroraを構築する

# 本記事の内容

- AWSが提供するCloudFormationサービスを利用し、Lambda(Python)、RDS Proxy、Auroraを建てる方法を記載した記事です。
- これまでに以下の記事でAurora関連をCloudFormationで実施していました。その続編です。
　- [検証目的のためにCloudFormationでシングルインスタンスのAuroraを建てようとしたら手こずった話](https://qiita.com/kanamekun/items/4db938d52712b1165c37)
　- [CloudFormationでAuroraとLambdaを構築。LambdaでLOAD DATA FROM S3コマンドを実行しS3のCSVファイルをAuroraにインポートする。](https://qiita.com/kanamekun/items/53da2f73bcc2e5d68dc2)


本記事の手順で以下のような構成のクラウド環境を作成します。
![aws_lambda_rdsproxy_aurora.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2017667/72ff5701-cd42-e7e6-7ed8-b07d33ec1d5e.png)
※AWSの操作画面はサービスリリースと共に更新されていくため、本記事をご覧いただくタイミングによっては、現在の画面と異なる場合があります。ご了承ください。本記事内の画面キャプチャーは2022年9月時点,

RDS Proxyの詳細は他の記事を参照ください。
こちらが私的に面白かったです。
 - [Amazon RDS Proxy が BASE にもたらした期待以上の導入メリット](https://devblog.thebase.in/entry/2022/05/26/180000)
 - [なぜAWS LambdaとRDBMSの相性が悪いかを簡単に説明する](https://www.keisuke69.net/entry/2017/06/21/121501) ※RDS Proxyが必要な理由が良く分かります

ここから実際のテンプレートを説明していきます。

# 実施環境
- Windows 10
- Chrome
- AWS

# 説明の流れ
1. S3にLambdaデプロイ用のzipファイルとデータ投入用のCSVファイルの配置
2. CloudFormationテンプレートの準備
3. CloudFormationスタックの作成
4. Lambadaによるテストデータの投入

## 1. S3にLambdaデプロイ用のzipファイルとデータ投入用のCSVファイルの配置

CloudFormationでのLambdaのデプロイは、事前にデプロイ用ファイルをS3に配置しておき、それをデプロイする方式をとる。
データ投入用のCSVファイルも事前に配置が必要です。

デプロイ用ファイルとCSVファイルの配置の仕方は、以下の記事を参照してご確認ください。

https://qiita.com/kanamekun/items/53da2f73bcc2e5d68dc2#1-s3%E3%81%ABlambda%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E7%94%A8%E3%81%AEzip%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E6%8A%95%E5%85%A5%E7%94%A8%E3%81%AEcsv%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E9%85%8D%E7%BD%AE

今回はDB接続のエンドポイントとしてRDS Proxyを利用しているため、
前回のプログラムから接続部分の変更を行う必要があります。

変更点は以下の箇所です。環境変数とコネクト時のホストの指定が変更になります。

(変更前)
```py 
  DB_WRITER_ENDPOINT_ADDRESS = os.environ["DB_WRITER_ENDPOINT_ADDRESS"]
  conn = pymysql.connect(host=DB_WRITER_ENDPOINT_ADDRESS, user=DB_USER, passwd=DB_PASSWORD, db=DB_NAME, connect_timeout=5)
```

(変更後)
```py 
  DB_PROXY_ENDPOINT_ADDRESS = os.environ["DB_PROXY_ENDPOINT_ADDRESS"]
  conn = pymysql.connect(host=DB_PROXY_ENDPOINT_ADDRESS, user=DB_USER, passwd=DB_PASSWORD, db=DB_NAME, connect_timeout=5)
```

プログラム全体は以下の通り。
```index.py
import boto3
import json
import pymysql.cursors
import os

def lambda_handler(event, context):
  
  #Lambdaの環境変数は、CloudFormationのデプロイ時に自動で設定する
  DB_NAME = os.environ["DB_NAME"]
  DB_USER = os.environ["DB_USER"]
  DB_PASSWORD = os.environ["DB_PASSWORD"]
  DB_PROXY_ENDPOINT_ADDRESS = os.environ["DB_PROXY_ENDPOINT_ADDRESS"]

  #pymysqlを利用してAuroraに接続するため、PyMySQLライブラリーを合わせてデプロイする必要がある
  conn = pymysql.connect(host=DB_PROXY_ENDPOINT_ADDRESS, user=DB_USER, passwd=DB_PASSWORD, db=DB_NAME, connect_timeout=5)
  with conn.cursor() as cur:

    #CreateTableコマンドは、mysqlの通常のコマンドと同じ。
    cur.execute("create table service (system_id varchar(255),name varchar(255),url varchar(2048),PRIMARY KEY (system_id))")

    #S3に配置したS3ファイルをインポートするためには"LOAD DATA FROM S3 FILE"コマンドを利用する。
    # 's3://bucket-name/testData.csv'でインポートするファイルのARNを指定しています。
    # FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 LINES の指定で、カンマ区切り・改行が\n・先頭行は無視を指定しています。
    # (system_id,name)でインポートするカラムを指定しています。
    #cur.execute("LOAD DATA FROM S3 FILE 's3://bucket-name/testData.csv' INTO TABLE service FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 LINES (system_id,name);")

    #こちらはSQLの従来通りのinsert intoコマンド。一つずつデータを入れる場合はこちらのSQLで良い。
    #cur.execute('insert into service (system_id, name) values("1", "test")')
    conn.commit()
    
    #インポートした結果はこちらのselect文で確認する。
    cur.execute("select * from service")
    for row in cur:
      print(row)
  
  return {
    'statusCode': 200
  }

```
## 2. CloudFormationスタックの準備

以下が実際に投入したファイルです。

```yaml
AWSTemplateFormatVersion: 2010-09-09
#####################################################
# Parameters
#####################################################
Parameters:
  DBMasterUserName:
    Description: Please enter the name of the master user on an RDS.
    Type: String
    Default: master

  DBMasterUserPassword:
    Description: Please enter the password of the master user on an RDS. That should be greater or equal to 8 characters.
    Type: String
    Default: testdbpass
    NoEcho: true

  TestDataS3BucketArn:
    Description: Please enter the ARN of the S3 bucket where the files to be imported into the Aurora database are stored.
    Type: String
    Default: arn:aws:s3:::bucket-name

  S3BucketName:
    Description: Please enter the S3 bucket name.
    Type: String

  VpcSubnet:
    Description: AP VPC subnet
    Type: String
    Default: 10.0.0.0/16

#####################################################
# Mappings
#####################################################
Mappings:
  Constant:
    RDS:
      ClusterName: test-aurora-cluster
      DatabaseName: auroradb
      InstanceName: test-aurora-instance1
      InstanceType: db.t3.small
      RDSProxyName: Auroa-DBProxy

#####################################################
# Resource
#####################################################

#####################################################
#  VPC section
#####################################################
Description: Create VPC
Resources:
  TestVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcSubnet
      Tags:
        - Key: Name
          Value: TestVPC

#####################################################
#  Subnet section
#####################################################
  TestPrivateSub1a:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Select [0, !Cidr [!GetAtt TestVPC.CidrBlock, 2, 8]] #10.0.0.0/24
      MapPublicIpOnLaunch: false
      VpcId: !Ref TestVPC
      AvailabilityZone: ap-northeast-1a
      Tags:
        - Key: Name
          Value: TestPrivateSub1a

  TestPrivateSub1c:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Select [1, !Cidr [!GetAtt TestVPC.CidrBlock, 2, 8]] #10.0.1.0/24
      MapPublicIpOnLaunch: false
      VpcId: !Ref TestVPC
      AvailabilityZone: ap-northeast-1c
      Tags:
        - Key: Name
          Value: TestPrivateSub1c

#####################################################
#  SecurityGroup section
#####################################################
  TestSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref "TestVPC"
      GroupDescription: Allow RDS Connection From TCP 3306 Port
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: !GetAtt "TestVPC.CidrBlock"
      Tags:
        - Key: Name
          Value: TestSecurityGroup

  TestSecurityGroupForLambda:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref "TestVPC"
      GroupDescription: Allow MySQL (TCP3306)
      Tags:
        - Key: Name
          Value: TestSecurityGroupForLambda

#####################################################
# RouteTable section
#####################################################
  TestRTPriSub:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref TestVPC
      Tags:
        - Key: Name
          Value: TestRTPriSub

  CfAssocciateRouteTableForPrivateSubnet:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref TestRTPriSub
      SubnetId: !Ref TestPrivateSub1a

#####################################################
#  VPC Endpoint section
#####################################################
  #PrivateSubnetのLambdaがS3にアクセスするためにはVPC S3エンドポイントが必要。Private Subnetのルーティング設定にエンドポイントを追加する。
  VPCS3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      RouteTableIds:
        - !Ref TestRTPriSub
      ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
      VpcId: !Ref TestVPC

#####################################################
#  DBSubnetGroup section
#####################################################
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: RDS subnet group.
      SubnetIds:
        - !Ref "TestPrivateSub1a"
        - !Ref "TestPrivateSub1c"

#####################################################
# Aurora section
#####################################################
  #AuroraがS3にアクセスするために必要なIAMロールの設定
  RDSS3AccessRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: rds.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: AllowS3Access
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:GetObjectVersion
                Resource:
                  - !Sub '${TestDataS3BucketArn}'
                  - !Sub '${TestDataS3BucketArn}/*'

  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-rds-dbparametergroup.html
  DBParameterGroup:
    Type: "AWS::RDS::DBParameterGroup"
    Properties:
      Description: "RDS DB parameter group the Aurora Cluster's instance(s)."
      Family: "aurora-mysql5.7"
      Parameters:
        max_connections: "10"
      Tags: 
        - Key: Name
          Value: TestClusterParamaterGroup

  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbclusterparametergroup.html
  DBClusterParameterGroup:
    Type: "AWS::RDS::DBClusterParameterGroup"
    Properties:
      Description: "RDS DB cluster parameter group for Hub-Amber"
      Family: "aurora-mysql5.7"
      Parameters:
        aws_default_s3_role: !GetAtt RDSS3AccessRole.Arn #LOAD DATA FROM S3コマンドを実行し、S3のファイルを読み込むためのIAM role。超重要。
        character_set_client: "utf8mb4"
        character_set_connection: "utf8mb4"
        character_set_database: "utf8mb4"
        character_set_filesystem: "utf8mb4"
        character_set_results: "utf8mb4"
        character_set_server: "utf8mb4"
        collation_connection: "utf8mb4_bin"
        collation_server: "utf8mb4_bin"
        time_zone: "Asia/Tokyo"
      Tags: 
        - Key: Name
          Value: TestClusterParamaterGroup

  DBCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      AssociatedRoles:
        - RoleArn: !GetAtt RDSS3AccessRole.Arn  #LOAD DATA FROM S3コマンドを実行し、S3のファイルを読み込むためのIAM role。超重要。
      Engine: aurora-mysql
      EngineVersion: 5.7.mysql_aurora.2.10.2
      DatabaseName: !FindInMap [Constant, RDS, DatabaseName]
      DBClusterIdentifier: !FindInMap [Constant, RDS, ClusterName]
      MasterUsername: !Ref "DBMasterUserName"
      MasterUserPassword: !Ref "DBMasterUserPassword"
      DBSubnetGroupName: !Ref "DBSubnetGroup"
      DBClusterParameterGroupName: !Ref "DBClusterParameterGroup"
      VpcSecurityGroupIds:
        - !Ref "TestSecurityGroup"

  DBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: aurora-mysql
      EngineVersion: 5.7.mysql_aurora.2.10.2
      DBInstanceClass: !FindInMap [Constant, RDS, InstanceType]
      DBSubnetGroupName: !Ref "DBSubnetGroup"
      DBParameterGroupName: !Ref "DBParameterGroup"
      DBClusterIdentifier: !Ref "DBCluster"
      AvailabilityZone: ap-northeast-1a

#####################################################
# Secret Manager section
#####################################################
  RDSProxySecrets:
    Type: AWS::SecretsManager::Secret
    Properties: 
      Name: "SystemManager-db-secret"
      Description: "This is a Secrets Manager secret for an RDS DB instance"
      SecretString: !Sub
        - '{
          "username": "${DBMasterUserName}" ,
          "password": "${DBMasterUserPassword}" ,
          "engine":"mysql",
          "host": "${DBCluster.Endpoint.Address}" ,
          "port":"3306" ,
          "dbname": "${data_base_name}",
          "dbInstanceIdentifier": "${DBCluster}"
        }'
        - data_base_name: !FindInMap [Constant, RDS, DatabaseName]
      Tags: 
        - Key: Name
          Value: RDSProxySecrets
    DependsOn: DBCluster


#####################################################
# RDS Proxy section
#####################################################

  RDSProxyRole:
    Type: AWS::IAM::Role
    Properties:
      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:GetSecretValue
                  - secretsmanager:DescribeSecret
                Resource:
                  #- !Ref RDSSecretAttachment
                  - !Ref RDSProxySecrets

  DBProxy:
    Type: AWS::RDS::DBProxy
    Properties:
      Auth: 
        - IAMAuth: DISABLED
          AuthScheme: SECRETS
          SecretArn: !Ref RDSProxySecrets #DBの認証情報をあらかじめシークレットARNを利用する
      DBProxyName: !FindInMap [Constant, RDS, RDSProxyName]
      EngineFamily: MYSQL
      IdleClientTimeout: 120
      RequireTLS: false
      RoleArn: !GetAtt RDSProxyRole.Arn #?
      VpcSecurityGroupIds: 
        - !Ref TestSecurityGroup
      VpcSubnetIds: 
        - !Ref TestPrivateSub1a
        - !Ref TestPrivateSub1c
    DependsOn: DBCluster

  DBProxyTargetGroup:
    Type: AWS::RDS::DBProxyTargetGroup
    Properties:
      DBProxyName: !FindInMap [Constant, RDS, RDSProxyName]
      DBClusterIdentifiers:
        - !FindInMap [Constant, RDS, ClusterName]
      TargetGroupName: default
      ConnectionPoolConfigurationInfo:
        MaxConnectionsPercent: 100
        MaxIdleConnectionsPercent: 50
        ConnectionBorrowTimeout: 120
    DependsOn:
      - DBProxy
      - DBCluster
      - DBInstance

#####################################################
# Lambda section (データ投入用Lambda)
#####################################################
  #Lambda実行権限とAuroraの操作権限を付与したIAMロールの設定
  IAMRoleForLambdaFunction:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
        - arn:aws:iam::aws:policy/AmazonRDSFullAccess
      MaxSessionDuration: 3600
      Path: "/"
      RoleName: "RDSFullAccessLambdaExecRole"

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Environment:
        Variables:
          DB_PORT: '3306'
          DB_NAME: !FindInMap [Constant, RDS, DatabaseName]
          DB_PASSWORD: !Ref "DBMasterUserPassword"
          #DB_WRITER_ENDPOINT_ADDRESS: !GetAtt DBCluster.Endpoint.Address
          DB_PROXY_ENDPOINT_ADDRESS: !GetAtt DBProxy.Endpoint
          DB_USER: !Ref "DBMasterUserName"

      Code:
        S3Bucket: !Ref "S3BucketName"
        S3Key: index.zip
      FunctionName: "TestDataInsertLambda"
      Handler: index.lambda_handler
      Runtime: python3.8
      Role: !GetAtt IAMRoleForLambdaFunction.Arn #Lambda実行権限とAuroraの操作権限を付与したIAM
      Timeout: 10
      VpcConfig:
        SecurityGroupIds:
          - !Ref TestSecurityGroupForLambda
        SubnetIds:
          - !Ref TestPrivateSub1a
```

前回の[記事](https://qiita.com/kanamekun/items/53da2f73bcc2e5d68dc2)との差異部分のみ説明します。

RDS proxyをCloudFormationで作成するためには以下が必要になります。

- 必要な設定
	- RDS ProxyがDBに接続するときに利用する認証情報が格納されたシークレット（AWS::SecretsManager::Secret）
	- RDS Proxyがシークレットを参照するためのIAMロール(AWS::IAM::Role)
	- RDS Proxyの本体。シークレット情報、IAMロール、サブネット、セキュリティグループ等を設定（AWS::RDS::DBProxy）
	- RDS Proxyの接続先情報（AWS::RDS::DBProxyTargetGroup）

以下が今回の差分でメインとなる部分。

```yaml
#####################################################
# Secret Manager section
#####################################################
  RDSProxySecrets:
    Type: AWS::SecretsManager::Secret
    Properties: 
      Name: "SystemManager-db-secret"
      Description: "This is a Secrets Manager secret for an RDS DB instance"
      SecretString: !Sub
        - '{
          "username": "${DBMasterUserName}" ,
          "password": "${DBMasterUserPassword}" ,
          "engine":"mysql",
          "host": "${DBCluster.Endpoint.Address}" ,
          "port":"3306" ,
          "dbname": "${data_base_name}",
          "dbInstanceIdentifier": "${DBCluster}"
        }'
        - data_base_name: !FindInMap [Constant, RDS, DatabaseName]
      Tags: 
        - Key: Name
          Value: RDSProxySecrets
    DependsOn: DBCluster


#####################################################
# RDS Proxy section
#####################################################

  RDSProxyRole:
    Type: AWS::IAM::Role
    Properties:
      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:GetSecretValue
                  - secretsmanager:DescribeSecret
                Resource:
                  - !Ref RDSProxySecrets

  DBProxy:
    Type: AWS::RDS::DBProxy
    Properties:
      Auth: 
        - IAMAuth: DISABLED
          AuthScheme: SECRETS
          SecretArn: !Ref RDSProxySecrets #DBの認証情報をあらかじめシークレットARNを利用する
      DBProxyName: !FindInMap [Constant, RDS, RDSProxyName]
      EngineFamily: MYSQL
      IdleClientTimeout: 120
      RequireTLS: false
      RoleArn: !GetAtt RDSProxyRole.Arn #?
      VpcSecurityGroupIds: 
        - !Ref TestSecurityGroup
      VpcSubnetIds: 
        - !Ref TestPrivateSub1a
        - !Ref TestPrivateSub1c
    DependsOn: DBCluster

  DBProxyTargetGroup:
    Type: AWS::RDS::DBProxyTargetGroup
    Properties:
      DBProxyName: !FindInMap [Constant, RDS, RDSProxyName]
      DBClusterIdentifiers:
        - !FindInMap [Constant, RDS, ClusterName]
      TargetGroupName: default
      ConnectionPoolConfigurationInfo:
        MaxConnectionsPercent: 100
        MaxIdleConnectionsPercent: 50
        ConnectionBorrowTimeout: 120
    DependsOn:
      - DBProxy
      - DBCluster
      - DBInstance
```

## 3. CloudFormationスタックの作成

AWSコンソールでのCloudFormationテンプレートの投入方法は、前回の記事通りです。
こちらを参考に進めてください。

https://qiita.com/kanamekun/items/53da2f73bcc2e5d68dc2#3-cloudformation%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%81%AE%E4%BD%9C%E6%88%90

## 4. Lambadaによるテストデータの投入

Lambdaによるテストデータについても前回の記事通りです。
こちらを参考に進めてください。

https://qiita.com/kanamekun/items/53da2f73bcc2e5d68dc2#4-lambada%E3%81%AB%E3%82%88%E3%82%8B%E3%83%86%E3%82%B9%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E6%8A%95%E5%85%A5

テストデータの投入は以上ですので、必要に応じてPythonプログラムを書き換えて利用できます。

本記事は以上です。
クラウド環境の作成の手助けになれば嬉しいです。

# 参考文献
本記事の作成に当たり、以下の情報も参考にさせて頂きました。ありがとうございました。
- [【AWS】実例で学ぶCloudFormation～RDS Aurora編～](https://qiita.com/tmiki/items/19cc4fc71949eaafbc3a)
- [CloudFormation の Fn::Cidr 組み込み関数の使い方のメモ。](https://ebc-2in2crc.hatenablog.jp/entry/2020/03/21/201028)
- [【AWS初学者向け・図解】CloudFormationの組み込み関数を現役エンジニアがわかりやすく解説②](https://o2mamiblog.com/aws-cloudformation-intrinsic-function-beginner-2/#toc2)
- [【CloudFormation】Lambdaの Role を定義する際は !GetAtt を使って明示的に指定する](https://www.yokoyan.net/entry/2019/01/22/181500)
- [AWS LambdaをVPC設定したときに「The provided execution role does not have permissions to call CreateNetworkInterface on EC2」](https://www.yamamanx.com/aws-lambda-vpc-error/)
- [CloudFormationでLambdaを作成する3パータン(S3/インライン/コンテナ)](https://awstut.com/2022/02/02/3-patterns-for-creating-lambda-with-cloudformation/)
- [Amazon RDS Proxy が BASE にもたらした期待以上の導入メリット](https://devblog.thebase.in/entry/2022/05/26/180000)
- [VPC内のLambdaからRDS Proxy経由でRDSに接続する](https://ichi-station.com/rds-proxy-cloudformation-aws-cli/)
- [【SAM】Lambda（Python）でRDS Proxyを使ってみる【user/pass, IAM認証】](https://qiita.com/akkino_D-En/items/7f39dac2969e120ce695)
- [なぜAWS LambdaとRDBMSの相性が悪いかを簡単に説明する](https://www.keisuke69.net/entry/2017/06/21/121501) ※RDS Proxyが必要な理由が良く分かります
- [【CloudFormation】AWS Secrets Managerを使いRDS Proxy経由でRDSに接続する方法](https://qiita.com/horitomoyuki/items/22aab25efcbc6d25655c)
- [!FindInMapを!Subの中で使う | CloudFormation](https://www.bioerrorlog.work/entry/cfn-sub-fimdinmap)
- [RDS（postgres）のデータをS3にexportする〜private network編〜](https://zenn.dev/carotene4035/articles/1e59471d6c7785)
- [VPCエンドポイントを使ってプライベートサブネットからS3へアクセスする。](https://qiita.com/kooohei/items/794a7d6e041f43f98f5b)
- [AssociatedRolesを使ってCFnでS3とデータをロード＆アンロードできるAurora MySQL5.7を構築する](https://dev.classmethod.jp/articles/cfn-aurora-mysql57-integrate-with-s3/)
- [Amazon S3 バケットのテキストファイルから Amazon Aurora MySQL DB クラスターへのデータのロード](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Integrating.LoadFromS3.html)
- [Amazon Aurora MySQLとS3間でデータをロード&アンロードする](https://dev.classmethod.jp/articles/aurora-mysql-integrate-with-s3/)