こんにちは、AWSおよびCDKひよこクラブのmasaozi3です
本記事はAWS/CDK/CloudFormation初学者向けの内容です。
あまりやらないとおもうのでニーズがあるのかわかりませんが、一度CFnで作成したスタック定義を完全にCDKで書き直したい場面にぶちあたり、どういう挙動になるのか気になったので調査してみました。
そしてさらにニーズがあるのかわかりませんが、逆にCDKで作成したスタックをCFnに完全に書き換えるパターンもやってみました。
ちなみにCDKではcloudformation-include を使うことで、CFnファイルをテンプレートとして取り込みつつ実装することもできるそうですが、今回はCDKで全てを書き直し、CFnのファイルは破棄できる状態にすることが前提です。
スタックIDを一致させれば同一のスタックへの操作と認識してくれて、その中のリソースもIDを一致させれば同じリソースへの操作として認識してくれないかな…というのが期待する結果です。
「前述のライブラリがあるってことは、うまいこといけるのでは?」という甘い考えでやっていきます
検証環境
- Mac OS 12
- Typescript 4.8.4
- node 18.10.0
- CDK CLI 2.44.0
準備
S3と、SSL通信じゃないアクセスを弾くバケットポリシーを作成するだけのシンプルなスタックを定義します。
スタックID、リソースのIDがCFnとCDKで一致するように明記しています。
CFn
AWSTemplateFormatVersion: '2010-09-09'
Description: create s3 bucket from CFn
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: 'cfn-cdk-sample-bucket'
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
S3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: 'cfn-cdk-sample-bucket'
PolicyDocument:
Version: 2012-10-17
Statement:
- Action: 's3:*'
Effect: Deny
Principal: '*'
Resource:
- !GetAtt S3Bucket.Arn
- !Sub ${S3Bucket.Arn}/*
Condition:
Bool:
'aws:SecureTransport': 'false'
CDK
スタックの定義はこちら。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {BlockPublicAccess, Bucket, BucketAccessControl} from "aws-cdk-lib/aws-s3";
export class s3Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new Bucket(this, 'S3Bucket', {
bucketName: 'cfn-cdk-sample-bucket',
accessControl: BucketAccessControl.PRIVATE,
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
enforceSSL: true
})
}
}
CDK appの方はこちら。
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { s3Stack } from '../lib/s3-stack';
const app = new cdk.App();
new s3Stack(app, 'cfn-cdk-sample-stack', {
stackName: 'cfn-cdk-sample-stack',
description: 'create s3 bucket from CDK yeah!',
});
調査1. CFn→CDKの書き換え
まずはCFnをデプロイします。
$ aws cloudformation create-stack \
--stack-name cfn-cdk-sample-stack \
--template-body file://path/to/s3stack.yaml \
--profile my-profile
次はいざ、このスタックに対してCDKでdiffを出してみます。
$ npm run cdk diff cfn-cdk-sample-stack -- --profile my-profile
いろいろ差分が示されました。
ふむふむどうやら無事に、CFnで作成したスタックの更新と認識されているようです。
わざと変えておいたDescriptionや、実装ミスで完全に同じにできていない箇所、CFnとCDKでの書き方の違いによるものが変更内容として出ていますね。
ここまでは良さそうです。
スタック内のリソースはどうでしょうか。
残念ながら、完全に別のリソースとして作り直されてしまうようです。
赤字のdestroyが悲しい…
特にS3はIDもバケット名も揃えて書きましたが、CDKによって指定したIDをもとに内部でさらにユニークなIDを振り直されてしまってだめなようです。
XXX.fromLookup()
とかXXX.fromXXXArn()
など既存のリソースを参照するための実装があることを思うと、納得の結果ではあります。
一応デプロイもしてみましょう。
$ npm run cdk deploy cfn-cdk-sample-stack -- --profile my-profile
diffの段階で判明したとおり、別のリソースとして同じ名前のバケットを作成するという操作をしようとしたため
「同名のバケットもうこのスタック内にありますね」と怒られてしまったようです
CFn→CDKのまとめ
- スタックはIDを揃えれば同一のスタックとして認識してくれる
- スタック内のリソースは別物(作り直し)になる
-
new
してる限りCDKでIDが振り直される -
XXX.fromLookup()
とかXXX.fromXXXArn()
など既存のリソースを参照するための実装がある
-
調査2. CDK→CFnの書き換え
先程のCFn→CDKで作成したスタックやリソースは一通り削除したうえで、まずはCDKをデプロイします。
$ npm run cdk deploy cfn-cdk-sample-stack --profile my-profile
ここで設定されたリソースのIDをもとに少しだけCFnを修正します。
Resources:
S3Bucket07682993: # ここをCDKで設定されたIDに修正
Type: AWS::S3::Bucket
Properties:
BucketName: 'cfn-cdk-sample-bucket'
〜中略〜
S3BucketPolicyF560589A: # ここをCDKで設定されたIDに修正
Type: AWS::S3::BucketPolicy
Properties:
Bucket: 'cfn-cdk-sample-bucket'
…
CFnではdiffを出す方法がないので、今回は変更セットを作成してみます。
$ aws cloudformation create-change-set \
--stack-name {stackArn}\
--change-set-name DemoChangeSet \
--template-body file://path/to/s3stack.yaml \
--profile my-profile
AWSコンソール上から、作成した変更セットを確認してみると、無事に変更セットが作成されていました。
CDKで作成したスタックの更新と認識されているようです。
アクションの欄を見ると、S3もバケットポリシーも Modify
とあるので、CDKで作成したリソースに対する更新だと認識してくれているようです
CDKMetadataは Remove
とあるのでもう不要だと判断して削除されるようですね。
そして置換の欄を見ると、S3はFalse
なので再作成されないようですね。CFn→CDKのときの挙動を思うと納得です。
バケットポリシーはTrue
なので、同じIDで作成され直すようです。
このあたりについてはどのリソースタイプの何のプロパティを変更するかによって決定されるので今回の調査としては問題ないです。
変更セットを適用してみると、無事にバケットを作り直すことなくスタックの更新が完了しました。
CDK→CFnのまとめ
- スタックはIDを揃えれば同一のスタックとして認識してくれる
- スタック内のリソースはもIDを揃えれば同一のスタックとして認識してくれる
まとめ
ということで、今回はレアケースだと思いますがCFnで作成したスタックとリソースをCDKでもう一度書き直してみるとどうなるのか試してみました。
CFn→CDKのケースではIDを揃えればスタックは同じものを操作できますが、スタック内のリソースはCDKによってIDを振り直されるので別のものと認識されてしまう事がわかりました。
逆にCDK→CFnのケースでは、IDを揃えればスタックもリソースも同じものを操作できるということがわかりました。
CFn→CDKのケースで、リソースを作り直されたくなければちゃんとLookupせよ、ということで
ニーズのよくわからない調査はこれにて終了です!お疲れさまでした