AWS CDKの基本と仕組みを初心者向けに解説

AWS CDK(Cloud Development Kit)の本質とは何か

AWSインフラの構築は、マネジメントコンソールでの手作業から設定ファイルの記述へ、そしてプログラミングコードによる定義へと進化しています。

このセクションでは、AWS CDKが一体何者なのか、その本質を3つの視点から紐解いていきます。

ソフトウェア開発のアプローチでインフラを定義するIaC

最近よく耳にする「IaC(Infrastructure as Code)」とは、サーバーやネットワークといったインフラの構成を、手作業ではなくコードとして記述して管理する考え方です。インフラ構成をテキストファイルに閉じ込めることで、同じ環境を何度でも正確に再現できるようになります。

AWSには以前から「CloudFormation」というIaCの仕組みが存在しています。しかし、CloudFormationはJSONやYAMLという形式で設定ファイルを書く必要があります。大きなシステムを構築する際、数千行に及ぶ設定ファイルを作成することになり、インデント(字下げ)を一つ間違えただけでエラーになるなど、管理が困難になります。

ここで登場するのがAWS CDKです。CDKは、CloudFormationが抱えていた「設定ファイルの記述の辛さ」を根本から解決するために生まれました。YAMLの代わりに、TypeScriptやPython、Javaといった「プログラミング言語」を使ってインフラを定義します。

TypeScriptを使えば、変数を使ったり、関数にまとめたりと、普段アプリケーションを開発しているのと同じ感覚でインフラのコードを書くことができます。これにより、インフラ構築は設定ファイルを記述する作業から、ソフトウェアを開発する作業へとシフトしました。

宣言型(CloudFormation)と命令型(CDK)のパラダイムの違い

CDKの本質を理解する上で、押さえておきたいのが「宣言型」と「命令型」というパラダイムの違いです。

CloudFormationなどの従来的なIaCツールは「宣言型」です。これは、「最終的にこうなってほしい」という結果だけを記述するスタイルです。例えばレストランで「ハンバーガーセットをお願いします」と注文するようなイメージで、厨房(AWS)がどうやってハンバーガーを作るかは意識せず、完成品を得られます。

一方、CDKで書くコードは「命令型」です。これは、「こうやって作って」という手順やロジックを記述するスタイルです。料理のレシピで「パンを焼き、肉を焼き、ケチャップを塗って重ねて」と指示するように、処理の流れを記述します。

CDKは、「命令型のコードを実行した結果として、宣言型の設定ファイル(YAML)を自動生成する」仕組みです。CDKはプログラミング言語で書かれたコードを解釈し、CloudFormation用のテンプレートを出力する「コンパイラ」として機能します。私たちが書くTypeScriptのコード(命令型のロジック)はAWSに直接読み込まれるわけではなく、CDKによってコンパイルされ、最終的にはCloudFormationのテンプレート(宣言型の定義)として出力されます。

Important

CDKのコードはAWSに直接送られるわけではありません。あくまで「CloudFormationテンプレートを生成するためのコード」であり、最終的にAWSに適用されるのは生成されたテンプレートです。

つまり、開発者はプログラミング言語の柔軟な表現力(命令型)を活用しつつ、結果として信頼性の高い宣言型の定義を生成してAWSに適用できるという、非常に合理的な設計になっています。

AWS CDKの背後にある思想と目的

なぜAWSは、プログラミング言語でインフラを書けるようにしたのでしょうか。そこには、「インフラとアプリケーションの境界をなくす」という思想があります。

これまでのシステム開発では、「インフラ担当」と「開発担当」が分かれていることが多く、インフラの準備が終わらないとアプリが動かせないといった壁が存在していました。

CDKは、開発者が普段使っている言語でインフラが書けるため、アプリケーションのコードとインフラのコードを同じリポジトリ(保存場所)で管理できます。バックエンドエンジニアが「APIの処理を追加したから、裏側のデータベースのテーブルもこのコードに足しておこう」といったように、アプリとインフラを一体として開発できるようになります。

また、AWSのサービスは非常に多機能であり、セキュリティの細かい設定やベストプラクティスをいちいち調べて設定するのは大変です。CDKには、そうした「面倒だけど本来やるべき設定」をあらかじめ適用してくれる仕組みが組み込まれています。

つまりCDKの目的とは、単に「YAMLを書かなくて済むツール」ではなく、「開発者がインフラの複雑さから解放され、本来やりたいアプリケーション開発に集中できるようにするための枠組み」を提供することです。

AWS CDKの核となる「コンストラクト」の考え方

クラウドリソースを表現する基本構成要素

CDKを語る上で絶対に外せないのが「コンストラクト(Construct)」という考え方です。これは、クラウドリソースを表現するレゴブロックのような部品です。

1つのコンストラクトが「S3バケット」であったり「DynamoDBのテーブル」であったりします。CDKでは、このコンストラクトを組み立てていくことで、最終的なインフラ全体を形作っていきます。

技術的には、コンストラクトはCloudFormationでリソースを作成するための設定を「カプセル化(複雑な内部を隠蔽し、外から使いやすくまとめること)」したものです。

コード例は以下の通りです。

new s3.Bucket(this, 'MyBucket', {
  versioned: true,
  removalPolicy: RemovalPolicy.DESTROY
});

この数行を書くだけで、「バージョニングが有効で、削除時に完全に消去されるS3バケット」が定義できます。裏側では何十行ものCloudFormationテンプレートが生成されますが、それを意識する必要はありません。

コンストラクトには親子関係があります。S3バケットというコンストラクトを、アプリケーション全体を表す大きなコンストラクトの中に配置するイメージです。この階層構造(ツリー構造)によって、何十、何百というリソースからなる複雑なインフラでも整理して管理できるようになっています。

AWS CDKが採用する3つの抽象化レイヤー(L1, L2, L3)

コンストラクトには、3つの「抽象化レベル」が用意されています。レベルが上がるほど、詳細を隠して使いやすくなります。

L1(CloudFormation直結レイヤー)

一番下のレイヤーです。クラス名が「Cfn」で始まるのが特徴で、例えばS3バケットなら「CfnBucket」になります。CloudFormationのプロパティとほぼ1対1で対応しているため、AWSのドキュメントを見ながら直接設定値を細かく指定したい時に使います。柔軟性は高いですが、設定項目が多くなります。

L2(便利なデフォルト設定レイヤー)

CDKの中核をなすレイヤーです。「s3.Bucket」のように書くとこれに該当します。L1と違い「何も指定しなくても、AWSのベストプラクティスに沿った設定が入る」のが最大の特徴です。

例えば、S3バケットをL2で作ると、デフォルトでバケット名が自動生成されます。また、セキュリティ的にも推奨される暗号化設定などが最初から入っていることが多いです。多くのユースケースでは、このL2を使っておけば間違いありません。

L3(完成されたパターンレイヤー)

一番上のレイヤーで、特定のユースケースに特化した完成品です。例えば「aws-ecs-patterns」にある「ApplicationLoadBalancedFargateService」は、ALB + Fargate + CloudWatchアラームなどを一気に構築してくれます。数十行のL1/L2コードを書くところを、数行で済ませることができます。

ただし、L3は対応しているパターンが限られており、裏側の設定をカスタマイズしづらい側面もあります。

Note

L3コンストラクトは便利ですが、要件がパターンから外れるとL2に戻る必要があります。最初からL3に依存しすぎず、用途に合うか事前に確認しましょう。

使い分けの考え方

Tip

基本はL2を使い、L3で該当するパターンがあればそれを使います。「どうしてもこの設定を変えたい」という時だけL1に降りるのが現実的なアプローチです。目的に応じて適切なレイヤーを選択するのがCDKの基本的な使い方です。

再利用と共有による開発の高速化

コンストラクトの利点は、一度作ったものを簡単に再利用できる点です。

自社のセキュリティ要件を満たした「S3バケット」「VPC」「Lambda関数」のコンストラクトを、1つのパッケージとしてまとめることができます。これをnpm(Node.jsのパッケージ管理システム)や、チーム内のプライベートリポジトリに公開すれば、他のプロジェクトやチームでも利用可能です。

例えば、Aチームが「セキュアなAPI構築用コンストラクト」を作ったとします。これには「API Gateway + Lambda + CloudWatchログの設定 + セキュリティヘッダーの設定」などが含まれています。Bチームはこのパッケージをインストールして、数行書くだけで同じセキュアなAPI環境を構築できます。

import { SecureApiConstruct } from '@mycompany/cdk-secure-api';

new SecureApiConstruct(this, 'MyApi', {
  stage: 'production'
});

こうすることで、組織全体でインフラ構築のベストプラクティスが共有され、「各チームがバラバラの設定で構築して、後からセキュリティ監査で指摘される」という事態を防げます。

また、AWS公式やコミュニティも多くのコンストラクトを公開しています。「aws-cdk-lib」自体がAWS公式のコンストラクトライブラリであり、「Construct Hub」という公式の共有プラットフォームもあり、世界中のエンジニアが作ったコンストラクトを探して利用できます。

インフラ構築を「ゼロから書く」ものから「良い部品を組み立てる」ものへとシフトさせるのが、コンストラクトの本質です。

プログラミング言語を使うことによる論理的なメリット

プログラミング言語を用いることで、YAMLやJSONといった設定ファイル形式と比較して論理的な面で大きなメリットがあります。

条件分岐やループなどプログラミングの表現力活用

従来の設定ファイルでは、似たようなコードをコピペして一部を書き換える作業が発生しがちです。複数のアベイラビリティゾーン(AZ)にサブネットを作成する際、YAMLだと同様のブロックが複数並び、本番環境と開発環境で設定を分ける場合も管理が煩雑になります。

CDKでは、これがプログラミングの基本的な機能で解決します。forループを使えば、複数のサブネット生成を数行のコードで一括生成できます。また、「デプロイ先が本番環境だったらインスタンスタイプを大きくする」といった条件分岐も、if文で直感的に記述できます。

これにより、ソフトウェア開発における「DRY(Don’t Repeat Yourself:同じことを繰り返すな)」原則をインフラ構築にも適用でき、コードの重複を徹底的に排除できます。

IDEの補完や型チェックによるヒューマンエラーの削減

設定ファイルによるインフラ定義の弱点は、デプロイするまでミスに気づけない点です。プロパティの名前を打ち間違えたり、必須項目を書き忘れたりしても、エディタ上では警告が出ないことが多く、記述量が増えるとデプロイ時のエラー原因を特定するのが困難になります。

CDKではTypeScriptやPythonといった言語を使うため、Visual Studio Codeなどの強力な統合開発環境(IDE)の機能をフル活用できます。入力途中に候補を表示してくれる入力補完や、スペルミスや設定漏れを赤い波線で指摘してくれる静的型チェックが利用可能です。

Tip

CDKでTypeScriptを選ぶ最大の理由は、型定義による入力補完の恩恵が最も大きいためです。AWS CDK自体もTypeScriptで書かれており、型サポートの面で最も相性が良いです。

これにより、デプロイするずっと前の段階でヒューマンエラーを未然に防ぐことができます。

アプリケーションコードとインフラコードの言語統一

アプリケーションとインフラで異なる言語や形式を扱うと、無意識のうちに脳の切り替え(コンテキストスイッチ)が発生し、集中力が削がれがちです。

CDKを使えば、アプリケーションの本体も、それを動かすためのインフラ定義も、同じTypeScriptやPythonで書くことができます。同じGitリポジトリの中に「アプリのコード」と「インフラのコード」を一緒に置いて管理することも自然にできるようになります。「アプリのコードを変えたので、それに合わせてインフラのコードも修正して、一気にデプロイする」という流れがシームレスに実行できるのです。言語の統一によりインフラとアプリケーションの境界がなくなることは、DevOpsの実践を促進する大きなメリットです。

AWS CDKの変換とデプロイの仕組み

プログラミング言語で記述されたコードが、どのようにAWSでプロビジョニングされるのかを理解することは重要です。CDKの裏側では、コードからAWSリソースを作成するための独自の仕組みが動いています。

コードからCloudFormationテンプレートを生成(合成)する流れ

CDKが裏側で行っていることは、「プログラミング言語の変換作業」です。

CDKは、AWSが提供しているインフラ構築サービスである「CloudFormation」の上位レイヤーとして機能します。開発者がCDKでコードを書くと、CDKはそれをAWSが直接理解できるCloudFormationのテンプレート形式(YAMLまたはJSON)に変換します。この変換作業を、CDKの用語で「合成(Synthesize)」と呼びます。

例えば、数行〜数十行のCDKコードから、数百行に及ぶCloudFormationテンプレートが一瞬で正確に生成されます。開発者は直接テンプレートを記述するのではなく、プログラムに生成させるアプローチをとります。

CloudFormationを介した安全で予測可能なプロビジョニング

合成されて作られたテンプレートは、次のステップで実際のAWSリソースとして構築(プロビジョニング)されます。ここで重要なのは、CDKが直接リソースを作るわけではなく、受け渡し先が「CloudFormation」であるということです。

CloudFormationは、AWSのインフラを自動構築する長年の実績があるマネージドサービスです。CDKはCloudFormationの機能を利用しているため、その高い信頼性を受け継いでいます。

例えば、デプロイの途中でエラーが起きた場合、CloudFormationは途中まで作成したリソースを自動的に削除し、元の状態に戻す**「ロールバック機能」を持っています。また、本番環境に反映する前に変更内容を事前にプレビューできる「変更セット」**という機能もあります。CDK経由であってもこれらの安全機能は一切損なわれず、CloudFormationに守られた安全なデプロイが可能です。

Warning

CloudFormationのロールバックは便利ですが、途中で失敗したリソースの一部が残ってしまうことがあります。本番環境では必ず変更セット(cdk diff)で事前確認を習慣づけましょう。

AWS CDK Toolkit(CLI)の役割

これらの変換からデプロイまでの一連の流れは、手動で行う必要はありません。CDKには「AWS CDK Toolkit」と呼ばれるコマンドラインツール(CLI)が用意されているからです。

最もよく使うのが、テンプレート生成の cdk synth と、実際のデプロイを実行する cdk deploy です。cdk deploy を実行すると、Toolkitは裏側で自動的に合成処理を行い、生成されたテンプレートをCloudFormationに送信し、リソースの構築が完了するまで待機してくれます。

開発者はcdk deployコマンドを実行するだけで、テンプレートの詳細な記述や適用手順を意識することなく、クラウド環境を構築できます。

AWS CDKを活用するための設計とベストプラクティス

ここでは、実際のプロジェクトでCDKを導入し、組織で運用していくための設計思想とベストプラクティスを解説します。

ソフトウェアエンジニアリングの原則の適用

CDKの大きな魅力の一つは、インフラの定義を「設定ファイル」から「ソフトウェアのコード」へと昇華させた点にあります。つまり、これまでアプリケーション開発で培ってきたベストプラクティスを、インフラ構築にそのまま持ち込めるようになります。

具体的には、まずバージョン管理の導入です。Gitを利用することで、「誰が、いつ、どんな理由でインフラを変更したのか」をすべて履歴として追えるようになります。これにより、設定の意図がブラックボックスになることを防げます。

次に、コードレビューの精度向上です。YAMLやJSONのような設定ファイルだと変更の意図を読み取るのが難しく、レビューが形骸化しがちです。しかし、論理的な構造を持ったプログラミング言語であれば、レビュアーも処理の分岐などの意図を汲み取りやすくなります。

さらに重要なのが、ユニットテストの導入です。「作成したS3バケットに、本当にバージョニングが設定されているか」「セキュリティグループのポート番号は正しいか」といった検証を、デプロイする前に自動で行えます。Jestなどのテストフレームワークを使い、cdk synthで生成されたテンプレートが意図した構成(スナップショット)と一致しているかをテストすることで、デプロイ時の設定ミスを未然に防ぐことができます。

Caution

テストを書かずにCDKを運用すると、インフラの変更が意図しない副作用を起こすリスクが高まります。特に本番環境では、スナップショットテストによる変更検知が必須です。

Aspects機能を利用した組織のガイドライン遵守

組織が成長し、扱うスタック(AWSリソースのまとまり)が増大すると、「すべてのリソースに必須のタグ付けをする」「パブリックなS3バケットは絶対に作らない」といったルールを、人間の目だけで徹底するのは困難になります。

そこで活躍するのが、CDKの「Aspects(アスペクト)」という機能です。Aspectsは、CDKのコンストラクトツリーを自動的に走査(トラバース)し、条件に合致するリソースに対して一律な処理を施す仕組みです。

例えば、すべてのリソースに「Environment」タグを付与したい場合、Aspectsを使うと以下のように簡潔に記述できます。

import { Aspects, Tag } from 'aws-cdk-lib';

// スタック内のすべてのリソースにタグを一括付与する
Aspects.of(myStack).add(new Tag('Environment', 'Production'));

各スタックの中でいちいちタグを記述するのではなく、アプリケーションの最上位の部分にこれを記述するだけで、タグ付け漏れを完全にゼロにできます。

さらに、ルールの強制(ガバナンス)にも使えます。例えば「パブリックアクセスが有効なS3バケットが見つかったら、デプロイをエラーにして止める」といった検証ロジックをAspectsに組み込んでおけば、ルールに違反する変更はデプロイの段階で機械的に弾かれます。100個のスタックがあっても、Aspectsの定義を1箇所変えるだけで全社ルールを適用できるため、大規模な環境になるほどその真価を発揮します。

プロジェクトでの導入に向けた設計のポイント

いざ自分のプロジェクトにCDKを導入する際の現実的なアプローチを解説します。

まず重要なのが、L1からL3のどの抽象化レイヤーを主力にするかの見極めです。L1(CloudFormationの直訳レイヤー)は設定項目が多くなりがちなので、基本的にはL2(便利なデフォルト設定付き)を主力として使うのがおすすめです。例えばL2のVPCコンストラクトを使えば、パブリックサブネットやプライベートサブネットの組み合わせといった面倒な設計を、AWSのベストプラクティスに沿って自動で行ってくれます。

プロジェクトが成熟してきたら、「自社特有のL3」を作ることも検討してください。「うちの会社のWebシステムは、必ずこのVPC構成とALBとFargateのセットが必要」というよく使うパターンを、1つのL3コンストラクトとして包んでしまうのです。これにより、新しいプロジェクトが立ち上がるたびのコード記述量が大きく削減されます。

もう一つ、現場でよく直面するのが「既存のインフラとの共存」の問題です。今動いているシステムをCDKに移行する際、すべて作り直す必要はありません。CDKには、既存のAWSリソースをコード上にインポートする仕組みがあります。これを使えば、既存のVPCやデータベースを参照しつつ、新しく作る部分だけをCDKで書くというハイブリッドな運用が可能です。

Note

既存リソースをCDKにインポートする方法はいくつかあります。代表的なのが、リソースの属性(ARNやID)を参照するFn.importValueや、CDKのfromLookupメソッドです。移行の際は既存リソースの識別子を正確に把握しておく必要があります。

導入のステップとしては、最初から全社的に導入するのではなく、「開発環境の新規機能から少しだけCDKを使ってみる」といった小さな一歩から始めるのが確実です。コードレビューによるミスの検出や、テストによる安全なデプロイといった成功体験をチーム内で共有することで、自然とCDKの利用範囲を広げていくことができます。

まとめ

AWS CDKは、インフラ構築をプログラミング言語の記述によって行える強力なツールです。命令型のコードから宣言型のCloudFormationテンプレートを自動生成するコンパイラとして機能し、CloudFormationの強固な信頼性を保ちながら、プログラミング言語ならではの柔軟性と利便性をもたらします。

コンストラクト(L1〜L3)を活用してインフラを部品化することで、複雑な構成の再利用性が高まります。さらに、IDEのサポートやテストの導入、Aspectsを活用したガバナンスの徹底により、大規模なシステムでも安全かつ効率的な開発が可能になります。

インフラとアプリケーションの境界をなくし、DevOpsの実践を強力に推進するAWS CDKは、現代のクラウド開発において欠かせない存在です。ぜひ実際のコード記述を通じて、その開発体験を実感してみてください。