AWS

AWS CDKの新機能”cdk migrate”試してみた

akira

はじめに

AWS CDK v2.100.0で cdk migrateが追加されました

こんにちは、入社してからCDKを触り始めたばかりのCDK初心者akiraです。
2023年10月7日にAWS CDK v2.100.0がリリースされました。本ブログではリリースされた機能のうち「cdk migrate」について書かせていただきます。

何が出来るのか

現在デプロイされているCFn、ローカルのCFnファイルからCDKのソースを作成することが出来ます。CDKの言語はCDKで利用できる全ての言語をサポートしています。
本検証ではデプロイ済みのCFnリソースをCDKに取り込んでみます。本検証ではCDKの言語はTypescriptで実施します。

注意事項

本記事はリポジトリにも

CDK migrate is currently experimental and may have breaking changes in the future.

https://github.com/aws/aws-cdk/pull/27325

との記載があるとおり、あくまで現時点での検証となりますのでご了承ください。
その他の注意事項もございますのでお手元で検証する際はリポジトリに記載の注意書きをお読みいただければと思います。

実際にやってみる(デプロイ済みのCFn→CDK)

下準備

テンプレート説明

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

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

デプロイ

デプロイの手順は省略しますが以下のように設定してデプロイします。
スタック名は後ほどのコマンドで利用するのでメモしておきます。

スタック名: cdk-migrate-test

image.png (76.6 kB)

確認

成功を確認後、念のためリソースを見に行きます。

image.png (70.4 kB)

想定通り作成されています

image.png (161.0 kB)

cdk migrateコマンド実行

ここまでで準備は整ったので早速コマンドを実行していきます。
※デプロイしたAWSに対してCDKコマンドが実行できる状態にするまでの手順は割愛いたします。

コマンド実行

CDKのバージョンがv2.100.0であることを確認して実行していきます。

# バージョン確認
cdk --version

デプロイ済みのCFnに対してcdk migrateを実行する際は以下のように指定します

cdk migrate --stack-name {ターゲットのスタック名} --language {CDKの言語} --from-stack

今回は以下のように指定して実行していきます。

cdk migrate --stack-name cdk-migrate-test --language typescript --from-stack

実行後 All done! まで表示されれば成功です。

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 で指定したスタック名で作成されます。

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-migrate-test.tscdk-migrate-test-stack.tsの中身を見てみます。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkMigrateTestStack } from '../lib/cdk-migrate-test-stack';

const app = new cdk.App();
new CdkMigrateTestStack(app, 'cdk-migrate-test', {
  /* If you don't specify 'env', this stack will be environment-agnostic.
   * Account/Region-dependent features and context lookups will not work,
   * but a single synthesized template can be deployed anywhere. */

  /* Uncomment the next line to specialize this stack for the AWS Account
   * and Region that are implied by the current CLI configuration. */
  // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },

  /* Uncomment the next line if you know exactly what Account and Region you
   * want to deploy the stack to. */
  // env: { account: '123456789012', region: 'us-east-1' },

  /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});
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,
    });
  }
}

元のCFnでパラメータとしていた部分が CdkMigrateTestStackのパラメータとして渡せる(8〜16行目付近)ように再現されていることがわかりました!

CDKをデプロイしてみる

作成されたCDKをデプロイしてみる

作成されたCDKに修正の必要がなさそうなのでデプロイしてみます。すると、元はCFnで作成されていたスタックに更新が発生します。

スクリーンショット 2023-10-12 14.46.51.png (102.5 kB)

今回のデプロイでは readonly pJid?: string;の部分に引数を渡さずに実行したため、リソースのNameタグがデフォルト値のtestに変更されたことがわかります。

image.png (175.7 kB)

編集してデプロイしてみる

CDKで管理できることを確認するためにreadonly pJid?: string;cdk-migrate-successを設定してリソースのタグが変更されるか実行してみます。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkMigrateTestStack } from '../lib/cdk-migrate-test-stack';

const app = new cdk.App();
new CdkMigrateTestStack(app, 'cdk-migrate-test', {
  pJid: "cdk-migrate-success" // ★★追加★★
});

特に問題なく更新されました!

image.png (60.9 kB)

リソースの方も問題なく変更されていることがわかります。

image.png (189.1 kB)

まとめ

CFnで管理していたリソースを簡単にCDKに変更し、継続管理できることがわかりました。
冒頭に記載したとおりこれはあくまで experimental な機能なので、まだ制約事項なども多く本番投入は出来ませんが、正式リリースされれば既存のCFn運用をCDKに乗せ替えることが出来て運用の高度化など多方面に有用な機能であるとワクワクしました。安定版のリリースが待ち遠しく感じたakiraでした。

AUTHOR
akira
akira
記事URLをコピーしました