AWS CDK のローカル開発環境を作ろう!

2024-11-01
デベロッパーのためのクラウド活用方法

Author : 木村 友則

みなさん、こんにちは ! AWS の Solutions Architect 木村 友則 (@tkimurz) です。

Infrastructure as Code (IaC) を活用していますか ? AWS Cloud Development Kit (CDK) を使ってみたことはありますか ? 本記事では、まだ AWS CDK の利用経験がない方に向けて、AWS CDK のローカル開発環境の構築や基本的な利用方法をご紹介します。

IaC や CDK について、キーワードは聞いたことがあるけど詳しい内容まではわからないという方は、こちらの「使い慣れたプログラミング言語でクラウド環境を構築 ! AWS CDK をグラレコで解説」という記事を事前にご覧ください。IaC や CDK について全体像をご理解いただくのに最適な記事となっています。

IaC や CDK に興味があっても、環境構築が大変そうだと感じられて二の足を踏んでいる方もいるのではないでしょうか ? 以降では、CDK の環境構築が実はとても簡単にできること、統合開発環境 (IDE) を使った CDK の開発体験の良さ、AWS Lambda 関数を例にしたローカル開発環境でのコード作成とデプロイの流れを、次の項目に分けてご説明します。ぜひ、手を動かしながら体験してみてください !

この記事のデモを無料でお試しいただけます »

毎月提供されるデベロッパー向けアップデート情報とともに、クレジットコードを受け取ることができます。 


1. 必要なものを準備しよう

AWS CDK を利用するには、AWS CLI、Node.js (アクティブな LTS バージョンを推奨)、AWS CDK Toolkit、AWS 認証情報が必要です。前提条件が整っていれば、使用している OS に関係なく実行することが可能です。図は本記事で構築するローカル開発環境とクラウドにあるデプロイ先の関係を表したものです。

インストール方法の一例として、macOS を題材にしてパッケージマネージャーとして広く利用されている Homebrew を利用した手順を紹介します。AWS CDK Toolkit については、グローバルインストールせず、npx コマンドを介して利用することにして、ここではインストールしていません。

brew install awscli
brew install node@20

# node@20 インストール後のメッセージに従い、PATH設定を追加します。 
# 設定の追記先は、みなさんの環境に合わせて変更してください。
# 再ログインなどで反映する必要があります。
echo 'export PATH="/opt/homebrew/opt/node@20/bin:$PATH"' >> ~/.zshrc

Windows や Linux の場合も、AWS CLIの公式ドキュメントNode.jsの公式Webサイト に記載されている手順に限らず、みなさんが普段利用している環境管理の手段を用いて、必要なツールをご用意いただければ問題ありません。

この記事の環境は、以下のバージョンで検証しています。

$ aws --version
aws-cli/2.17.61 Python/3.12.6 Darwin/23.6.0 source/arm64

$ node -v
v20.17.0

$ npx cdk --version
2.160.0 (build 7a8ae02)

お手元の環境で実行する場合は、こちらのコマンドをコピーアンドペーストしてください。

aws --version
node -v
npx cdk --version

ローカルマシンから AWS アカウント環境に接続するために AWS 認証情報を利用します。設定には いくつかの方法 がありますが、その中から AWS IAM Identity Center (SSO) を使用する方法をご紹介します。

AWS IAM Identity Center (SSO) を使用した場合、長期的に有効なアクセスキーではなく一時的な認証情報を使用することができます。AWS CLI を使用して次のように設定します。設定に指定する値が不明な場合は、AWS IAM Identity Center の管理者にご相談してください。

aws configure sso

以下、実行例です。

$ aws configure sso
SSO session name (Recommended): my-sso
SSO start URL [None]: https://my-sso-portal.awsapps.com/start
SSO region [None]: us-east-1
SSO registration scopes [None]: sso:account:access

注: 設定値はすべてサンプルの値です

2. 作業ディレクトリを作ろう

本記事では、次のように hello-cdk ディレクトリを作成して作業しています。カレントディレクトリ名は自動生成されるファイル名に影響を与えます。以降の手順で編集対象のファイルを迷わないように、手順に沿って hello-cdk というディレクトリを作成し、そこで作業を行ってください。

pwd
mkdir hello-cdk && cd hello-cdk
pwd

以下、実行結果の出力イメージです。

$ pwd
/<任意の作業ディレクトリ>

$ mkdir hello-cdk && cd hello-cdk

$ pwd
/<任意の作業ディレクトリ>/hello-cdk

3. 快適な開発環境を整えよう

AWS CDK の開発では、テキストファイルの編集やコマンド操作さえできれば十分なので、シンプルなエディタとターミナルだけでも十分に開発は可能です。しかし快適な開発体験のためには、各⾔語のコード補完やリファクタリング機能を持つツールの利⽤を強く推奨したいです。本記事では、統合開発環境 (IDE) として Visual Studio Code、IntelliJ IDEA の場合を取り上げています。ご利用中の統合開発環境ごとにツールキットが異なりますので、利用する環境に合わせて 選択してください。

  • Visual Studio Code
    1. Visual Studio Code の拡張機能タブで「AWS」と入力して検索します。
    2. AWS Toolkit」を選択してインストールします。
    3. 必要に応じて、対象となる開発言語用のプラグインを追加しておきます 。

    クリックすると拡大します

  • IntelliJ IDEA
    1. IntelliJ IDEA のプラグインメニューから「AWS」と入力して検索します。
    2. AWS Toolkit」を選択してインストールします。
    3. 次に表示されるダイアログで利用に同意します。
    4. 必要に応じて、対象となる開発言語用のプラグインを追加しておきます 。

    クリックすると拡大します


4. CDK プロジェクトを作ろう

ここまでの手順では、AWS CDK のローカル開発環境ができました ! ここからは、実際に CDK で AWS リソースの定義を行っていきます。実際に試してみることで、CDK がどのようなしくみで動いているかを理解できます。

IDE で 作業ディレクトリ を開き、プロジェクトを新規作成します。本記事では、CDK を TypeScript で記述するため、オプションに --language=typescript を付与しています。

本記事では、CDK・Lambdaのコードを、TypeScript 言語で記述しています。コードはすべて、公式ドキュメントのチュートリアル (記事執筆した2024年10月時点のもの) に掲載されているものを利用しており、実行手順も概ねチュートリアルに沿っています。TypeScript 言語以外にも、JavaScript/Python/Java/C#/Go の例がありますので、お好みの言語でお試しいただくことが可能です。

コマンドは必ず作業ディレクトリに移動して実行してください。

npx cdk init app --language=typescript

クリックすると拡大します

AWS 環境を ブートストラップ します。この操作によって、「CDKToolkit」スタックが作成され、CDK 専用の Amazon S3 バケットや IAM ロールなどの AWS CDK の動作に必要なリソースが用意されます。

npx cdk bootstrap

CDK アプリに含まれるスタックを確認します。この時点で、プロジェクト作成と同時に作成されたスタックが一つ表示されます。この表示がされない場合、作業ディレクトリ (hello-cdk の中) が正しいことを確認して、再実行してみてください。

npx cdk list

以下、npx cdk list の実行例です。

$ npx cdk list
HelloCdkStack

5. Lambda 関数を作ってみよう

lib/hello-cdk-stack.ts を変更します。IDE でファイルを開き、変更後のコードをコピー&ペーストで貼り付けてみてください。Visual Studio Code 上でファイルを編集している様子のスクリーンショットを貼付します。変更箇所 (行頭が青い) がわかりやすく表示されています。

クリックすると拡大します

変更前のコード

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';

export class HelloCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here

    // example resource
    // const queue = new sqs.Queue(this, 'HelloCdkQueue', {
    //   visibilityTimeout: cdk.Duration.seconds(300)
    // });
  }
}

変更後のコード

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// Import the Lambda module
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class HelloCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Define the Lambda function resource
    const myFunction = new lambda.Function(this, "HelloWorldFunction", {
      runtime: lambda.Runtime.NODEJS_20_X, // Provide any supported Node.js runtime
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello World!'),
          };
        };
      `),
    });
  }
}
  • aws-cdk-lib/aws-lambda を import
  • myFunction の定義を追加 (コメント「Define the Lambda function resource」以下)

本記事では、チュートリアル手順に沿ってインラインで Lambda 関数のコードを記載できる fromInline を使用していますが、もちろん Lambda 関数を別ファイルとして用意することも可能です。その場合は、例えば NodejsFunction を使う方法があります。また NodejsFunction を使用する例は、本記事まとめの Next Step でも紹介している、AWS CDK Immersion Day ワークショップ のなかでも実例を使って体験していただくことができます。


6. Lambda 関数を https でアクセスできるようにしよう

先ほどのファイルに Function URLs の定義と Outputs の定義を追加します。ここは記述内容も少ないので、コピー & ペーストではなく、実際に入力して、IDE による入力候補の提案を体験してください。Visual Studio Code のスクリーンショットのように入力候補の提案を行う様子が確認できます。

クリックすると拡大します

変更後のコード

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// Import the Lambda module
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class HelloCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Define the Lambda function resource
    const myFunction = new lambda.Function(this, "HelloWorldFunction", {
      runtime: lambda.Runtime.NODEJS_20_X, // Provide any supported Node.js runtime
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello World!'),
          };
        };
      `),
    });

    // 追記 ここから
    // Define the Lambda function URL resource
    const myFunctionUrl = myFunction.addFunctionUrl({
      authType: lambda.FunctionUrlAuthType.NONE,
    });

    // Define a CloudFormation output for your URL
    new cdk.CfnOutput(this, "myFunctionUrlOutput", {
      value: myFunctionUrl.url,
    })
    // 追記 ここまで
  }
}
  • myFunctionUrl を定義を追加 (コメント「Define the Lambda function URL resource」以下)
  • CloudFormation の Output を定義を追加 (コメント「Define a CloudFormation output for your URL」以下)

7. 実際に AWS にデプロイしてみよう

CloudFormation テンプレートを合成します。

npx cdk synth

デプロイを実行します。

npx cdk deploy

以下、npx cdk deploy の実行例です。

$ npx cdk deploy

✨  Synthesis time: 2.8s

HelloCdkStack:  start: Building e0feffbc0459e446d7e99aff15115db658baa95db82957e475bfd28e84508237:current_account-current_region
HelloCdkStack:  success: Built e0feffbc0459e446d7e99aff15115db658baa95db82957e475bfd28e84508237:current_account-current_region
HelloCdkStack:  start: Publishing e0feffbc0459e446d7e99aff15115db658baa95db82957e475bfd28e84508237:current_account-current_region
HelloCdkStack:  success: Published e0feffbc0459e446d7e99aff15115db658baa95db82957e475bfd28e84508237:current_account-current_region
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬───────────────────────────────┬────────┬───────────────────────────────┬────────────────────────────────┬───────────┐
│   │ Resource                      │ Effect │ Action                        │ Principal                      │ Condition │
├───┼───────────────────────────────┼────────┼───────────────────────────────┼────────────────────────────────┼───────────┤
│ + │ ${HelloWorldFunction.Arn}     │ Allow  │ lambda:InvokeFunctionUrl      │ *                              │           │
├───┼───────────────────────────────┼────────┼───────────────────────────────┼────────────────────────────────┼───────────┤
│ + │ ${HelloWorldFunction/ServiceR │ Allow  │ sts:AssumeRole                │ Service:lambda.amazonaws.com   │           │
│   │ ole.Arn}                      │        │                               │                                │           │
└───┴───────────────────────────────┴────────┴───────────────────────────────┴────────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬───────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                          │ Managed Policy ARN                                                             │
├───┼───────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloWorldFunction/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
└───┴───────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
HelloCdkStack: deploying... [1/1]
HelloCdkStack: creating CloudFormation changeset...

 ✅  HelloCdkStack

✨  Deployment time: 40.91s

Outputs:
HelloCdkStack.myFunctionUrlOutput = https://<url-id>.lambda-url.<region>.on.aws/
Stack ARN:
arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/HelloCdkStack/75e39360-7dc6-11ef-b1a8-0affd4860f5b

✨  Total time: 43.71s
  • 最後の Outputs のなかにある myFunctionUrlOutput の値が、Lambda 関数 の Function URLs で作成されたエンドポイントです。
  • 記事中では、myFunctionUrlOutput および Stack ARN は、内容の一部をマスキングしています。

デプロイした Lambda 関数を実行します。curl に指定するURLは、実際の myFunctionUrlOutput の値を指定してください。以降の確認手順も同じです。

curl コマンドが実行できない場合は、任意のブラウザで開いてください。

curl https://<url-id>.lambda-url.<region>.on.aws/

8. Lambda 関数を変更して再デプロイしよう

lib/hello-cdk-stack.ts の中で定義している myFunction を変更して、Lambda 関数の出力を「Hello World!」から「Hello CDK!」に変更します。

※ 以下のコードは変更箇所の一部抜粋です

// ...

export class HelloCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Modify the Lambda function resource
    const myFunction = new lambda.Function(this, "HelloWorldFunction", {
      runtime: lambda.Runtime.NODEJS_20_X, // Provide any supported Node.js runtime
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello CDK!'),
          };
        };
      `),
    });

    // ...
  • body: JSON.stringify('Hello CDK!'), の行のみを変更しています

デプロイ済みの CloudFormation Template との差分を確認します

npx cdk diff

以下、npx cdk diff の実行例です。

$ npx cdk diff  
Stack HelloCdkStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[~] AWS::Lambda::Function HelloWorldFunction HelloWorldFunctionB2AB6E79 
 └─ [~] Code
     └─ [~] .ZipFile:
         ├─ [-] 
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello World!'),
          };
        };
      
         └─ [+] 
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello CDK!'),
          };
        };
      


✨  Number of stacks with differences: 1
  • body: JSON.stringify の中身が変わっていることを確認できます

再デプロイを実施します。 synth を省略していますが、その場合も cdk deploy の中で自動で実行されます。

$ npx cdk deploy

✨  Synthesis time: 2.84s

HelloCdkStack: deploying... [1/1]
HelloCdkStack: creating CloudFormation changeset...

 ✅  HelloCdkStack

✨  Deployment time: 29.92s

Outputs:
HelloCdkStack.myFunctionUrlOutput = https://<url-id>.lambda-url.<region>.on.aws/
Stack ARN:
arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/HelloCdkStack/75e39360-7dc6-11ef-b1a8-0affd4860f5b

✨  Total time: 32.76s

Lambda 関数のエンドポイントからも出力が変わることを確認します。次のステップに進むまえに、再デプロイにかかった時間を確認しましょう。本記事の例では、初回に 43 秒、再デプロイに 32 秒かかっていました。

curl https://<url-id>.lambda-url.<region>.on.aws/

以下、curl の実行例です。

$ curl https://<url-id>.lambda-url.<region>.on.aws/
"Hello CDK!"%

9. Lambda 関数への変更を素早く反映させる方法

Lambda 関数の開発時において、コードへの変更とデプロイを高速に繰り返したい場合に、--hotswap オプションを使用すると、CloudFormation を経由せず API で直接デプロイすることで素早く反映することができます。ただし、CloudFormation を経由しないことで、CloudFormation スタックと実際のリソースの間に差分 (ドリフト) が発生します。そのため、本番環境へのデプロイには適しておらず、開発用途に限定しての利用を推奨します

npx cdk deploy --hotswap

--hotswap に関連するオプションに、--hotswap-fallback があります。これらは、ホットスワップデプロイできないリソースがあった際の挙動が異なります。ホットスワップデプロイできないリソースがあった場合に、--hotswap は無視しますが、--hotswap-fallback は自動で通常のデプロイに切り替わります。ホットスワップデプロイできるリソースの種類については、AWS CDK Toolkit の README.md をご確認ください。

lib/hello-cdk-stack.ts の中で定義している myFunction を変更して、Lambda 関数の出力を「Hello CDK!」から「Hello CDK! by hotswap deploy」に変更します。

※ 以下のコードは変更箇所の一部抜粋です

// ...

export class HelloCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Modify the Lambda function resource
    const myFunction = new lambda.Function(this, "HelloWorldFunction", {
      runtime: lambda.Runtime.NODEJS_20_X, // Provide any supported Node.js runtime
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello CDK! by hotswap deploy'),
          };
        };
      `),
    });

    // ...
  • body: JSON.stringify('Hello CDK! by hotswap deploy'), の行のみを変更しています

--hotswap オプション付きでデプロイを実施します。以下の実行例では、デプロイ時間が 32 秒から 11 秒に短縮されました。

$ npx cdk deploy --hotswap

✨  Synthesis time: 2.82s

⚠️ The --hotswap and --hotswap-fallback flags deliberately introduce CloudFormation drift to speed up deployments
⚠️ They should only be used for development - never use them for your production Stacks!

HelloCdkStack:  start: Building a598b1780057683646f0fdba1fa3f8b8d37f902efda96800d83569be5b6d4da3:current_account-current_region
HelloCdkStack:  success: Built a598b1780057683646f0fdba1fa3f8b8d37f902efda96800d83569be5b6d4da3:current_account-current_region
HelloCdkStack:  start: Publishing a598b1780057683646f0fdba1fa3f8b8d37f902efda96800d83569be5b6d4da3:current_account-current_region
HelloCdkStack:  success: Published a598b1780057683646f0fdba1fa3f8b8d37f902efda96800d83569be5b6d4da3:current_account-current_region
HelloCdkStack: deploying... [1/1]

✨ hotswapping resources:
   ✨ Lambda Function 'HelloCdkStack-HelloWorldFunctionB2AB6E79-iC3U1SSNflnY'
✨ Lambda Function 'HelloCdkStack-HelloWorldFunctionB2AB6E79-iC3U1SSNflnY' hotswapped!

 ✅  HelloCdkStack

✨  Deployment time: 8.56s

Outputs:
HelloCdkStack.myFunctionUrlOutput = https://<url-id>.lambda-url.<region>.on.aws/
Stack ARN:
arn:aws:cloudformation:us-east-1:xxxxxxxxxxxx:stack/HelloCdkStack/75e39360-7dc6-11ef-b1a8-0affd4860f5b

✨  Total time: 11.38s

$ curl https://<url-id>.lambda-url.<region>.on.aws/
"Hello CDK! by hotswap deploy"%

さらに、変更を継続して監視してデプロイを実施する場合、cdk deploy ではなく cdk watch を実施する方法があります。cdk watch を使用すると、ファイルを変更して保存したタイミングで、デプロイ処理が実施されます。また監視中は、Amazon CloudWatch Logs の更新を自動的に受信できるため、デプロイを繰り返しながら実行ログの確認が可能です。変更しながら挙動を確認してください。

npx cdk watch

10. 使ったリソースを片付けよう

最後に、今回作成したリソースを削除します。IaC で管理していると、検証したリソースの削除も簡単です。次のコマンドで削除が可能です。

npx cdk destroy

削除の確認に y で答えると、自動的にリソースが削除されます。


11. まとめ

お疲れさまでした ! 以上で AWS CDK のローカル開発環境の構築と、基本的な利用方法の体験は終了です ! 無事に、Lambda 関数からの「Hello CDK!」などの出力は確認いただけましたでしょうか ??

小規模な内容ではありましたが、コードで AWS リソースを定義し、npx cdk ~ というコマンドを使用して CDK アプリケーションのデプロイなど、テンプレートとコードの差分の確認、高速な反映方法、AWS リソースの削除の操作を体験いただきました。実際に体験してみると、思ったよりも簡単だった ! という感想を持たれる方も多いのではないかと期待しています。

次は後述の Next Step も参照・体験いただき、ぜひ皆さんが実際に業務で構築する環境のコード化も目指していただければと思います。

Next Step

  • AWS CDK に関する技術情報のキャッチアップ
    後述の関連リンクに記載した、AWS CDK について詳しく知りたい方向けの記事・ドキュメントをご覧ください
  • ワークショップで AWS CDK の理解を深める
  • Lambda 関数をローカルでテストする

関連リンク


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

木村 友則 (@tkimurz)
アマゾン ウェブ サービス ジャパン合同会社
ソリューションアーキテクト

過去にはオンプレミスのサーバーやストレージを販売する側でしたが、その後AWSを利用する側に変わり、現在はAWS利用を検討している幅広いお客様に対して技術的な側面からビジネス支援を行っています。AWSを使い始めたとき、CLI やSDK、IaCを通じてリソースを自在に作れること感動しました。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する