AWS CDKでAWS Lambda関数を構築する方法(TypeScript版)
概要
AWS CDK(Cloud Development Kit)は、AWS上のリソースをPython / Java / .NET / TypeScriptを使って構築することができるフレームワークです。 Amazon LambdaはAWS上でコードを実行できるFaaS(Function as a Service)です。
- 概要
- 動作確認
- AWS CDKのCLIをインストール
- AWS CDKのプロジェクトを作成
- AWS Lambdaを構築するためのパッケージを追加する
- AWS Lambdaの関数本体のコードを作成する
- AWS Lambdaを構築するコードを追加する
- Lambda関数本体をアップロードするためのS3バケットを作成する
- AWS CDKで構築したリソースをデプロイする
- リソースを削除する
- DynamoDBテーブルへのアクセス権限をスマートに付与する
動作確認
- aws-cdk@1.6.1
AWS CDKのCLIをインストール
$ npm i -g aws-cdk
AWS CDKのプロジェクトを作成
# ディレクトリを作成 $ mkdir lambda # ディレクトリに移動 $ cd lambda # AWS CDKプロジェクトを作成 $ cdk init app --language=typescript
この時点でのディレクトリは以下のようになっています。
$ tree . -I node_modules . ├── README.md ├── bin │ └── lambda.ts ├── cdk.json ├── lib │ └── lambda-stack.ts ├── package-lock.json ├── package.json └── tsconfig.json
AWS Lambdaを構築するためのパッケージを追加する
AWS CDKでは、AWSの各リソースを構築するためのクラスはそれぞれ別パッケージとなっています。 次のコマンドでAWS Lambdaを構築するためのパッケージをインストールします。
$ npm i @aws-cdk/aws-lambda @aws-cdk/aws-iam
AWS Lambdaの関数本体のコードを作成する
今回は入力をログに出力するだけのシンプルなLambda関数をNode.jsを使って書きます。
resources/sample-function/index.js
として、以下の内容を作成します。
exports.handler = async events => { console.log(event); return {}; };
AWS Lambdaを構築するコードを追加する
lib/lambda-stack.ts
に以下のコードを追加します。
import cdk = require("@aws-cdk/core"); // 以下のコードを追加 import iam = require("@aws-cdk/aws-iam"); import lambda = require("@aws-cdk/aws-lambda"); export class LambdaStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // 以下のコードを追加(IAMロール作成) const role = new iam.Role(this, "SampleFunctionRole", { assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"), path: "/service-role/", inlinePolicies: { CloudWatchWritePolicy: new iam.PolicyDocument({ statements: [ new iam.PolicyStatement({ actions: [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], resources: ["*"] }) ] }) } }); // 以下のコードを追加(Lambda関数作成) new lambda.Function(this, "SampleFunction", { code: lambda.Code.asset("resources/sample-function"), // パスはプロジェクトのルートからのパス handler: "index.handler", runtime: lambda.Runtime.NODEJS_10_X, role }); } }
Lambda関数を生成する前に、Lambdaに設定するIAMロールを生成しています。 付与する権限はCloudWatch Logsへの書き込みです。
Lambda関数本体をアップロードするためのS3バケットを作成する
AWS Lambdaの関数をAWS CDKでデプロイするにはいくつか方法がありますが、Amazon S3を経由する方法を使います。 AWSアカウントで初めてS3にファイルを配置するようなデプロイを行う際は、まず以下のコマンドでS3バケットを作成する必要があります。
$ npx cdk bootstrap ⏳ Bootstrapping environment aws://XXXXXXXXX/ap-northeast-1... CDKToolkit: creating CloudFormation changeset... 0/2 | 21:15:08 | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket 0/2 | 21:15:10 | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket Resource creation Initiated 1/2 | 21:15:32 | CREATE_COMPLETE | AWS::S3::Bucket | StagingBucket 2/2 | 21:15:33 | CREATE_COMPLETE | AWS::CloudFormation::Stack | CDKToolkit ✅ Environment aws://XXXXXXXXX/ap-northeast-1 bootstrapped.
これで準備完了です。
AWS CDKで構築したリソースをデプロイする
IAMロールの変更を伴う場合、変更点が表示されて確認が求められます。
# TypeScriptをコンパイル $ npx tsc # リソースをデプロイ $ cdk deploy 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 │ ├───┼───────────────────────────────────┼────────┼───────────────────────────────────┼───────────────────────────────────┼───────────┤ │ + │ ${SampleFunctionRole.Arn} │ Allow │ sts:AssumeRole │ Service:lambda.amazonaws.com │ │ ├───┼───────────────────────────────────┼────────┼───────────────────────────────────┼───────────────────────────────────┼───────────┤ │ + │ * │ Allow │ logs:CreateLogGroup │ AWS:${SampleFunctionRole} │ │ │ │ │ │ logs:CreateLogStream │ │ │ │ │ │ │ logs:PutLogEvents │ │ │ └───┴───────────────────────────────────┴────────┴───────────────────────────────────┴───────────────────────────────────┴───────────┘ (NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np) Do you wish to deploy these changes (y/n)? y LambdaStack: deploying... Updated: asset.cdc666b01549182a49a7205d6dccaa8f295349e048a070ed4c3c2cfba0f724a9 (zip) LambdaStack: creating CloudFormation changeset... 0/4 | 21:18:21 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata 0/4 | 21:18:22 | CREATE_IN_PROGRESS | AWS::IAM::Role | SampleFunctionRole (SampleFunctionRoleE8514E0F) 0/4 | 21:18:22 | CREATE_IN_PROGRESS | AWS::IAM::Role | SampleFunctionRole (SampleFunctionRoleE8514E0F) Resource creation Initiated 0/4 | 21:18:23 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata Resource creation Initiated 1/4 | 21:18:24 | CREATE_COMPLETE | AWS::CDK::Metadata | CDKMetadata 2/4 | 21:18:35 | CREATE_COMPLETE | AWS::IAM::Role | SampleFunctionRole (SampleFunctionRoleE8514E0F) 2/4 | 21:18:38 | CREATE_IN_PROGRESS | AWS::Lambda::Function | SampleFunction (SampleFunction7DB1D36A) 2/4 | 21:18:39 | CREATE_IN_PROGRESS | AWS::Lambda::Function | SampleFunction (SampleFunction7DB1D36A) Resource creation Initiated 3/4 | 21:18:39 | CREATE_COMPLETE | AWS::Lambda::Function | SampleFunction (SampleFunction7DB1D36A) 4/4 | 21:18:41 | CREATE_COMPLETE | AWS::CloudFormation::Stack | LambdaStack ✅ LambdaStack Stack ARN: arn:aws:cloudformation:ap-northeast-1:854529464916:stack/LambdaStack/e5755240-ce44-11e9-8c4f-0efdab6a43a0
AWS Lambdaの関数をAWS CDK経由でデプロイすることができました。
リソースを削除する
# リソースを削除する $ npx cdk destroy
DynamoDBテーブルへのアクセス権限をスマートに付与する
AWS CDKには、Cfn
で始まるコンストラクタ(ex:CfnTable
, CfnFunction
)とそれ以外のコンストラクタの2種類があります。
Cfn
付きコンストラクタはAWS CloudFormationで用意されているフィールドがそのまま反映された形の仕様となっています。
一方、 Cfn
なしコンストラクタの方は、CloudFormationの仕様からさらに抽象化されて、よりシンプルに、より便利にリソースを構築できるようになっています。
DynamoDBテーブルとLambda関数にはCfn
なしコンストラクタが用意されているので、相互に連携して便利に権限付与を行うことができます。
必要な部分だけ抜粋したものですが、Lambdaの関数からDynamoDBテーブルへアクセスできるような権限を付与するには、以下のように書きます。
// Lambda関数を生成 const lambdaFunction = new lambda.Function(this, "SampleFunction", { code: lambda.Code.asset("resources/sample-function"), // パスはプロジェクトのルートからのパス handler: "index.handler", runtime: lambda.Runtime.NODEJS_10_X, role }); // DynamoDBテーブルを作成 const dynamodbTable = new dynamodb.Table(this, "PostTable", { partitionKey: { name: "id", type: dynamodb.AttributeType.STRING } }); // 読み取り権限を追加 dynamodbTable.grantReadData(lambdaFunction); // 書き込み権限を追加 dynamodbTable.grantWriteData(lambdaFunction); // 読み書き権限を追加 dynamodbTable.grantReadWriteData(lambdaFunction);
Cfn
なしコンストラクタが提供されていないリソースについては、この書き方ができないので、iam.Role
クラスを使って地道にリソース間の権限付与を行なっています。
Cfn
なしコンストラクタも徐々に増えていくかと思うので、キャッチアップすることでどんどんCDKのプロジェクトをシンプルに、より安全にすることができそうです。