AWS CloudFormation でリソースを別のスタックに移行する

とりあえずで作ったテンプレートが肥大化しそうになり、あとから分けたいというときなどに。

# 移行前

DynamoDB に Service 、 Games という2つのテーブルが存在する。
このうちの Games を新しいテンプレートに移行する。

移行前ののテンプレートがこちら

```yaml:source.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: source template

Resources:
  ServiceTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: Service
      AttributeDefinitions:
        -
          AttributeName: key
          AttributeType: S
      KeySchema:
        -
          AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 1
  GamesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: Games
      AttributeDefinitions:
        -
          AttributeName: key
          AttributeType: S
      KeySchema:
        -
          AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 1
```

# 移行する

## 既存のスタックからリソースをスコープ外にする

### スタックからリソースを削除しても物理リソースは保持されるようにする

移行するリソースに `DeletionPolicy: Retain` を追加してデプロイする

```yaml:source.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: source template

Resources:
  ServiceTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: Service
      AttributeDefinitions:
        -
          AttributeName: key
          AttributeType: S
      KeySchema:
        -
          AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 1
  GamesTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: Retain
    Properties:
      TableName: Games
      AttributeDefinitions:
        -
          AttributeName: key
          AttributeType: S
      KeySchema:
        -
          AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 1
```

```sh
aws cloudformation deploy --template-file ./yaml:source.yml --stack-name source-stack
```

### スタックからリソースを削除する

移行するリソースを削除してデプロイする

```yaml:source.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: source template

Resources:
  ServiceTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: Service
      AttributeDefinitions:
        -
          AttributeName: key
          AttributeType: S
      KeySchema:
        -
          AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 1
```

```sh
aws cloudformation deploy --template-file ./yaml:source.yml --stack-name source-stack
```

## 移行先のスタックにリソースをインポートする

### 移行先のスタックのテンプレートを作成する

```yaml:destination.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: destination template

Resources:
  GamesTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: Retain
    Properties:
      TableName: Games
      AttributeDefinitions:
        -
          AttributeName: key
          AttributeType: S
      KeySchema:
        -
          AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 1
```

### インポート用のリソース定義ファイルを作成する

`ResourceIdentifier` はリソースの種別ごとに異なるようだけど、どこを見ればよいかわからなかった…
大抵は ARN （JSONのキーは `Arn` ） で良さそう？？

```json:import.json
[
  {
      "ResourceType":"AWS::DynamoDB::Table",
      "LogicalResourceId":"GamesTable",
      "ResourceIdentifier": {
        "TableName":"Games"
      }
  }
]
```

### インポートする

```sh
aws cloudformation create-change-set \
  --stack-name destination-stack --change-set-name ImportChangeSet \
  --change-set-type IMPORT \
  --resources-to-import file://import.json \
  --template-body file://destination.yml
aws cloudformation execute-change-set --change-set-name ImportChangeSet --stack-name destination-stack
```

# 最後に

意外と手間なので事前にどのリソースをどのスタックに含めるか検討しましょう。