cdk importとcdk migrateについて

# はじめに

[Japan AWS Jr. Champions Advent Calendar](https://qiita.com/advent-calendar/2023/jr-champions) 5日目の投稿です。
今回は、AWS CDKのコマンドである`cdk import`と 10 月に AWS CDK `v2.100.0`となりリリースされた`cdk migrate`コマンドについて紹介します。
`v2.100.0` では、`cdk import`コマンドが stable となり、`cdk migrate`コマンドが新しく追加されました。


https://github.com/aws/aws-cdk/releases/tag/v2.100.0

# AWS Cloud Development Kit(AWS CDK)

AWS Cloud Development Kit(AWS CDK)とは、AWS リソースをプログラミング言語で定義できるツールです。
 AWS CDK では、AWS リソース定義の抽象化レイヤーが提供されているため、簡潔にリソースを定義できることが特徴です。

AWS CDK の開始方法については公式ドキュメントを参照してください。

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/getting_started.html#getting_started_prerequisites

# `cdk import`

:::note warn
cdk importは、`v2.20.0`以上で使用できる機能となっています。
:::

[`cdk import`](https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk/README.md#cdk-import) は CDK 以外の手段で作成した AWS リソースをAWS CDKの管理下に置くことができる機能です。`cdk import`コマンドを実行することで、既存のAWSリソースをAWS CDKのスタックにインポートできます。

`cdk import`を使用して既存のAmazon S3バケットをCDKスタックにインポートする例を紹介します。


:::note warn
`cdk import`はS3バケット以外のリソースにも対応しています。
`cdk import`でサポートされているリソースについては[こちら](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html)を参照してください。

:::


ここでは、AWS CDKプロジェクトがデプロイ済みで、既存のS3バケットをAWS CDKで管理したいケースを想定しています。

AWSマネージドコンソールからS3バケットを新規に作成し、S3バケットを追加したいスタックに以下のように記述します。

```ts

const bucket = new s3.Bucket(this,'SampleBucket')

```
以下のdiffコマンドで変更が発生することを確認します。
```sh
$ npx cdk diff
Resources
[+] AWS::S3::Bucket Sample-Bucket SampleBucket<ランダムな文字列>
```
次に、`cdk import`コマンドを実行し、作成したS3のバケット名を入力するとインポートが実行されます。

```sh
$ npx cdk import 
The 'cdk import' feature is currently in preview.
SampleEc2Stack
SampleEc2StackSample-Bucket/Resource (AWS::S3::Bucket): enter BucketName to import (empty to skip): [作成したバケット名を入力]
SampleEc2Stack: importing resources into stack...
SampleEc2Stack: creating CloudFormation changeset...

```

再度diffコマンドを入力し、`no differences`となっていれば取り込みが完了しています。
このようにAWS CDKの利便性を活用しながら、既存のリソースを再利用することができます。
また、AWS CDKで管理されているリソースと一緒に、既存のリソースも同じコードベースで管理することができるため、管理が簡単になります。




# `cdk migrate`

`cdk migrate`は既存のCloudFormationテンプレートもしくは既存のスタックをAWS CDKプロジェクトに変換する機能です。

:::note warn
`cdk migrate` は現在実験的なものであり、将来的に変更される可能性があります。
:::

## CloudFormationテンプレートファイルでの`cdk migrate`

### ローカルにCloudFormationテンプレートファイルを作成します。


今回はEC2インスタンスのテンプレートを作成しました。実際のコードは下記の通りです。

<details>
<summary>コードを見る</summary>

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Metadata:
  License: Apache-2.0
Description:
  "AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample:
  Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based
  on the region in which the stack is run. This example creates an EC2 security group
  for the instance to give you SSH access. **WARNING** This template creates an Amazon
  EC2 instance. You will be billed for the AWS resources used if you create a stack
  from this template."
Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t3.small
    AllowedValues:
      [
        t2.nano,
        t2.micro,
        t2.small,
        t2.medium,
        t2.large,
        t2.xlarge,
        t2.2xlarge,
        t3.nano,
        t3.micro,
        t3.small,
        t3.medium,
        t3.large,
        t3.xlarge,
        t3.2xlarge,
        m4.large,
        m4.xlarge,
        m4.2xlarge,
        m4.4xlarge,
        m4.10xlarge,
        m5.large,
        m5.xlarge,
        m5.2xlarge,
        m5.4xlarge,
        c5.large,
        c5.xlarge,
        c5.2xlarge,
        c5.4xlarge,
        c5.9xlarge,
        g3.8xlarge,
        r5.large,
        r5.xlarge,
        r5.2xlarge,
        r5.4xlarge,
        r3.12xlarge,
        i3.xlarge,
        i3.2xlarge,
        i3.4xlarge,
        i3.8xlarge,
        d2.xlarge,
        d2.2xlarge,
        d2.4xlarge,
        d2.8xlarge,
      ]
    ConstraintDescription: must be a valid EC2 instance type.
  SSHLocation:
    Description: The IP address range that can be used to SSH to the EC2 instances
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 0.0.0.0/0
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  LatestAmiId:
    Type: "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>"
    Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref "InstanceType"
      SecurityGroups: [!Ref "InstanceSecurityGroup"]
      KeyName: !Ref "KeyName"
      ImageId: !Ref "LatestAmiId"
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref "SSHLocation"
Outputs:
  InstanceId:
    Description: InstanceId of the newly created EC2 instance
    Value: !Ref "EC2Instance"
  AZ:
    Description: Availability Zone of the newly created EC2 instance
    Value: !GetAtt [EC2Instance, AvailabilityZone]
  PublicDNS:
    Description: Public DNSName of the newly created EC2 instance
    Value: !GetAtt [EC2Instance, PublicDnsName]
  PublicIP:
    Description: Public IP address of the newly created EC2 instance
    Value: !GetAtt [EC2Instance, PublicIp]

```
</details>

### `cdk migrate`コマンド実行

`cdk migrate`コマンドを実行します。
コマンドのオプションの説明は以下になります。
- `--stack-name`
    - スタック名
- `--language`
    - AWS CDKプロジェクトの言語
    - AWS CDKがサポートしている言語（TypeScript、JavaScript、Python、Java, C#、Go）はすべてサポートしています。
- `--from-path`
    - CloudFormationテンプレートファイルのパス

```sh
% cdk migrate --stack-name SampleApplication --language typescript --from-path template/ec2.yaml
This is an experimental feature and development on it is still in progress. We make no guarantees about the outcome or stability of the functionality.
 ⏳  Generating CDK app for SampleApplication...
Applying project template app for typescript
Initializing a new git repository...
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:       git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:       git branch -m <name>
Executing npm install...
✅ All done!
```

コマンドが成功するとコマンドを実行したディレクトリ配下にAWS CDKプロジェクトが作成されます。

```text
sample-application
├── README.md
├── bin
│   └── sample-application.ts
├── cdk.json
├── jest.config.js
├── lib
│   └── sample-application-stack.ts
├── node_modules
├── package-lock.json
├── package.json
├── test
│   └── sample-application.test.ts
└── tsconfig.json
```

`lib/sample-application-stack.ts`を見てみると、CloudFormationの`Parameters`が`SampleApplicationStackProps`として渡せるようになっています。


```ts:lib/sample-application-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

export interface SampleApplicationStackProps extends cdk.StackProps {
  /**
   * Name of an existing EC2 KeyPair to enable SSH access to the instance
   */
  readonly keyName: string;
  /**
   * WebServer EC2 instance type
   * @default 't3.small'
   */
  readonly instanceType?: string;
  /**
   * The IP address range that can be used to SSH to the EC2 instances
   * @default '0.0.0.0/0'
   */
  readonly sshLocation?: string;
  /**
   * @default '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
   */
  readonly latestAmiId?: string;
}

/**
 * AWS CloudFormation Sample Template EC2InstanceWithSecurityGroupSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example creates an EC2 security group for the instance to give you SSH access. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.
 */
export class SampleApplicationStack extends cdk.Stack {
  /**
   * InstanceId of the newly created EC2 instance
   */
  public readonly instanceId;
  /**
   * Availability Zone of the newly created EC2 instance
   */
  public readonly az;
  /**
   * Public DNSName of the newly created EC2 instance
   */
  public readonly publicDns;
  /**
   * Public IP address of the newly created EC2 instance
   */
  public readonly publicIp;

  public constructor(scope: cdk.App, id: string, props:SampleApplicationStackProps) {
    super(scope, id, props);

    // Applying default props
    props = {
      ...props,
      keyName: new cdk.CfnParameter(this, 'KeyName', {
        type: 'AWS::EC2::KeyPair::KeyName',
        default: props.keyName.toString(),
        description: 'Name of an existing EC2 KeyPair to enable SSH access to the instance',
      }).valueAsString,
      instanceType: props.instanceType ?? 't3.small',
      sshLocation: props.sshLocation ?? '0.0.0.0/0',
      latestAmiId: new cdk.CfnParameter(this, 'LatestAmiId', {
        type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>',
        default: props.latestAmiId?.toString() ?? '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2',
      }).valueAsString,
    };

    // Resources
    const instanceSecurityGroup = new ec2.CfnSecurityGroup(this, 'InstanceSecurityGroup', {
      groupDescription: 'Enable SSH access via port 22',
      securityGroupIngress: [
        {
          ipProtocol: 'tcp',
          fromPort: 22,
          toPort: 22,
          cidrIp: props.sshLocation!,
        },
      ],
    });

    if (instanceSecurityGroup == null) { throw new Error(`A combination of conditions caused 'instanceSecurityGroup' to be undefined. Fixit.`); }
    const ec2Instance = new ec2.CfnInstance(this, 'EC2Instance', {
      instanceType: props.instanceType!,
      securityGroups: [
        instanceSecurityGroup.ref,
      ],
      keyName: props.keyName!,
      imageId: props.latestAmiId!,
    });

    // Outputs
    this.instanceId = ec2Instance.ref;
    new cdk.CfnOutput(this, 'InstanceId', {
      description: 'InstanceId of the newly created EC2 instance',
      value: this.instanceId!.toString(),
    });
    this.az = ec2Instance.attrAvailabilityZone;
    new cdk.CfnOutput(this, 'AZ', {
      description: 'Availability Zone of the newly created EC2 instance',
      value: this.az!.toString(),
    });
    this.publicDns = ec2Instance.attrPublicDnsName;
    new cdk.CfnOutput(this, 'PublicDNS', {
      description: 'Public DNSName of the newly created EC2 instance',
      value: this.publicDns!.toString(),
    });
    this.publicIp = ec2Instance.attrPublicIp;
    new cdk.CfnOutput(this, 'PublicIP', {
      description: 'Public IP address of the newly created EC2 instance',
      value: this.publicIp!.toString(),
    });
  }
}


```





## 既存のスタックでの`cdk migrate`

次は既存のデプロイ済みのCloudFormationのスタックに対し、`cdk migrate`コマンドを実行します。

### CloudFormationのスタックをデプロイ

[AWS公式のCloudFormationサンプル](https://github.com/awslabs/aws-cloudformation-templates/blob/master/aws/services/EC2/EC2_Instance_With_Ephemeral_Drives.yaml)をあらかじめデプロイします。




### `cdk migrate`コマンド実行

`--stack-name`オプションでAWSのマネージドコンソールで表示されるCloudFormationのスタック名を指定し、コマンドを実行します。

```sh
$ cdk migrate --stack-name sample-stack --language typescript --from-stack
```

生成されたソースコードはコード量が多いので折りたたんでいます。

<details>
<summary>コードを見る</summary>

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

export interface SampleStackStackProps extends cdk.StackProps {
  /**
   * Name of an existing EC2 KeyPair to enable SSH access to the web server
   */
  readonly keyName: string;
  /**
   * WebServer EC2 instance type
   * @default 't2.small'
   */
  readonly instanceType?: string;
  /**
   * Lockdown SSH access to the bastion host (default can be accessed from anywhere)
   * @default '0.0.0.0/0'
   */
  readonly sshLocation?: string;
}

/**
 * AWS CloudFormation Sample Template EC2_Instance_With_Ephemeral_Drives: Example to show how to attach ephemeral drives using EC2 block device mappings. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.
 */
export class SampleStackStack extends cdk.Stack {
  /**
   * DNS Name of the newly created EC2 instance
   */
  public readonly instance;

  public constructor(scope: cdk.App, id: string, props: SampleStackStackProps) {
    super(scope, id, props);

    // Applying default props
    props = {
      ...props,
      keyName: new cdk.CfnParameter(this, 'KeyName', {
        type: 'AWS::EC2::KeyPair::KeyName',
        default: props.keyName.toString(),
        description: 'Name of an existing EC2 KeyPair to enable SSH access to the web server',
      }).valueAsString,
      instanceType: props.instanceType ?? 't2.small',
      sshLocation: props.sshLocation ?? '0.0.0.0/0',
    };

    // Mappings
    const awsInstanceType2Arch: Record<string, Record<string, string>> = {
      't1.micro': {
        'Arch': 'HVM64',
      },
      't2.nano': {
        'Arch': 'HVM64',
      },
      't2.micro': {
        'Arch': 'HVM64',
      },
      't2.small': {
        'Arch': 'HVM64',
      },
      't2.medium': {
        'Arch': 'HVM64',
      },
      't2.large': {
        'Arch': 'HVM64',
      },
      'm1.small': {
        'Arch': 'HVM64',
      },
      'm1.medium': {
        'Arch': 'HVM64',
      },
      'm1.large': {
        'Arch': 'HVM64',
      },
      'm1.xlarge': {
        'Arch': 'HVM64',
      },
      'm2.xlarge': {
        'Arch': 'HVM64',
      },
      'm2.2xlarge': {
        'Arch': 'HVM64',
      },
      'm2.4xlarge': {
        'Arch': 'HVM64',
      },
      'm3.medium': {
        'Arch': 'HVM64',
      },
      'm3.large': {
        'Arch': 'HVM64',
      },
      'm3.xlarge': {
        'Arch': 'HVM64',
      },
      'm3.2xlarge': {
        'Arch': 'HVM64',
      },
      'm4.large': {
        'Arch': 'HVM64',
      },
      'm4.xlarge': {
        'Arch': 'HVM64',
      },
      'm4.2xlarge': {
        'Arch': 'HVM64',
      },
      'm4.4xlarge': {
        'Arch': 'HVM64',
      },
      'm4.10xlarge': {
        'Arch': 'HVM64',
      },
      'c1.medium': {
        'Arch': 'HVM64',
      },
      'c1.xlarge': {
        'Arch': 'HVM64',
      },
      'c3.large': {
        'Arch': 'HVM64',
      },
      'c3.xlarge': {
        'Arch': 'HVM64',
      },
      'c3.2xlarge': {
        'Arch': 'HVM64',
      },
      'c3.4xlarge': {
        'Arch': 'HVM64',
      },
      'c3.8xlarge': {
        'Arch': 'HVM64',
      },
      'c4.large': {
        'Arch': 'HVM64',
      },
      'c4.xlarge': {
        'Arch': 'HVM64',
      },
      'c4.2xlarge': {
        'Arch': 'HVM64',
      },
      'c4.4xlarge': {
        'Arch': 'HVM64',
      },
      'c4.8xlarge': {
        'Arch': 'HVM64',
      },
      'g2.2xlarge': {
        'Arch': 'HVMG2',
      },
      'g2.8xlarge': {
        'Arch': 'HVMG2',
      },
      'r3.large': {
        'Arch': 'HVM64',
      },
      'r3.xlarge': {
        'Arch': 'HVM64',
      },
      'r3.2xlarge': {
        'Arch': 'HVM64',
      },
      'r3.4xlarge': {
        'Arch': 'HVM64',
      },
      'r3.8xlarge': {
        'Arch': 'HVM64',
      },
      'i2.xlarge': {
        'Arch': 'HVM64',
      },
      'i2.2xlarge': {
        'Arch': 'HVM64',
      },
      'i2.4xlarge': {
        'Arch': 'HVM64',
      },
      'i2.8xlarge': {
        'Arch': 'HVM64',
      },
      'd2.xlarge': {
        'Arch': 'HVM64',
      },
      'd2.2xlarge': {
        'Arch': 'HVM64',
      },
      'd2.4xlarge': {
        'Arch': 'HVM64',
      },
      'd2.8xlarge': {
        'Arch': 'HVM64',
      },
      'hi1.4xlarge': {
        'Arch': 'HVM64',
      },
      'hs1.8xlarge': {
        'Arch': 'HVM64',
      },
      'cr1.8xlarge': {
        'Arch': 'HVM64',
      },
      'cc2.8xlarge': {
        'Arch': 'HVM64',
      },
    };
    const awsInstanceType2NatArch: Record<string, Record<string, string>> = {
      't1.micro': {
        'Arch': 'NATHVM64',
      },
      't2.nano': {
        'Arch': 'NATHVM64',
      },
      't2.micro': {
        'Arch': 'NATHVM64',
      },
      't2.small': {
        'Arch': 'NATHVM64',
      },
      't2.medium': {
        'Arch': 'NATHVM64',
      },
      't2.large': {
        'Arch': 'NATHVM64',
      },
      'm1.small': {
        'Arch': 'NATHVM64',
      },
      'm1.medium': {
        'Arch': 'NATHVM64',
      },
      'm1.large': {
        'Arch': 'NATHVM64',
      },
      'm1.xlarge': {
        'Arch': 'NATHVM64',
      },
      'm2.xlarge': {
        'Arch': 'NATHVM64',
      },
      'm2.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'm2.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'm3.medium': {
        'Arch': 'NATHVM64',
      },
      'm3.large': {
        'Arch': 'NATHVM64',
      },
      'm3.xlarge': {
        'Arch': 'NATHVM64',
      },
      'm3.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'm4.large': {
        'Arch': 'NATHVM64',
      },
      'm4.xlarge': {
        'Arch': 'NATHVM64',
      },
      'm4.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'm4.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'm4.10xlarge': {
        'Arch': 'NATHVM64',
      },
      'c1.medium': {
        'Arch': 'NATHVM64',
      },
      'c1.xlarge': {
        'Arch': 'NATHVM64',
      },
      'c3.large': {
        'Arch': 'NATHVM64',
      },
      'c3.xlarge': {
        'Arch': 'NATHVM64',
      },
      'c3.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'c3.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'c3.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'c4.large': {
        'Arch': 'NATHVM64',
      },
      'c4.xlarge': {
        'Arch': 'NATHVM64',
      },
      'c4.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'c4.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'c4.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'g2.2xlarge': {
        'Arch': 'NATHVMG2',
      },
      'g2.8xlarge': {
        'Arch': 'NATHVMG2',
      },
      'r3.large': {
        'Arch': 'NATHVM64',
      },
      'r3.xlarge': {
        'Arch': 'NATHVM64',
      },
      'r3.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'r3.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'r3.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'i2.xlarge': {
        'Arch': 'NATHVM64',
      },
      'i2.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'i2.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'i2.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'd2.xlarge': {
        'Arch': 'NATHVM64',
      },
      'd2.2xlarge': {
        'Arch': 'NATHVM64',
      },
      'd2.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'd2.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'hi1.4xlarge': {
        'Arch': 'NATHVM64',
      },
      'hs1.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'cr1.8xlarge': {
        'Arch': 'NATHVM64',
      },
      'cc2.8xlarge': {
        'Arch': 'NATHVM64',
      },
    };
    const awsRegionArch2Ami: Record<string, Record<string, string>> = {
      'af-south-1': {
        'HVM64': 'ami-064cc455f8a1ef504',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'ap-east-1': {
        'HVM64': 'ami-f85b1989',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'ap-northeast-1': {
        'HVM64': 'ami-0b2c2a754d5b4da22',
        'HVMG2': 'ami-09d0e0e099ecabba2',
      },
      'ap-northeast-2': {
        'HVM64': 'ami-0493ab99920f410fc',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'ap-northeast-3': {
        'HVM64': 'ami-01344f6f63a4decc1',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'ap-south-1': {
        'HVM64': 'ami-03cfb5e1fb4fac428',
        'HVMG2': 'ami-0244c1d42815af84a',
      },
      'ap-southeast-1': {
        'HVM64': 'ami-0ba35dc9caf73d1c7',
        'HVMG2': 'ami-0e46ce0d6a87dc979',
      },
      'ap-southeast-2': {
        'HVM64': 'ami-0ae99b503e8694028',
        'HVMG2': 'ami-0c0ab057a101d8ff2',
      },
      'ca-central-1': {
        'HVM64': 'ami-0803e21a2ec22f953',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'cn-north-1': {
        'HVM64': 'ami-07a3f215cc90c889c',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'cn-northwest-1': {
        'HVM64': 'ami-0a3b3b10f714a0ff4',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'eu-central-1': {
        'HVM64': 'ami-0474863011a7d1541',
        'HVMG2': 'ami-0aa1822e3eb913a11',
      },
      'eu-north-1': {
        'HVM64': 'ami-0de4b8910494dba0f',
        'HVMG2': 'ami-32d55b4c',
      },
      'eu-south-1': {
        'HVM64': 'ami-08427144fe9ebdef6',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'eu-west-1': {
        'HVM64': 'ami-015232c01a82b847b',
        'HVMG2': 'ami-0d5299b1c6112c3c7',
      },
      'eu-west-2': {
        'HVM64': 'ami-0765d48d7e15beb93',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'eu-west-3': {
        'HVM64': 'ami-0caf07637eda19d9c',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'me-south-1': {
        'HVM64': 'ami-0744743d80915b497',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'sa-east-1': {
        'HVM64': 'ami-0a52e8a6018e92bb0',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'us-east-1': {
        'HVM64': 'ami-032930428bf1abbff',
        'HVMG2': 'ami-0aeb704d503081ea6',
      },
      'us-east-2': {
        'HVM64': 'ami-027cab9a7bf0155df',
        'HVMG2': 'NOT_SUPPORTED',
      },
      'us-west-1': {
        'HVM64': 'ami-088c153f74339f34c',
        'HVMG2': 'ami-0a7fc72dc0e51aa77',
      },
      'us-west-2': {
        'HVM64': 'ami-01fee56b22f308154',
        'HVMG2': 'ami-0fe84a5b4563d8f27',
      },
    };

    // Resources
    const ec2SecurityGroup = new ec2.CfnSecurityGroup(this, 'EC2SecurityGroup', {
      groupDescription: 'SSH access',
      securityGroupIngress: [
        {
          ipProtocol: 'tcp',
          fromPort: 22,
          toPort: 22,
          cidrIp: props.sshLocation!,
        },
      ],
    });

    if (ec2SecurityGroup == null) { throw new Error(`A combination of conditions caused 'ec2SecurityGroup' to be undefined. Fixit.`); }
    const ec2Instance = new ec2.CfnInstance(this, 'EC2Instance', {
      keyName: props.keyName!,
      instanceType: props.instanceType!,
      imageId: awsRegionArch2Ami[this.region][awsInstanceType2Arch[props.instanceType!]['Arch']],
      securityGroups: [
        ec2SecurityGroup.ref,
      ],
      blockDeviceMappings: [
        {
          deviceName: '/dev/sdc',
          virtualName: 'ephemeral0',
        },
      ],
    });

    // Outputs
    this.instance = ec2Instance.attrPublicDnsName;
    new cdk.CfnOutput(this, 'Instance', {
      description: 'DNS Name of the newly created EC2 instance',
      value: this.instance!.toString(),
    });
  }
}

```

</details>

生成されたコードを見るに、各リージョンやインスタンスタイプのマッピングに`Record<string, Record<string, string>> `などTypeScriptの型定義されていることがわかります。


### `cdk migrate`の制約と注意点

`cdk migrate`はまだ実験的なコマンドのため、執筆時点(2023/12/5）では以下の制約があります。

- `cdk migrate` はネストされたスタック、カスタムリソース、`Fn::ForEach` 組み込み関数をサポートしていません。
- `cdk migrate`はL1コンストラクトのみを対応し、L2,L3コンストラクトには対応していません。
- `cdk migrate`がアプリケーションの生成に成功しても、そのアプリケーションがすぐにデプロイ可能であることを保証するものではありません。


# 最後に

今回は、`cdk import`と`cdk migrate`コマンドを紹介しました。`cdk import`はあるリソースをAWS CDKの管理下にしたい時に使い、`cdk migrate`はCloudFormationのスタックをAWS CDKのプロジェクトにしたい時に使えます。
`cdk migrate`は実験的な機能ということやまだL1コンストラクトにしか対応していませんが、今後、L2コンストラクトなどに対応すると便利だなと思いました。