はじめに
この記事は AWS CDK Advent Calendar 2023 23日目 の記事です。
概要
AWS CDKは、AWS公式のIaC(Infrastructure as Code)ツールということもあり、AWSでIaCを使ったインフラ構成管理を行いたい場合にお薦めで、プログラミング言語(TypeScript, Pythonなど)でコードを記載するので、アプリエンジニアにもおすすめのツールです。
その一方で「コードと違う挙動をする」と感じることがある方も多く、特に「エラーハンドリングが効かない!」と感じた人がかなり多いようです。(私はJAWS-UG CDK支部に結構関わらせてもらっていますが、実際その中でそういう話を聞くことが多いです)
そこで今回なぜ「AWS CDKでエラーハンドリングができないのか」の仕組みを記載したいと思います。
具体例
例えば、下記のコードです。
下記のコードでは、以下の挙動を想定しています。
- パラメータストアからDockerイメージのイメージタグを取得する
- 上記が未設定の場合エラーになるので、その際は固定イメージを設定する
- 上記イメージタグのイメージからLambda関数を参照する
// ※注意!このコードは正しく動きません import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Repository } from 'aws-cdk-lib/aws-ecr'; import { aws_lambda as lambda } from "aws-cdk-lib" let imageTag = ''; try { // パラメータストアから最新Dockerイメージのイメージタグを取得 imageTag= StringParameter.valueForStringParameter(this, 'IMAGE_TAG'); } catch (e) { // もし未設定だったらエラーになるので、その際は固定値を設定 imageTag = 'initial'; } const repo = new Repository(this, 'EcrRepo'); new lambda.DockerImageFunction(this, 'AssetFunction', { code: lambda.DockerImageCode.fromEcr(repo, { // 上記イメージタグのイメージからLambda関数を参照する tagOrDigest: imageTag, }), });
しかし実際にはパラメータストアにイメージタグがない場合、deploy時に「パラメータストアに該当のキーが存在しない」エラーで強制終了してしまいます。
「ちゃんとtry~catchで判定しているのになんでだ!」となってしまうわけです。
AWS CDKの挙動を理解する
まずはAWS CDKの挙動について。
AWS CDKの挙動は大きく分けて、以下の2つです。(もちろん、正確にはもっとたくさんある)
合成(Synthesize、以下「synth」)
- AWS CDKのソースコード(Constructsなど)から、CloudFormationテンプレートファイル(json/yaml)を作成する
- CLIコマンドの
cdk synth
に該当- 正確には
cdk synthesize
コマンドだが、大抵省略形で書く
- 正確には
- プログラミング言語の制御が効く(if文とか)
展開(Deploy、以下「deploy」)
- synthで作成したCloudFormationテンプレートファイルを使用して、CloudFormationにデプロイを行う
- CLIコマンドの
cdk deploy
に近い(同じではない)cdk deploy
はsynthの処理も一緒に行うため(synth→deploy)
- プログラミング言語の制御が効かない
これを踏まえて、synth時とdeploy時で「なぜエラーハンドリングが効かないのか」を説明します。
synthでエラーハンドリングが効かない理由
まず前提として、synth時にはAWSへのアクセスは行われません。(ファイル変換のみ)
したがって、各種リソースのプロパティ(ARNなど)の取得も行われません。
じゃあARNなどが設定されている箇所ではどうしているかというと、「トークン(Token)」という一時的な値に置き換えられてます。
なのでこの「トークン」について説明します。
トークンを理解する
トークンについては、AWS CDKの公式リファレンスにも載っています。 docs.aws.amazon.com
Represents a special or lazily-evaluated value.
Can be used to delay evaluation of a certain value in case, for example, that it requires some context or late-bound data. Can also be used to mark values that need special processing at document rendering time.
Tokens can be embedded into strings while retaining their original semantics.
訳:
特別な、あるいは遅延評価された値を表す。
例えば、ある値がコンテキストや遅延バウンドデータを必要とする場合に、その値の評価を遅延させるために使用することができます。また、ドキュメントのレンダリング時に特別な処理が必要な値をマークするために使用することもできます。
トークンは、元のセマンティクスを保持したまま文字列に埋め込むことができます。
要はARNのような「deployしないとわからない」ような値について、一時的に仮の値を設定するために使用されているのがトークンです。
synthでエラーハンドリングが効かない理由(結論)
そしてこのトークンは「Token[...]
」のような文字列値になっています。(console.log
などで出力すると分かります)
ここがポイントで、例えば「具体例」のケースだと、StringParameter.valueForStringParameter()
の戻り値として 「Token[...]
」 という文字列が返却されるため、特にエラーにはなりません。
したがって、当然エラーハンドリングも行われません。
説明が長くなりましたが、これが「synthでエラーハンドリングが効かない理由」です。
deployでエラーハンドリングが効かない理由
次にdeploy時にエラーハンドリングが効かない理由ですが、これは単に「jsonやyamlファイルにエラーハンドリングなんて機構がないから」になります。
「AWS CDKの挙動」で説明した通り、deployはCloudFormationテンプレートファイル(json/yaml)を使用します。
当然jsonやyamlにエラーハンドリングなんて機構がない(そもそもプログラミングコードではない)ため、deploy時にエラーが発生しても、エラーハンドリングなんてできず、結果としてエラーで終了します。
と、ここまで長々とCDKでエラーハンドリングが効かない理由を記載しましたが「CDKでは、AWSリソースの有無で処理を分岐することはできない」と考えておくとよいかもしれません。(100%断定はできませんが...)
AWSリソースの有無で処理を分岐する方法
といっても、実際は「AWSリソースの有無で処理を分岐したい」というケースがあると思います。
その場合に一番手っ取り早いのが「AWS CLIを使う方法」です。(具体例は下記ソース参照)
// 「具体例」と同じimport分は省略 import { execSync } from 'child_process'; const getParameterStoreValue = (keyName: string, region: string): string => { const command = `aws ssm get-parameter --name ${keyName} --region ${region} --query "Parameter.Value" --output text`; try { const value = execSync(command).toString().trim(); return value; } catch (error) { return 'initial'; } }; const imageTag = getParameterStoreValue('IMAGE_TAG', 'ap-northeast-1'); // あとは同じなので省略
これならAWS CDKの外部での処理なので、ちゃんとリソースの有無で判定できますし、エラーハンドリングも効きます。
他にもやり方はあると思いますが、一例として。
まとめ
以上、AWS CDKでエラーハンドリングができない理由でした。
といっても、理由さえわかってしまえばそこまで難しい話ではないので、色々対処は出来ると思います。
AWS CDK、慣れると使いやすくて色々便利ですし、何より現在でも頻繁にアップデートがされている便利なツールですので、ぜひ導入してインフラ管理を便利なものにしてみてください。
それでは、今回はこの辺で。