Lambda(Python)でRDSスナップショットをS3にエクスポートする

#はじめに
RDSのデータを参照する際

・日時のdumpジョブなどを用意していない
・リードレプリカがない
・商用環境ログインには問答無用で承認が必要

などの理由で直接RDSの中身を覗くことが困難な場合がありました。
そんな時、Amazon RDSにはスナップショットをS3にエクスポートし、Athenaで参照することが可能とのことでしたので実際にLambdaを使ってRDSスナップショットのS3エクスポートを試してみました。


#全体像

<img width="600" alt="スクリーンショット.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671034/73b55edc-6eb1-3f96-f17f-f6d9fb6c774a.png">

RDSスナップショットのS3へのエクスポートをLambdaにて実行する。
エクスポートされたデータはKMSにより暗号化されるため、KMSキーも用意する必要がある。

#前提
・RDS及びスナップショットを作成してある。
・エクスポート先のS3バケットを作成してある。


#手順

###1.RDSスナップショットをS3エクスポートする際に使用するポリシー、ロールの作成

####1-1.ポリシー作成

スナップショットエクスポートタスクからS3にアクセスするためのポリシーを以下のjsonで作成する。
(Resourceの`your-s3-bucket`はエクスポート先とするS3バケットを指定)

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject*",
                "s3:GetObject*",
                "s3:DeleteObject*"
            ],
            "Resource": [
                "arn:aws:s3:::your-s3-bucket",
                "arn:aws:s3:::your-s3-bucket/*"
            ]
        }
    ]
}

```

####1-2.ロール作成
ロールを作成し、上記で作成したポリシーをアタッチする。
`信頼関係の編集`から以下のようにアクセスコントロールポリシードキュメントを編集する。
(Serviceが`export.rds.amazonaws.com`になる)


```json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "export.rds.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

###2.KMSキーの作成
エクスポートデータの暗号化に使用するキーをKMSで作成する。

1.マネジメントコンソールからKMSにて`カスタマー管理型のキー`を選択。
2.`キーの作成`にて作成ウィザードを表示する。
3.ステップ 1/5 キーのタイプ：対象
4.ステップ 2/5 エイリアス：任意のキーの名前を入力
5.ステップ 3/5 キー管理者:キーと管理者となるユーザーを選択
6.ステップ 4/5 キーの使用アクセス許可を定義: ここでは特に指定しない
7.ステップ 5/5 完了


###3.Lambda作成

####3-1.ロール、ポリシー作成
Lambda関数作成時に同時に作成することができる`AWSLambdaBasicExecutionRole`のロールを作成。
スナップショットエクスポートに必要となる以下ポリシーを別途作成し上記ロールにアタッチする。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole",
                "rds:StartExportTask"
            ],
            "Resource": "*"
        }
    ]
}
```

####3-2.Lambdaコード作成
```pyhton：lambda_handler.py
import json
import boto3
from datetime import datetime

SOURCE_ARN="YOUR_SOURCE_ARN"
S3_BUCKET_NAME="YOUR_S3_BUCKET_NAME"
IAM_ROLE_ARN="YOUR_IAM_ROLE_ARN"
KMS_KEY_ID="YOUR_KMS_KEY_ID"

client = boto3.client('rds')

def lambda_handler(event, context):
  
    export_task_identifier="mysnapshot" + datetime.now().strftime("%Y%m%d%H%M%S")
    
    response = client.start_export_task(
        ExportTaskIdentifier=export_task_identifier,
        SourceArn=SOURCE_ARN,
        S3BucketName=S3_BUCKET_NAME,
        IamRoleArn=IAM_ROLE_ARN,
        KmsKeyId=KMS_KEY_ID,
        )

```


・コード内の以下変数は各自の環境に合わせて設定する。

|変数名|値|
|:---|:---|
|SOURCE_ARN|エクスポート対象とするRDSスナップショットのARN|
|S3_BUCKET_NAME|出力先となるS3バケット名|
|IAM_ROLE_ARN|手順1で作成したS3エクスポートする際に使用するロールのARN|
|KMS_KEY_ID|作成したKMSキーのARN|

・`ExportTaskIdentifier`については以下制約があるため今回は日時をつけたものにしています。

>エクスポート識別子
エクスポートを識別するための名前を入力します。この名前は、AWS アカウントが現在の AWS リージョンで所有する、すべてのスナップショットエクスポート間で一意である必要があります。

###4.KMSキーのキーユーザーにLambdaロールを追加

手順2で作成したキーのキーユーザーに手順3のLambdaにて使用しているロールを追加する。

<img width="８00" alt="スクリーンショット.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671034/c2513387-f016-726f-bab2-83d4caa05737.png">

この作業を行わないと、Lambda実行時に`An error occurred (KMSKeyNotAccessibleFault)`というエラーが表示される。

本作業後、Lambdaを実行することでRDSスナップショットがS3へエクスポートされる。

#おわりに
スナップショットをS3にエクスポートするだけであればマネジメントコンソールでも同様のことが可能ですが、データソースをシステムスナップショットにし、日時でデータ更新を行いたかったためLambdaでの実行を試しました。

RDSもDynamoDBのようにちょっとした参照であればマネジメントコンソール上で確認できると嬉しいですが、今回のスナップショットを参照にする方法も安全で簡単な方法だと感じました。

# 参考文献

この記事は以下の情報を参考にして執筆しました。

- [Amazon S3 への DB スナップショットデータのエクスポート](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_ExportSnapshot.html)