CodeBuild で Aurora スナップショットから Aurora クラスタを作る

RDS Aurora では、他と異なり「クラスタ」というのがあるため、awscliのコマンドのパラメータや実行順序が面倒なことになってます。しかもAWSマネジメントコンソールでぽちぽちやるのとも挙動が異なるので、いっそう面倒です。

awscliでの実行手順は、

* Aurora クラスタ削除は、クラスタのインスタンスを全部削除の完了を待って、クラスタを削除
* Aurora クラスタ作成は、クラスタ作成して、クラスタのインスタンス作成完了を待つ

わりと時間もかかるので、AWSマネジメントコンソールでちまちまやるのもダルいです。CodeBuildにやらせます。

# buildspec.yml

* variables の変数と値をよしなに設定して使ってください。
* `$SOURCE_DB_CLUSTER_NAME` の RDS Aurora のデイリーバックアップスナップショットの最新を 使って、 RDS クラスタ＆インスタンスをつくります。
* 復元先のクラスタ＆インスタンスが存在してたら、まず削除して、それから復元します。
    * `$RDS_CLUSTER_NAME` は復元 **先** のクラスタ名だぞ。
    * 間違っても復元 **元** のクラスタ名を指定するんじゃないぞ。
* RDSストレージ暗号化してないなら `--kms-key-id` を外してください。

```yaml
version: 0.2

env:
  variables:
    SOURCE_DB_CLUSTER_NAME: hoge-rds-aurora
    RDS_CLUSTER_NAME: maskdb
    DB_INSTANCE_CLASS: db.t3.small
    ENGINE_VERSION: 5.6.mysql_aurora.1.19.2
    OPTION_GROUP_NAME: default:aurora-5-6
    CLUSTER_PARAM_GROUP: maskdb-cluster-param
    DB_PARAM_GROUP: maskdb-db-param
    SUBNET_GROUP_NAME: maskdb-subnet-rds
    SECURITY_GROUP_ID: sg-xxxxxxxxxxxxxxx
    KMS_KEY_ID: xxxxxxxx-xxxxxx-xxxxxxxxx-xxxxxxxxxxxx

phases:
  install:
    commands:
      - env | sort
      - yum install -y -q wget which unzip jq
      - pip install --upgrade --quiet pip
      - pip install --quiet awscli
      - aws --version

  build:
    commands:
      - |
        # Auroraの削除手順は、クラスタのインスタンスを全部削除する、待つ、クラスタを削除
        aws rds delete-db-instance \
            --db-instance-identifier "${RDS_CLUSTER_NAME}" \
            --skip-final-snapshot || true
      - |
        aws rds wait db-instance-deleted \
            --db-instance-identifier "${RDS_CLUSTER_NAME}"
      - | 
        aws rds delete-db-cluster \
            --db-cluster-identifier "${RDS_CLUSTER_NAME}" \
            --skip-final-snapshot || true
      - |
        # Auroraの作成手順は、スナップショットからクラスタを作成、インスタンスを作成、待つ
        # 自動バックアップから最新のスナップショット名を取得
        SNAPSHOT_NAME=$(
            aws rds describe-db-cluster-snapshots \
                --db-cluster-identifier "${SOURCE_DB_CLUSTER_NAME}" \
                --snapshot-type automated \
            | jq -r '.DBClusterSnapshots[].DBClusterSnapshotIdentifier' \
            | sort \
            | tail -n1
        )
        echo ${SNAPSHOT_NAME}

        aws rds restore-db-cluster-from-snapshot \
            --snapshot-identifier ${SNAPSHOT_NAME} \
            --db-cluster-identifier "${RDS_CLUSTER_NAME}" \
            --engine "aurora" \
            --engine-version "${ENGINE_VERSION}" \
            --db-subnet-group-name "${SUBNET_GROUP_NAME}" \
            --vpc-security-group-ids "${SECURITY_GROUP_ID}" \
            --db-cluster-parameter-group-name "${CLUSTER_PARAM_GROUP}" \
            --kms-key-id "${KMS_KEY_ID}" \
            --no-deletion-protection
      - |
        aws rds create-db-instance \
            --db-instance-identifier "${RDS_CLUSTER_NAME}" \
            --db-cluster-identifier "${RDS_CLUSTER_NAME}" \
            --db-instance-class "${DB_INSTANCE_CLASS}" \
            --engine "aurora" \
            --no-publicly-accessible
      - |
        aws rds wait db-instance-available \
            --db-instance-identifier ${RDS_CLUSTER_NAME}
```

# IAM

CodeBuildにつけるIAMロールは、RDS Auroraのクラスタ名＆インスタンス名限定にするのが安全です。扱うモノがモノだけに、てへぺろが通用しないと思いますので。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "rds:AuthorizeDBSecurityGroupIngress",
                "rds:StartDBCluster",
                "rds:RestoreDBClusterFromSnapshot",
                "rds:StartDBInstance",
                "rds:ModifyDBSnapshot",
                "rds:CreateDBSnapshot",
                "rds:RestoreDBInstanceFromDBSnapshot",
                "rds:RebootDBInstance",
                "rds:CreateDBCluster",
                "rds:CreateDBInstance",
                "rds:ModifyDBInstance",
                "rds:ModifyDBCluster",
                "rds:CreateDBClusterSnapshot",
                "rds:ModifyDBClusterSnapshotAttribute",
                "rds:ModifyDBSnapshotAttribute",
                "rds:DeleteDBCluster",
                "rds:DeleteDBInstance"
            ],
            "Resource": [
                "arn:aws:rds:*:*:cluster-snapshot:*",
                "arn:aws:rds:*:*:snapshot:*",
                "arn:aws:rds:*:*:cluster:maskdb",
                "arn:aws:rds:*:*:db:maskdb",
                "arn:aws:rds:*:*:secgrp:*",
                "arn:aws:rds:*:*:subgrp:*",
                "arn:aws:rds:*:*:og:*",
                "arn:aws:rds:*:*:cluster-pg:maskdb",
                "arn:aws:rds:*:*:pg:maskdb"
            ]
        }
    ]
}
```

CodeBuildから awscli を実行するので PassRole も付けておきましょう。

# 実行時間

CodeBuildは最大で 8 時間まで実行できます。8時間以上かかるなら、

* 最後の `aws rds wait db-instance-available` を外す
    * 終わるのを目視なりで確認する必要がある
* CodeBuildでやるのを諦める
    * EC2インスタンスでやるとか

などを検討してください。

# 参考

* https://docs.aws.amazon.com/cli/latest/reference/rds/index.html