ちょっと話題の記事

[Cloud Development Kit ] LINEアプリのサンプルをCDKで書き直して見て、その凄さを実感しました

2019.07.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部の平内(SIN)です。

昨日、GAとなった AWS Cloud Development Kit (AWS CDK) 凄すぎます。

完全に、上の記事の真似事なんですが・・・私も、早速やってみたい!って事で、先日、作ったAWS SAM + TypeScriptで作成したLINE Bot のサンプルをAWS CDKで書き直してみました。

2 プロジェクト作成

CDKのコマンド initでプロジェクトを作成します。

$ mkdir line-sample
$ cd line-sample
[line-sample]$ cdk init app --language=typescript

3 Lambda

続いて、プロジェクトの階層下にsrc/lambdaを掘って、Lambdaのコードを作成します。

[line-sample]$ mkdir src; cd src
[line-sample/src]$ mkdir lambda; cd lambda
[line-sample/src/lambda]$ touch index.ts

コードは、前回作成したものと同じです。

src/lambda/index.ts

import * as Lambda from 'aws-lambda';
import * as Line from "@line/bot-sdk";
import * as Types from "@line/bot-sdk/lib/types";
import { WebhookEvent } from '@line/bot-sdk';

const accessToken = process.env.ACCESS_TOKEN!;
const channelSecret = process.env.CHANNEL_SECRET!;

const config: Line.ClientConfig = {
    channelAccessToken: accessToken,
    channelSecret: channelSecret,
};
const client = new Line.Client(config);

async function eventHandler(event: Types.MessageEvent): Promise<any> {
    if (event.type !== 'message' || event.message.type !== 'text') {
        return null;
    }
    const message: Types.Message = { type: "text", text: event.message.text + "ってか?" };
    return client.replyMessage(event.replyToken, message);
}

export const handler: Lambda.APIGatewayProxyHandler = async (proxyEevent:Lambda.APIGatewayEvent, _context) => {
    console.log(JSON.stringify(proxyEevent));

    // 署名確認
    const signature = proxyEevent.headers["X-Line-Signature"];
    if (!Line.validateSignature(proxyEevent.body!, channelSecret, signature)) {
        throw new Line.SignatureValidationFailed("signature validation failed", signature);
    }

    const body: Line.WebhookRequestBody = JSON.parse(proxyEevent.body!);
    await Promise
        .all(body.events.map( async event => eventHandler(event as Types.MessageEvent)))
        .catch( err => {
            console.error(err.Message);
            return {
                statusCode: 500,
                body: "Error"
            }
        })
    return {
        statusCode: 200,
        body: "OK"
    }
}

4 パッケージのインストール

npmパッケージのインストールを行います。

TypeScript用の型情報は、プロジェクトのトップに入れます。

TS 型定義用

[line-sample]$ npm install @types/aws-lambda
[line-sample]$ npm install @line/bot-sdk

CDK 型定義用

[line-sample]$ npm install @aws-cdk/aws-lambda
[line-sample]$ npm install @aws-cdk/aws-apigateway

そして、Lambdaで使用されるパッケージは、src/lambda/node_modulesに入れます。

Lambda用

[line-sample/src/lambda]$ npm init
[line-sample/src/lambda]$ npm install @line/bot-sdk
[line-sample/src/lambda]$ npm install aws-lambda

5 テンプレート

いよいよテンプレートをTSで作成します。これが、ビックリするぐらい簡単過ぎなんです。

下記は、APIGatewayでエンドポイントを作成し、POSTメソッドからLambdaをプロキシーで呼び出しているものです。

これをテンプレートで実際書いたら結構な量になると思います。これだけ簡単にできるのは、何と言ってもロールなどが、AWSのベストプラアクティスにのっとって、デフォルトで生成されるのが、一番の理由でしょう。

lib/line-sample-stack.ts

import cdk = require('@aws-cdk/core');
import * as lambda  from '@aws-cdk/aws-lambda';
import * as apigateway from '@aws-cdk/aws-apigateway';

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

    const lineSampleLambda = new lambda.Function(this, 'LineSampleLambda', {
      code: lambda.Code.asset('src/lambda'),
      handler: 'index.handler',
      runtime: lambda.Runtime.NODEJS_10_X,
      timeout: cdk.Duration.seconds(5),
      environment: {
          ACCESS_TOKEN: "xxxxx",
          CHANNEL_SECRET: "xxxxx"
      }
    });

    const api = new apigateway.RestApi(this, 'LineSampleApi', {
        restApiName: 'line-sample'
    });
    const lambdaIntegration = new apigateway.LambdaIntegration(
      lineSampleLambda, { proxy: true } );
    api.root.addMethod('POST', lambdaIntegration)
  }
}

6 ビルド

ビルドは、実際にはtscが走っています。

[line-sample]$ npm run build
> line-sample@0.1.0 build /Users/hirauchi.shinichi/tmp/line-sample
> tsc

そして、出来上がったテンプレートです。(長いので、省略しています)

$ cdk synth
Resources:
  LineSampleLambdaServiceRole802545FF:
    Type: AWS::IAM::Role
・・・略・・・
  LineSampleLambda148E180F:
    Type: AWS::Lambda::Function
・・・略・・・
  LineSampleLambdaApiPermissionPOST53C46664:
    Type: AWS::Lambda::Permission
・・・略・・・
  LineSampleLambdaApiPermissionTestPOSTF7E1E36E:
    Type: AWS::Lambda::Permission
・・・略・・・
  LineSampleApi562C5419:
    Type: AWS::ApiGateway::RestApi
・・・略・・・
  LineSampleApiDeployment3578462012279476a9eab561f7edbe4db9f431ea:
    Type: AWS::ApiGateway::Deployment
・・・略・・・
  LineSampleApiDeploymentStageprod08264243:
    Type: AWS::ApiGateway::Stage
・・・略・・・
  LineSampleApiCloudWatchRole3AC02CBD:
    Type: AWS::IAM::Role
・・・略・・・
  LineSampleApiAccount094622A7:
    Type: AWS::ApiGateway::Account
・・・略・・・
  LineSampleApiPOSTF56853D2:
    Type: AWS::ApiGateway::Method
・・・略・・・
Parameters:
・・・略・・・
Outputs:
・・・略・・・

7 bootstrap

初めてCDKを利用する場合は、1回だけ bootstrapコマンドを実行してデプロイ用のBucketの準備が必要です。

$ cdk bootstrap --profile developer

8 デプロイ

$ cdk deploy --profile developer

作成されるリソースの一覧で y を入力するとデプロイされます。

9 スタック確認

AWSコンソールから確認した、リソースです。

CloudFormation

Lambda

API Gateway

ステージからURLをコピーして、LINEプロジェクトのエンドポイントに設定します。

10 動作確認

LINEから使用すると、元気にLambdaから返事が返ってきます。

11 最後に

いやー何度も言ってしまいますが、AWS CDK凄すぎます。

CFnや、SAMのテンプレート作成が、なんだかんだ言って、手際が悪くて辛かった私にとって、TypeScriptで書けるAWS CDKは、神です。

ちなみに、AWS CDKは、現時点で開発者プレビューみたいですが、C#とかJavaでも利用できます。間違いなく静的型付けの威力が発揮されるでしょう。

Use the AWS CDK to define your cloud resources in a familiar programming language. 
The AWS CDK supports TypeScript, JavaScript, and Python. 
The AWS CDK also provides Developer Preview support for C#/.NET, and Java.

12 参考リンク


AWS CDK API Reference