【AWS-CDK】認証付きWebAPIをSolutions Constructsを使って手軽に構築


Solutions ConstructsでAWSインフラ構築

先日、AWS Solutions Constructsが発表されました。これはAWS-CDKにおいて、抽象化されたリソースを表すConstructsのよく使う組み合わせをWell-Architected準拠の設定込みで一発で構築できるライブラリです。例えばLambda + DynamoDBCloudFront + S3などの組み合わせのパターンが25個(2020/7月現在)提供されています。

これをつかってパパっとAWS上にWebAPIを構築したいと思います。

注意

2020/7/6現在、Solutions ConstructsのstabilityはすべてExperimentalとなっています。
本番環境などでの使用はよく検討した上で行ってください。

環境

  • AWS-CDK CLI v1.47.0
    • ※ Solutions ConstrucsはCDK v1.46.0以上に対応しています。
  • TypeScript v3.9.2
  • npm v6.14.5
  • node.js v12.18.0

使用するConstructs

今回は以下のSolutions Constructsを使用します。

構成図は以下のとおりです。

CDKによって、API Gateway, Lambda, Cognito, DynamoDBテーブルを作成します。
さらに、各リソースのオプションはデフォルトで下記のWell-Architected準拠の設定が施されています。

  • Cognito
    • ユーザープールに対するパスワードポリシーの設定
    • ユーザープールに対するアドバンスドセキュリティの有効化
  • API Gateway
    • エンドポイントのエッジ最適化設定
    • アクセスログの有効化
    • 最小限のIAMロールの設定
    • すべてのメソッドに対するIAM認証設定
  • Lambda
    • 最小限のIAMロールの設定
    • ログ出力の有効化
    • keep-alive時のコネクション再利用設定(Node.jsファンクション)
  • DynamoDB
    • オンデマンドモードの設定
    • AWSマネージドCMKによる暗号化設定

作成手順

AWS-CDK CLIをグローバルインストールします。

npm install -g aws-cdk

インストール後にプロジェクトを作成します。

cdk init app --language=typescript

今回使用するライブラリをインストールします。(SolutionsConstructsの最新verが1.47.0なので統一)

npm install -s @aws-cdk/[email protected] @aws-cdk/[email protected] @aws-cdk/[email protected] @aws-cdk/[email protected] @aws-solutions-constructs/[email protected] @aws-solutions-constructs/[email protected]

Constructsを使ってCDKを記述します。

import * as cdk from "@aws-cdk/core";
import * as lambda from "@aws-cdk/aws-lambda";
import { CognitoToApiGatewayToLambda } from "@aws-solutions-constructs/aws-cognito-apigateway-lambda";
import { LambdaToDynamoDB } from "@aws-solutions-constructs/aws-lambda-dynamodb";
import {
  AttributeType,
  BillingMode,
  TableEncryption,
} from "@aws-cdk/aws-dynamodb";

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

    // The code that defines your stack goes here
    const apigwToLambda = new CognitoToApiGatewayToLambda(
      this,
      "CognitoToApiGatewayToLambdaPattern",
      {
        deployLambda: true,
        lambdaFunctionProps: {
          runtime: lambda.Runtime.NODEJS_12_X,
          handler: "index.handler",
          code: lambda.Code.asset(`${__dirname}/lambda`),
        },
      }
    );

    new LambdaToDynamoDB(this, "LambdaToDynamoPattern", {
      deployLambda: false,
      existingLambdaObj: apigwToLambda.lambdaFunction, // 生成済みのLambdaを使用
      dynamoTableProps: {
        partitionKey: { name: "id", type: AttributeType.STRING },
        // デフォルトの設定は以下の通り
        //   キャパシティーモード: オンデマンド
        //   暗号化タイプ: KMS - AWSマネージドCMK
        // 無料枠で使用するために設定を変更している。
        billingMode: BillingMode.PROVISIONED,
        readCapacity: 5,
        writeCapacity: 5,
        encryption: TableEncryption.DEFAULT,
      },
    });
  }
}

lib/lambdaディレクトリにはlambdaのソースを配置します。

index.js
const { DynamoDB } = require("aws-sdk");

exports.handler = async function (event) {
  console.log("request:", JSON.stringify(event, undefined, 2));

  // create AWS SDK clients
  const dynamo = new DynamoDB();

  // APIのパスをパーティションキーとして、アクセス回数を記録する
  await dynamo
    .updateItem({
      TableName: process.env.DDB_TABLE_NAME,
      Key: { id: { S: event.path } },
      UpdateExpression: "ADD hits :incr",
      ExpressionAttributeValues: { ":incr": { N: "1" } },
    })
    .promise();

  return {
    statusCode: 200,
    headers: { "Content-Type": "text/plain" },
    body: `Hello, AWS Solutions Constructs! You've hit ${event.path}\n`,
  };
};

以下のコマンドでインフラをデプロイできます

npm run build && cdk deploy

cdk deploy実行時にリソースのデプロイ確認があるので、yを入力してデプロイを開始しましょう。

API Gatewayのコンソールから/fooメソッドのテストをおこなうと正常にレスポンスが返されることが確認できます。

また、DynamoDBに、アクセスしたパスの文字列とアクセス回数が記録されていることが確認できます。

まとめ

このように、Solution ConstructsではAWSでよく使われるリソースの組み合わせをConstructsをして提供してくれています。それを利用することで、爆速で今までよりもすばやく、かつ、Well-Architectedに準拠したインフラを構築することが可能になりました。

参考