DynamoDBテーブルをリストアしてもCDKで管理したい！

## はじめに
バックアップからDynamoDBのテーブルをリストアすると、新規のテーブルとしてオリジナルとは別に作成されます。そのため、リソースをCDKなどのIaCツールで管理している場合では、リストアしたテーブルはIaCツールの管理対象外となってしまいます。今回はリストアしたテーブルもCDK上で管理する機会があったので、手順をまとめてみました。

## 手順
1. バックアップやPITRからテーブルをリストア
1. リストアしたテーブルをCDKに追加
1. ドリフト確認
1. CDKデプロイの実行

## 1.バックアップからテーブルをリストア
今回はDynamoDBテーブルのPITRを用いてテーブルのリストアを行います。

ますは、テーブルのPITRが有効になっているか確認
```
aws dynamodb describe-continuous-backups --table-name original-table
```
結果は以下の通り。
PITRが有効の場合、`PointInTimeRecoveryStatus`が`ENABLED`となっており、`LatestRestorableDateTime`に最新の復元ポイントが表示されます。
```
{
    "ContinuousBackupsDescription": {
        "ContinuousBackupsStatus": "ENABLED",
        "PointInTimeRecoveryDescription": {
            "PointInTimeRecoveryStatus": "ENABLED",
            "EarliestRestorableDateTime": "2023-08-30T02:11:54+09:00",
            "LatestRestorableDateTime": "2023-08-30T02:13:51.802000+09:00"
        }
    }
}
```

以下のコマンドから最新の復元ポイントでテーブルリストアを実行します。
```
aws dynamodb restore-table-to-point-in-time \
    --source-table-name original-table \
    --target-table-name restored-table \
    --use-latest-restorable-time
```

## 2.リストアしたテーブルをCDKに追加

### CDKのソースコード編集
リストアしたテーブルをCDKのソースコード上に追加します。
```python
restored_table = dynamodb.Table(
    self,
    "restored-table",
    table_name="restored-table",
    partition_key=dynamodb.Attribute(
        name="PK",
        type=dynamodb.AttributeType.STRING,
    ),
    sort_key=dynamodb.Attribute(
        name="SK",
        type=dynamodb.AttributeType.STRING,
    ),
    billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
    removal_policy=aws_cdk.RemovalPolicy.DESTROY
)
```
この時点では、DynamoDBのバックアップに含まれない項目についての設定は行いません。バックアップにより作成さたテーブルの設定値とCDKで定義した設定値を同じにするため、後ほど設定します（ドリフト対策）。

:::note info
DynamoDBのバックアップに含まれない設定値
AutoScaringポリシー、タグ、Stream設定、TTL設定、PITR設定、IAMポリシー、Cloudwatchメトリクス・アラーム
:::

### スタック取り込み

ソースコードの加筆を行った後、以下のコマンドを実行してリストアしたテーブル及びコードをスタックに取り込みます。
```
cdk import
```

以下のコマンドを実行してスタックで管理されているリソースを確認します。
```
aws cloudformation list-stack-resources --stack-name 'スタック名'
```
元のテーブルとリストアしたテーブルが共にスタックの管理下にあることが確認できます。
```
    （省略）
    {
        "LogicalResourceId": "originaltableF3B7AD93",
        "PhysicalResourceId": "original-table",
        "ResourceType": "AWS::DynamoDB::Table",
        "LastUpdatedTimestamp": "2023-08-29T17:11:55.035000+00:00",
        "ResourceStatus": "UPDATE_COMPLETE",
        "DriftInformation": {
            "StackResourceDriftStatus": "NOT_CHECKED"
        }
    },
    {
        "LogicalResourceId": "restoredtable3BC64029",
        "PhysicalResourceId": "restored-table",
        "ResourceType": "AWS::DynamoDB::Table",
        "LastUpdatedTimestamp": "2023-08-29T17:55:42.729000+00:00",
        "ResourceStatus": "UPDATE_COMPLETE",
        "DriftInformation": {
            "StackResourceDriftStatus": "NOT_CHECKED"
        }
    }
```

## 3.ドリフト確認

念のためCDKで管理しているスタックでドリフトが発生していないか確認を行います。
```
aws cloudformation detect-stack-drift --stack-name 'スタック名'
```
以下のコマンドを入力して、上記のコマンドで行ったドリフト検知の結果を参照します。
```
aws cloudformation describe-stack-resource-drifts \
--stack-name 'スタック名' \
--stack-resource-drift-status-filters 'MODIFIED'
```
以下のように何も表示されない場合はドリフトが発生していません。
```
{
    "StackResourceDrifts": []
}
```

## CDKデプロイの実行

### CDKの編集
DynamoDBのバックアップに含まれない項目についての設定を追加します。

AutoScaringポリシー、タグ、Stream設定、TTL設定、PITR設定、IAMポリシー、Cloudwatchメトリクス・アラーム
```python
restored_table = dynamodb.Table(
    self,
    "restored-table",
    table_name="restored-table",
    partition_key=dynamodb.Attribute(
        name="PK",
        type=dynamodb.AttributeType.STRING,
    ),
    sort_key=dynamodb.Attribute(
        name="SK",
        type=dynamodb.AttributeType.STRING,
    ),
    billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
    removal_policy=aws_cdk.RemovalPolicy.DESTROY,
    point_in_time_recovery=True,
    time_to_live_attribute="hoge"    
)
```

また、個別に許可ポリシーの設定を行っている場合はその対象リソースの切り替えも行います。

### 4.デプロイ実行
変更内容の確認を行います。
```
cdk diff
```
そしてデプロイを実行
```
cdk deploy
```

これによりリストアしたテーブルもCDKの管理下に置かれました！
IAMポリシー等も変更されるため、リストアしたテーブルに対して他リソースからアクセスすることもできます。

<details><summary>今回使用したCDKコード</summary>

LambdaからDynamoDBへアクセスするリソースを定義するスタックです。


```python
from constructs import Construct
from aws_cdk import (
    Duration,
    Stack,
    aws_dynamodb as dynamodb,
    aws_lambda as lambda_,
    aws_logs as logs,
)
import aws_cdk

class RestoreTestStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # DynamoDB
        original_table = dynamodb.Table(
            self,
            f"original_table",
            table_name=f"original_table",
            partition_key=dynamodb.Attribute(
                name="PK",
                type=dynamodb.AttributeType.STRING,
            ),
            sort_key=dynamodb.Attribute(
                name="SK",
                type=dynamodb.AttributeType.STRING,
            ),
            billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
            point_in_time_recovery=True,
            time_to_live_attribute="hoge",
            removal_policy=aws_cdk.RemovalPolicy.DESTROY
        )
        restored_table = dynamodb.Table(
            self,
            f"restored_table",
            table_name=f"restored_table",
            partition_key=dynamodb.Attribute(
                name="PK",
                type=dynamodb.AttributeType.STRING,
            ),
            sort_key=dynamodb.Attribute(
                name="SK",
                type=dynamodb.AttributeType.STRING,
            ),
            point_in_time_recovery=True,
            time_to_live_attribute="hoge",
            billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
            removal_policy=aws_cdk.RemovalPolicy.DESTROY
        )
        # テーブルの切り替え用の変数
        target_table = restored_table

        # Lambda
        dynamodb_access_function = lambda_.Function(
            self,
            f"AccessDynamodbFunction",
            function_name=f"AccessDynamodbFunction",
            runtime=lambda_.Runtime.PYTHON_3_10,
            handler="lambda_function.lambda_handler",
            code=lambda_.Code.from_asset("lambda_functions/dynamodb_access_function"),
            timeout=Duration.seconds(10),
            log_retention=logs.RetentionDays.ONE_MONTH,
        )
        # 指定したDynamoDBテーブルへの読み書き許可権限をLambda関数に付与
        target_table.grant_read_write_data(dynamodb_access_function)
        

```
</details>

## まとめ
バックアップからリストアDynamoDBテーブルをオリジナルテーブルと置き換えてCDKの管理下に置く方法をまとめました。
完全に自分の備忘録になっています。


## 参考文献

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/PointInTimeRecovery.Tutorial.html

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/detect-drift-stack.html

https://docs.aws.amazon.com/cli/latest/reference/dynamodb/restore-table-from-backup.html