cdk migrateやってみた

# はじめに
## cdk migrateとは

2023年10月7日に[AWS CDK v2.100.0](http://https//github.com/aws/aws-cdk/releases/tag/v2.100.0)がリリースされました。その中より、cdk migrateについて書かせていただきます。

## 機能を簡単に

現時点でデプロイされているCloudFormation（以下、CFn）、ローカルのCFnファイルからCDKのソースを作成することが出来る機能です。

# やってみる

## 下準備

まずはCFnをAWS上にデプロイします。構成はVPCの中に1つだけサブネットがあるシンプルなものです。
リソースのタグとCIDRをパラメータとして渡すテンプレートになっています。


<details>
<summary>テンプレート</summary>
<div>

```yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  PJid:
    Type: String
    Default: "test"

  VPCCIDR:
    Type: String
    Default: "10.0.0.0/16"

  PublicSubnetCIDR:
    Type: String
    Default: "10.0.10.0/24"

Resources: 
# VPC
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${PJid}-vpc"

# InternetGateway
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${PJid}-igw"

# IGW Attach
  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 

# Public Subnet
  PublicSubnet: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnetCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJid}-public-subnet-a"

# Public RouteTable
  PublicRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJid}-public-route-a"

# PublicRoute
  PublicRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway
 
# PublicRouteTable Associate Subnet
  PublicSubnetRouteTablessociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnet 
      RouteTableId: !Ref PublicRouteTable
```


</div></details>



## コマンド実行

デプロイが完了したら早速コマンドを実行してみます。

```bash
# 念のためバージョンを確認
% cdk --version
2.100.0 (build e1b5c77)

# 実行していくコマンド
cdk migrate --stack-name {ターゲットのスタック名} --language {CDKの言語} --from-stack
```

コマンド実行後に以下のような表示があれば成功です。

```bash
# 実際の実行結果
% cdk migrate --stack-name cdk-migrate-test --language typescript --from-stack

This is an experimental feature. We make no guarantees about the outcome or stability of the functionality.
 ⏳  Generating CDK app for cdk-migrate-test...
Applying project template app for typescript
Initializing a new git repository...
Executing npm install...
✅ All done!
```

成功すると、コマンドを実行したディレクトリ配下にCDKのリソースが --stack-name で指定したスタック名で作成されます。

```bash
# 今回の実行結果

% tree ./cdk-migrate-test

.
├── README.md
├── bin
│   └── cdk-migrate-test.ts
├── cdk.json
├── jest.config.js
├── lib
│   └── cdk-migrate-test-stack.ts
├── node_modules
│   ├── @ampproject
│   │   └── remapping
│   │       ├── LICENSE
│   │       ├── README.md

~~ 以下略 ~~

```

## CDKのソースを確認してみる

cdk-migrate-test-stack.tsの中身を見てみると以下のようにコードが生成されていました。

```typescript
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

export interface CdkMigrateTestStackProps extends cdk.StackProps {
  /**
   * @default 'test'
   */
  readonly pJid?: string;
  /**
   * @default '10.0.0.0/16'
   */
  readonly vpccidr?: string;
  /**
   * @default '10.0.10.0/24'
   */
  readonly publicSubnetCidr?: string;
}

export class CdkMigrateTestStack extends cdk.Stack {
  public constructor(scope: cdk.App, id: string, props: CdkMigrateTestStackProps = {}) {
    super(scope, id, props);

    // Applying default props
    props = {
      ...props,
      pJid: props.pJid ?? 'test',
      vpccidr: props.vpccidr ?? '10.0.0.0/16',
      publicSubnetCidr: props.publicSubnetCidr ?? '10.0.10.0/24',
    };

    // Resources
    const internetGateway = new ec2.CfnInternetGateway(this, 'InternetGateway', {
      tags: [
        {
          key: 'Name',
          value: `${props.pJid!}-igw`,
        },
      ],
    });

    const vpc = new ec2.CfnVPC(this, 'VPC', {
      cidrBlock: props.vpccidr!,
      enableDnsSupport: true,
      enableDnsHostnames: true,
      instanceTenancy: 'default',
      tags: [
        {
          key: 'Name',
          value: `${props.pJid!}-vpc`,
        },
      ],
    });

    if (internetGateway == null) { throw new Error(`A combination of conditions caused 'internetGateway' to be undefined. Fixit.`); }
    if (vpc == null) { throw new Error(`A combination of conditions caused 'vpc' to be undefined. Fixit.`); }
    const internetGatewayAttachment = new ec2.CfnVPCGatewayAttachment(this, 'InternetGatewayAttachment', {
      internetGatewayId: internetGateway.ref,
      vpcId: vpc.ref,
    });

    if (vpc == null) { throw new Error(`A combination of conditions caused 'vpc' to be undefined. Fixit.`); }
    const publicRouteTable = new ec2.CfnRouteTable(this, 'PublicRouteTable', {
      vpcId: vpc.ref,
      tags: [
        {
          key: 'Name',
          value: `${props.pJid!}-public-route-a`,
        },
      ],
    });

    if (vpc == null) { throw new Error(`A combination of conditions caused 'vpc' to be undefined. Fixit.`); }
    const publicSubnet = new ec2.CfnSubnet(this, 'PublicSubnet', {
      availabilityZone: 'ap-northeast-1a',
      cidrBlock: props.publicSubnetCidr!,
      vpcId: vpc.ref,
      tags: [
        {
          key: 'Name',
          value: `${props.pJid!}-public-subnet-a`,
        },
      ],
    });

    if (internetGateway == null) { throw new Error(`A combination of conditions caused 'internetGateway' to be undefined. Fixit.`); }
    if (publicRouteTable == null) { throw new Error(`A combination of conditions caused 'publicRouteTable' to be undefined. Fixit.`); }
    const publicRoute = new ec2.CfnRoute(this, 'PublicRoute', {
      routeTableId: publicRouteTable.ref,
      destinationCidrBlock: '0.0.0.0/0',
      gatewayId: internetGateway.ref,
    });

    if (publicRouteTable == null) { throw new Error(`A combination of conditions caused 'publicRouteTable' to be undefined. Fixit.`); }
    if (publicSubnet == null) { throw new Error(`A combination of conditions caused 'publicSubnet' to be undefined. Fixit.`); }
    const publicSubnetRouteTablessociation = new ec2.CfnSubnetRouteTableAssociation(this, 'PublicSubnetRouteTablessociation', {
      subnetId: publicSubnet.ref,
      routeTableId: publicRouteTable.ref,
    });
  }
}

```

ちゃんと変数部分まで再現されていることが確認できました！！
このCDKを更新してデプロイすると元々CFnで構築していたリソースが更新されますので試してみてください！



# まとめ

CFnで管理していたインフラがコマンド1つでCDKに出来たら最高ですよね。