cdk migrateを使ってみる
cdk migrateを使ってみる
cdk migrate自体の詳しくはこちらをご参照ください。
※本ページ記載の内容は2023年11月18日時点での挙動です。
今回サンプルとするテンプレート
実際にcdk migrateの対象とするスタック作成の元となるテンプレートは、過去のテンプレからリージョンを跨いで連携してるようなものを適当に選びました。
TGWとかRAMがリソースとして入っていますが、そこは今回重要でなく、サンプルですので気にせずでOKです。
一応参照元(開かず飛ばして問題なしです)
今回cdk migrateで取り込んでみるテンプレは↓のような経緯で作成したものから適当に選びました。
TGWアカウント挟んでアカウントAとアカウントBがアカウント跨ぎリージョン跨ぎで通信出来るようにするテンプレートのうち、TGWアカウントで作成する東京とヴァージニアのテンプレートを引っ張ってきただけのものです。(図2の①と②のテンプレート)
※これらは工程の一部で使っていたものでこれだけでは特段意味をなしません。
[図1]
[図2]
リージョンが違えばスタック名は重複可能な為、どちらも"sample"という名前でスタックを作成してみます。
サンプルテンプレA(ap-northeast-1に作成する)
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
PJPrefix: # リソース名やNameタグ値の接頭辞に利用
Type: String
Environment: # 同上(環境名)
Type: String
Default: prd
AccountAId:
Type: String
Resources:
# ------------------------------------------------------------#
# RAM
# ------------------------------------------------------------#
ResourceShare:
Type: AWS::RAM::ResourceShare
DependsOn:
- TransitGateway
Properties:
Name: !Sub ${PJPrefix}-${Environment}-ram
ResourceArns:
- !Sub arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:transit-gateway/${TransitGateway}
Principals:
- !Ref AccountAId
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-ram-for-account-a
# ------------------------------------------------------------#
# TransitGateway
# ------------------------------------------------------------#
TransitGateway:
Type: AWS::EC2::TransitGateway
Properties:
AmazonSideAsn: 64512 # デフォルト値
AutoAcceptSharedAttachments: enable
DefaultRouteTableAssociation: enable
DefaultRouteTablePropagation: enable
DnsSupport: enable
VpnEcmpSupport: enable
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-transitgateway-for-account-a
Outputs:
TransitGatewayId:
Value: !Ref TransitGateway
PeerRegion:
Value: !Ref AWS::Region
PeerTransitGatewayId:
Value: !Ref TransitGateway
サンプルテンプレB(us-east-1に作成する)
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
PJPrefix: # リソース名やNameタグ値の接頭辞に利用
Type: String
Environment: # 同上(環境名)
Type: String
Default: prd
AccountBId:
Type: String
PeerRegion: # 1stリージョンのスタックの同名出力値を入力
Type: String
PeerTransitGatewayId: # 1stリージョンのスタックの同名出力値を入力
Type: String
Resources:
# ------------------------------------------------------------#
# RAM
# ------------------------------------------------------------#
ResourceShare:
Type: AWS::RAM::ResourceShare
DependsOn:
- TransitGateway
Properties:
Name: !Sub ${PJPrefix}-${Environment}-ram
ResourceArns:
- !Sub arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:transit-gateway/${TransitGateway}
Principals:
- !Ref AccountBId
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-ram-for-account-b
# ------------------------------------------------------------#
# TransitGateway
# ------------------------------------------------------------#
TransitGateway:
Type: AWS::EC2::TransitGateway
Properties:
AmazonSideAsn: 64512 # デフォルト値
AutoAcceptSharedAttachments: enable
DefaultRouteTableAssociation: enable
DefaultRouteTablePropagation: enable
DnsSupport: enable
VpnEcmpSupport: enable
Tags:
- Key: Name
Value: !Sub ${PJPrefix}-${Environment}-transitgateway-for-account-b
TransitGatewayPeeringAttachment:
Type: AWS::EC2::TransitGatewayPeeringAttachment
Properties:
PeerAccountId: !Ref AWS::AccountId
PeerRegion: !Ref PeerRegion
PeerTransitGatewayId: !Ref PeerTransitGatewayId
TransitGatewayId: !Ref TransitGateway
Outputs:
TransitGatewayId:
Value: !Ref TransitGateway
やってみる
cdk migrateが動作するように最新バージョンにしておきます。(migrate機能が追加される前のヴァージョンのまま先に進まないように)
$ npm install -g aws-cdk
changed 2 packages in 2s
↓
フォルダを作成し、中に入っておきます。
$ mkdir cdk_migrate_test && cd cdk_migrate_test
↓
cdk migrate --stack-name sample --language typescript --from-stack --account [アカウントのID] --region ap-northeast-1
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 sample...
Applying project template app for typescript
Executing npm install...
✅ All done!
cdk migrate --stack-name sample --language typescript --from-stack --account [アカウントのID] --region us-east-1
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 sample...
Applying project template app for typescript
Executing npm install...
✅ All done!
ローカルの同ディレクトリに同名スタックをcdk migrateした場合
あえて同じスタック名で試したのはこれがどうなるか確認したかっただけなのですが、
cdk migrateを実行したローカルのディレクトリで
・ap-northeast-1にあるsampleスタックをcdk migrateした後
・us-east-1にあるsampleスタックをcdk migrateした場合、
同じ名前のフォルダが既にある為エラーを表示せず、そのまま上書きされるのを確認しました。
ヘルプコマンドを確認しましたが、このようなスタック名の重複時、生成するフォルダ(ディレクトリ)名自体を意図的に変更するコマンドや、重複する場合エラーを起こすかどうかや、名前を自動的に(1)等にする事を選択出来るものはなさそうに感じました。
私が実行した時点で且つざっと見た感じですので念の為、ご確認いただければと思います。
※これ以降アカウント、リージョン跨ぎでスタック名が重複している問題には注力しませんので、一旦us-east-1リージョンのスタック名はsample2である前提で進めます。
migrateコマンド実行結果
binとlib直下のtsだけですが、結果を畳みました。
当たり前ですが、作成されたスタックのjsonから読み取っているのだと思うので、CloudFormationでスタックを作成する時点で既にコメントアウトしていた部分はcdk migrateではlib/⚪︎⚪︎-stack.tsには反映されない事が確認出来ました。
テンプレの記述を補足するコメントをcdk migrateで吸い出す事は出来なさそうです。
テンプレAのmigrate結果
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { SampleStack } from '../lib/sample-stack';
const app = new cdk.App();
new SampleStack(app, 'sample', {
/* 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';
import * as ram from 'aws-cdk-lib/aws-ram';
export interface SampleStackProps extends cdk.StackProps {
/**
*/
readonly pjPrefix: string;
/**
* @default 'prd'
*/
readonly environment?: string;
/**
*/
readonly accountAId: string;
}
export class SampleStack extends cdk.Stack {
public readonly transitGatewayId;
public readonly peerRegion;
public readonly peerTransitGatewayId;
public constructor(scope: cdk.App, id: string, props: SampleStackProps) {
super(scope, id, props);
// Applying default props
props = {
...props,
environment: props.environment ?? 'prd',
};
// Resources
const transitGateway = new ec2.CfnTransitGateway(this, 'TransitGateway', {
amazonSideAsn: 64512,
autoAcceptSharedAttachments: 'enable',
defaultRouteTableAssociation: 'enable',
defaultRouteTablePropagation: 'enable',
dnsSupport: 'enable',
vpnEcmpSupport: 'enable',
tags: [
{
key: 'Name',
value: `${props.pjPrefix!}-${props.environment!}-transitgateway-for-account-a`,
},
],
});
if (transitGateway == null) { throw new Error(`A combination of conditions caused 'transitGateway' to be undefined. Fixit.`); }
const resourceShare = new ram.CfnResourceShare(this, 'ResourceShare', {
name: `${props.pjPrefix!}-${props.environment!}-ram`,
resourceArns: [
`arn:aws:ec2:${this.region}:${this.account}:transit-gateway/${transitGateway.ref}`,
],
principals: [
props.accountAId!,
],
tags: [
{
key: 'Name',
value: `${props.pjPrefix!}-${props.environment!}-ram-for-account-a`,
},
],
});
resourceShare.addDependency(transitGateway);
// Outputs
this.transitGatewayId = transitGateway.ref;
new cdk.CfnOutput(this, 'TransitGatewayId', {
value: this.transitGatewayId!.toString(),
});
this.peerRegion = this.region;
new cdk.CfnOutput(this, 'PeerRegion', {
value: this.peerRegion!.toString(),
});
this.peerTransitGatewayId = transitGateway.ref;
new cdk.CfnOutput(this, 'PeerTransitGatewayId', {
value: this.peerTransitGatewayId!.toString(),
});
}
}
テンプレBのmigrate結果
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { SampleStack } from '../lib/sample-stack';
const app = new cdk.App();
new SampleStack(app, 'sample', {
/* 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';
import * as ram from 'aws-cdk-lib/aws-ram';
export interface SampleStackProps extends cdk.StackProps {
/**
*/
readonly pjPrefix: string;
/**
* @default 'prd'
*/
readonly environment?: string;
/**
*/
readonly accountBId: string;
/**
*/
readonly peerRegion: string;
/**
*/
readonly peerTransitGatewayId: string;
}
export class SampleStack extends cdk.Stack {
public readonly transitGatewayId;
public constructor(scope: cdk.App, id: string, props: SampleStackProps) {
super(scope, id, props);
// Applying default props
props = {
...props,
environment: props.environment ?? 'prd',
};
// Resources
const transitGateway = new ec2.CfnTransitGateway(this, 'TransitGateway', {
amazonSideAsn: 64512,
autoAcceptSharedAttachments: 'enable',
defaultRouteTableAssociation: 'enable',
defaultRouteTablePropagation: 'enable',
dnsSupport: 'enable',
vpnEcmpSupport: 'enable',
tags: [
{
key: 'Name',
value: `${props.pjPrefix!}-${props.environment!}-transitgateway-for-account-b`,
},
],
});
if (transitGateway == null) { throw new Error(`A combination of conditions caused 'transitGateway' to be undefined. Fixit.`); }
const resourceShare = new ram.CfnResourceShare(this, 'ResourceShare', {
name: `${props.pjPrefix!}-${props.environment!}-ram`,
resourceArns: [
`arn:aws:ec2:${this.region}:${this.account}:transit-gateway/${transitGateway.ref}`,
],
principals: [
props.accountBId!,
],
tags: [
{
key: 'Name',
value: `${props.pjPrefix!}-${props.environment!}-ram-for-account-b`,
},
],
});
resourceShare.addDependency(transitGateway);
if (transitGateway == null) { throw new Error(`A combination of conditions caused 'transitGateway' to be undefined. Fixit.`); }
const transitGatewayPeeringAttachment = new ec2.CfnTransitGatewayPeeringAttachment(this, 'TransitGatewayPeeringAttachment', {
peerAccountId: this.account,
peerRegion: props.peerRegion!,
peerTransitGatewayId: props.peerTransitGatewayId!,
transitGatewayId: transitGateway.ref,
});
// Outputs
this.transitGatewayId = transitGateway.ref;
new cdk.CfnOutput(this, 'TransitGatewayId', {
value: this.transitGatewayId!.toString(),
});
}
}
スタックが残存している状況で、先ほどmigrateしたものをcdk deployしてみる
環境変数部分など多少の書き換えをした後、試しに同じプロジェクトのlib直下に
・sample-stack.ts
・sample2-stack.ts
を配置し、
bin内のsample.tsの内容(リージョン部分等)整合をつけ
cdk deploy --all
を実行しました。
それぞれのリージョンのそれぞれのスタックの「変更セット」タブを見ると
cdkによって制御しデプロイがされている事が確認出来ました。
この時点で従前の内容から変更点がなければ、単純にCDKの統制下に置くに相当する作業かと思います。この位のテンプレートであれば問題なくcdkに書き換える事が出来るみたいです。
ローカルにあるYAML,JSONからでも作成可能
cdk migrate --stack-name sample --language typescript --from-path sample.yml
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 sample...
Applying project template app for typescript
Executing npm install...
✅ All done!
一瞬CFn慣れしてる人がYAMLで書いてcdk migrateしてCDK化出来るねって考えにも出来るかなーと思いましたが、そういう事じゃないなと。
L3が無理なら→L2、L2で無理なら→L1っていう順序で作成していく事はあっても、
L1で成立しちゃってるものを取り込んで、L2→L3で同じ事出来ないかって模索する事はないだろうというのと(CDK的な可読性の恩恵が受けられない)+CFnで物作ってちゃ開発者体験あがらないって事だと思います。(linter等の恩恵が受けられない=>デプロイ前にエラーやミスが起きる可能性を減少させられない)
以上でした
まだまだ調べたい事・試したい事はありますが、今回はここまでとして次回以降頑張りたいと思います。有難うございました。
Discussion