既存 AWS リソースから CloudFormation テンプレートと CDK アプリケーションを数分で生成

# どんな内容？

ずっと気になっていた機能をやっと試してみることができました。

https://aws.amazon.com/jp/about-aws/whats-new/2024/02/aws-cloudformation-templates-cdk-apps-minutes/

https://aws.amazon.com/jp/blogs/news/announcing-cdk-migrate-a-single-command-to-migrate-to-the-aws-cdk/

一言で言うと、AWS 上にデプロイされている既存リソースから CloudFromation テンプレートを生成してくれるよ。

OSS で、[Former2](https://former2.com/) と言うものがありますがこれに近いものでしょうか。

ここでは、**IaC ジェネレーター** と **cdk migrate**　を利用して実現します。


# IaC ジェネレーター

## 概要

CloudFormation IaC ジェネレーターを使用すると、CloudFormation で管理されていないプロビジョニングされた AWS リソースをテンプレートとして生成することができます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/generate-IaC.html

## 注意事項

クウォータに注意が必要そうです。

|名前|Value|
|---|---|
|1 回のアカウントスキャンで処理できるリソースの最大数|100000|
　|1 日あたりのスキャン数 (リソースが 10,000 未満のアカウントの場合)|3|
|1 日あたりのスキャン数 (リソースが 10,000 以上のアカウントの場合)|1|
|アカウントあたりの同時に生成されるテンプレートの数|5|
|1 回のテンプレート生成で同時にモデル化されるリソースの数|5|
|1 つのテンプレートでモデル化できるリソースの合計数|500|

## サポートされている AWS リソース

サポートされている AWS リソースは随時追加されていくと思いますが、ドキュメントを確認する必要があります。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html

## 1. アカウントのリソースをスキャンする

[IaC ジェネレーター] から、[新しいスキャンを開始]をクリック。
![スクリーンショット 2024-04-04 14.21.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/8b85e378-f5ce-9132-d1d9-d1ef41b9374e.png)


## 2. CloudFormation テンプレートを作成する

スキャンが完了したら、[テンプレートを作成]をクリック。
![スクリーンショット 2024-04-04 14.29.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/a44631ee-dcc4-1126-3104-7637a8e66e4b.png)

[テンプレート名]を入力。
![スクリーンショット 2024-04-04 14.40.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/7fbade1d-81c2-2af9-dfc9-7b94a937321f.png)
(オプション)必要に応じて [削除ポリシー]、[置換ポリシーを更新] を変更します。
[削除ポリシー]：スタックが削除された場合にリソースを保持するか削除するか
[置換ポリシーを更新]：スタックが置換された場合にリソースを保持するか削除するか

CloudFormation テンプレート化するリソースを選択。（ここでは、「AWS:EC2:VPC」のみ選択。）
![スクリーンショット 2024-04-04 15.08.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/7edfcbd4-3a05-1f9a-0796-7a32d69ff180.png)

ここでは、先程追加するリソースとして選択した「AWS:EC2:VPC」に関連するリソースを追加するかどうかの画面となります。ひとまずここでは全て指定しました。
![スクリーンショット 2024-04-04 14.39.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/b6b3e07d-1e66-438c-8a8a-1fb16a9b3429.png)

[テンプレートを作成]
![スクリーンショット 2024-04-04 14.40.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/9c711b0a-688b-af82-1f43-47d7beba9d55.png)

テンプレートが作成されました。Ref などもある程度いい感じに生成してくれるようです。
![スクリーンショット 2024-04-04 15.12.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/ecec10dd-283c-e6c3-dc86-5fa8366f259e.png)
（警告）「一部のオプションのプロパティがテンプレートに含まれていませんでした。リソース用に設定されている場合は、テンプレートをダウンロードしてオプションのプロパティを追加できます」：メッセージ通りサポートされていないプロパティがありそのプロパティを利用している場合は一度テンプレートをダウンロードし自身で編集してあげる必要があります。今回は利用していないプロパティでしたので特に編集は行なわず進みます。

一つ隣の[テンプレートリソース]に移動すると、ここで「論理ID」を編集することもできるようです。今回は特に編集せずこのまま進みます。
![スクリーンショット 2024-04-04 15.20.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/0582d51e-4e83-35a8-9c68-7204deba40bf.png)

## 3: CloudFormation にインポートする

「スタックにインポート」をクリック。
![スクリーンショット 2024-04-04 15.23.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/d2ab5724-0b40-6880-75aa-a352cfa0f876.png)
以降は 画面に従って、CloudFormation スタックをインポートするだけです。

## 参考

https://aws.amazon.com/jp/blogs/news/import-entire-applications-into-aws-cloudformation/

# cdk migrate

## 概要

デプロイされた CloudFormation テンプレートや CloudFormation スタック、IaC の管理外で作成されたリソースを CDK アプリケーションに移行できるよ。

https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/migrate.html

## 考慮事項

- L1 コンストラクトのみ
- CloudFormation スタックを移行する場合ネストされたテンプレートはサポートされていない
- CloudFormation テンプレートから移行する場合、 CDK CLI　は [Fn クラス](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Fn.html)を使用してロジックを CDK アプリに移行しようとする

## CloudFormation スタックからの移行

次の CloudFormation テンプレート(vpc.yaml)で、CloudFormation スタック(vpc)がデプロイ済と仮定します。

```vpc.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC with public and private subnets

Parameters:
  VpcCIDR:
    Description: CIDR block for the VPC
    Type: String
    Default: '10.0.0.0/16'

Resources:
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: '10.0.1.0/24'
      MapPublicIpOnLaunch: true

  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: '10.0.2.0/24'

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref InternetGateway

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable
```

CloudFormation スタックから移行する場合は、**--from-stack** オプションを利用します。

```
cdk migrate --from-stack --stack-name "vpc"
```

コマンドの実行が完了したら、スタックを確認してみます。

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

export interface VpcStackProps extends cdk.StackProps {
  /**
   * CIDR block for the VPC
   * @default '10.0.0.0/16'
   */
  readonly vpcCidr?: string;
}

/**
 * VPC with public and private subnets
 */
export class VpcStack extends cdk.Stack {
  public constructor(scope: cdk.App, id: string, props: VpcStackProps = {}) {
    super(scope, id, props);

    // Applying default props
    props = {
      ...props,
      vpcCidr: props.vpcCidr ?? '10.0.0.0/16',
    };

    // Resources
    const internetGateway = new ec2.CfnInternetGateway(this, 'InternetGateway', {
    });

    const myVpc = new ec2.CfnVPC(this, 'MyVPC', {
      cidrBlock: props.vpcCidr!,
    });

    const attachGateway = new ec2.CfnVPCGatewayAttachment(this, 'AttachGateway', {
      vpcId: myVpc.ref,
      internetGatewayId: internetGateway.ref,
    });

    const privateSubnet = new ec2.CfnSubnet(this, 'PrivateSubnet', {
      vpcId: myVpc.ref,
      cidrBlock: '10.0.2.0/24',
    });

    const publicRouteTable = new ec2.CfnRouteTable(this, 'PublicRouteTable', {
      vpcId: myVpc.ref,
    });

    const publicSubnet = new ec2.CfnSubnet(this, 'PublicSubnet', {
      vpcId: myVpc.ref,
      cidrBlock: '10.0.1.0/24',
      mapPublicIpOnLaunch: true,
    });

    const publicRoute = new ec2.CfnRoute(this, 'PublicRoute', {
      routeTableId: publicRouteTable.ref,
      destinationCidrBlock: '0.0.0.0/0',
      gatewayId: internetGateway.ref,
    });
    publicRoute.addDependency(attachGateway);

    const publicSubnetRouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, 'PublicSubnetRouteTableAssociation', {
      subnetId: publicSubnet.ref,
      routeTableId: publicRouteTable.ref,
    });
  }
}
```

確かに L1 コンストラクトですね。

一応変更して　cdk でデプロイされるか動作を確認してみました。myVpc に tags を追加して、デプロイしてみます。

```vpc/lib/vpc-stack.ts
    const myVpc = new ec2.CfnVPC(this, 'MyVPC', {
      cidrBlock: props.vpcCidr!,
      tags: [
        {
          key: 'Name',
          value: 'MyVPC'
        },
      ]
    });
```
    
```
cdk deploy
```

CLI で確認すると反映されていることが tags が反映されていることが確認できました。

```json
aws ec2 describe-vpcs --query 'Vpcs[].Tags'


[
    [
        {
            "Key": "aws:cloudformation:stack-name",
            "Value": "vpc"
        },
        {
            "Key": "Name",
            "Value": "MyVPC"
        },
```

## CloudFormation テンプレートからの移行

CloudFormation テンプレートから移行する場合は **--from-path** を利用します。

```
cdk migrate --from-path "./vpc.yaml" --stack-name "vpc"
```

## デプロイされたリソースからの移行

デプロイされた AWS リソースから移行するには、 **--from-scan** を利用します。
**--filter**を利用しないと、 CDK CLI は AWS 環境全体をスキャンし、IaC ジェネレーターの最大クォータ制限までリソースを移行します。

```
cdk migrate --from-scan --stack-name "vpc" --filter "type=AWS::EC2::VPC" 
```

## 参考

https://aws.amazon.com/jp/blogs/news/announcing-cdk-migrate-a-single-command-to-migrate-to-the-aws-cdk/


# アップデート

## 2025/03/27

従来、IaCジェネレーターはAWSアカウント内のすべてのリソースをスキャンしていましたが、新機能により、スキャン対象のリソースタイプを最大100種類まで指定できるようになりました。

https://aws.amazon.com/jp/about-aws/whats-new/2025/03/aws-cloudformation-targeted-resource-scans-iac-generator/