CS1の石井です。
タイトルの通りCDKのsynth時にVSCodeを用いてデバッグ実行を行う手法をまとめた記事です。
本記事を書いた動機
私は今までCDKで扱う環境ごとのパラメータはcdk.jsonにちょろちょろ書いていました。
ただ、最近パラメータの量も多くなりバリデーションチェックを行いたいと思うようになってきました。
そこで、下記の記事を参考にzodというパッケージを使ってみました。
参考にしたzodの記事:https://go-to-k.hatenablog.com/entry/ts-cdk-zod-validation
zodの使い方は大変参考になったのですが、変数の格納状況をデバッグ機能を使って行いたいと思い調査しました。
未来の自分のためにも備忘録がてら調査した内容を本記事にてまとめようと思います。
早速結論
cdkのプロジェクトファイルに下記のjsonを入れ、任意の場所にブレークポイントを設定し、デバッグ実行押すだけです。
// .vscode/launch.json { "version": "0.2.0", "configurations": [ { "type": "pwa-node", "request": "launch", "name": "CDK Synth Debug", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ts-node", "args": [ "${workspaceFolder}/node_modules/.bin/cdk", "synth", "-c", "stage=Stg" ], "skipFiles": [ "<node_internals>/**" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } ] }
解説
configurations以下のパラメータについてまとめておきます。 重要なのはargsで指定するstage=Stgの部分です。
これをstage=ProdにすればProd用のパラメータを取得します。
フィールド名 | 説明 |
---|---|
type | デバッガのタイプを指定します。"pwa-node"はVS Codeの新しいJavaScriptデバッガを指します。 |
request | デバッグセッションのタイプを指定します。"launch"は新しいプログラムの起動を指します。 |
name | デバッグ設定の名前を指定します。これはVS Codeのデバッグドロップダウンメニューに表示されます。 |
runtimeExecutable | デバッグセッションを開始するために使用する実行可能ファイルを指定します。ここではts-nodeを指定しています。 |
args | 実行可能ファイルに渡す引数の配列を指定します。ここではCDKのsynthコマンドとその引数を指定しています。 |
skipFiles | デバッガがスキップするべきファイルパターンの配列を指定します。ここではNode.jsの内部モジュールをスキップしています。 |
console | デバッグ出力を表示するコンソールを指定します。"integratedTerminal"はVS Codeの統合ターミナルを指します。 |
internalConsoleOptions | デバッグセッションの開始時に内部コンソールを開くかどうかを指定します。"neverOpen"は内部コンソールを開かないことを指します。 |
サンプルを使って試す
まずはプロジェクトを作ります、ついでに私がバリデーションチェックで使ったzodというパッケージもインストールします。
mkdir debug-test; cd debug-test npx cdk init --language=typescript npm install zod mkdir -p lib/config mkdir -p lib/types mkdir .vscode touch .vscode/launch.json touch lib/config/config-stack-props.ts touch lib/types/stack-input-schema.ts touch lib/types/stack-validator.ts
今回のサンプルでは「npx cdk synth -c stage=Stg | Prod」といったコマンドでstageごとに異なるパラメータを取得してみます。
作成した空ファイルに以下のように記載します。
config定義ファイル
// lib/config/config-stack-props.ts import { StackProps } from "aws-cdk-lib"; import { StackInput } from "../types/stack-input-schema"; export interface ConfigStackProps extends StackProps { config: StackInput; } export const configStackProps: ConfigStackProps = { config: { Prefix: "hoge", Stg: { "$comment": "HogeProject", "Env": "stg" }, Prod: { "$comment": "HogeProject", "Env": "prod", } }, };
スキーマ定義ファイル
// lib/types/stack-input-schema.ts import { z } from "zod"; const StageSchema = z.object({ "$comment": z.string(), "Env": z.string(), }); export const StackInputSchema = z.object({ "Prefix": z.string(), "Stg": StageSchema, "Prod": StageSchema, }); export type StackInput = z.infer<typeof StackInputSchema>;
バリデーションファイル
// lib/types/stack-validator.ts import { IValidation } from "constructs"; import { StackInput, StackInputSchema } from "./stack-input-schema"; export class StackValidator implements IValidation { private stackInput: StackInput; constructor(stackInput: StackInput) { this.stackInput = stackInput; } public validate(): string[] { const errors: string[] = []; try { StackInputSchema.parse(this.stackInput); } catch (e) { errors.push(JSON.stringify(e)); } return errors; } }
Stackファイル
// lib/debug-test-stack.ts import { Construct } from 'constructs'; import * as cdk from 'aws-cdk-lib'; import { aws_s3 as s3 } from 'aws-cdk-lib'; import { StackInput } from "./types/stack-input-schema"; import { ConfigStackProps } from './config/config-stack-props'; import { StackValidator } from './types/stack-validator'; export class DebugTestStack extends cdk.Stack { private stackInput: StackInput; private init(props: ConfigStackProps): void { this.stackInput = props.config; const stackValidator = new StackValidator(this.stackInput); this.node.addValidation(stackValidator); } constructor(scope: Construct, id: string, props: ConfigStackProps) { super(scope, id, props); this.init(props); // Stg や Prod ごとの環境変数を取得 from config/config-stack-props const stagename: 'Stg' | 'Prod' = this.node.tryGetContext('stage') as 'Stg' | 'Prod'; const context = props.config[stagename]; // Prefix取得 const Prefix = props.config['Prefix']; // S3へのデプロイ内容を定義 const sourcebucket = new s3.Bucket(this, 'source',{ bucketName: `s3-${Prefix}-${context.Env}-src`, autoDeleteObjects: true, removalPolicy: cdk.RemovalPolicy.DESTROY }) } }
cdkエンドポイントファイル
// bin/debug-test.ts #!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { DebugTestStack } from '../lib/debug-test-stack'; import { configStackProps } from '../lib/config/config-stack-props'; // configStackPropsをインポート const app = new cdk.App(); new DebugTestStack(app, 'DebugTestStack', { config: configStackProps.config, // configStackPropsのconfigを使う });
※lunch.jsonは記事冒頭のjsonを貼り付けてください。
デバッグ実行
StackファイルのS3を作成する28行目にブレークポイントを設定して、早速F5で実行してみました。
正常にブレークポイントで止まり、デバッグコンソールにも狙い通りの変数が格納されていることがわかります。
また定義された変数以外にもnodeの状態も確認可能です。これでCDKがCFnを生成に至るまでの過程がわかるので、CDKの使いっぷりがイメージしやすくなるかなと思います。
まとめ
簡単でしたが、以上となります。
ステップ実行できる環境があると、開発の敷居が下がると思っています。
typescriptやったことないからCDKはちょっと・・・と思っている人に刺さればいいなぁと思います。