1. はじめに

Lambda ✕ ApiGatewayでAPIを構築することが良くあります。

この構成をAWS SAM や CloudForamtionで構築することがありましたが、APIが数十個構築する必要があるとLambdaごとにテンプレートを作成するのが大変になります。

また、手作業でテンプレートをAWS SAM に記載すると追加漏れや修正漏れが発生することが、見受けられました。

こういった場面で CDK を利用するとスマートに構築することができます。

2. やりたいこと

  • Lambdaを複数定義する。
  • API GatewayとLambdaを紐付ける。
  • IAM を別スタックに分離する。
  • CLoudWatch のロググループを明示的に作成する。

前提

  • AWS CLIインストール済みであること
  • Python3.13インストール済みであること
  • AWS CDKインストール済みであること

3. 構成

3.1. スタックの構成

AppStack       IamStack
  ↓               ↓
[Lambda]       [IAM Role]
  ↓
[LogGroup]
  ↓
[API Gateway]

3.2. ディレクトリ構造

.
└── cdk_test
    ├── app.py
    ├── cdk.json
    ├── functions.json
    ├── lambda
    │   ├── lambda_1
    │   │   └── handler.py
    │   ├── lambda_2
    │   │   └── handler.py
    │       └── handler.py
    ├── requirements-dev.txt
    ├── requirements.txt
    ├── source.bat
    ├── stacks
    │   ├── __init__.py
    │   ├── app_stack.py
    │   └── iam_stack.py

4. Lambdaの定義をjsonで管理するメリット

  • Lambdaの追加や設定の変更時にコードを修正する必要がなくなる。

5. 実装コード紹介

5.1 functions.json

Lambdaを追加する際はこのjsonファイルに記述する。

[
  { "name": "lambda_1", "expose_api": true },
  { "name": "lambda_2", "expose_api": false }
]

5.2 IAMスタック

Lambdaの実行ロール用のスタックを生成する。

from aws_cdk import Stack
from aws_cdk.aws_iam import Role, ServicePrincipal, PolicyStatement
from constructs import Construct

class IamStack(Stack):

    def __init__(self, scope: Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        self.lambda_execution_role = Role(
            self, "LambdaExecutionRole",
            assumed_by=ServicePrincipal("lambda.amazonaws.com")
        )

        self.lambda_execution_role.add_to_policy(PolicyStatement(
            actions=[
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            resources=["*"]
        ))

5.3 AppStack

AppStackでLambda、ロググループ、ApiGatewayの生成を行う。

from aws_cdk import Stack
from aws_cdk.aws_lambda import Function, Runtime, Code
from aws_cdk.aws_apigateway import RestApi, LambdaIntegration
from aws_cdk.aws_logs import LogGroup, RetentionDays
from constructs import Construct
import os
import json

class AppStack(Stack):

    def __init__(self, scope: Construct, id: str, lambda_role, **kwargs):
        super().__init__(scope, id, **kwargs)

        api = RestApi(self, "MyApi")

        functions = self._load_functions_from_file("functions.json")

        for function in functions:
            log_group = LogGroup(
                self, f"{function["name"]}LogGroup",
                log_group_name=f"/aws/lambda/{function["name"]}",
                retention=RetentionDays.ONE_WEEK
            )

            lambda_fn = Function(
                self, f"{function["name"].capitalize()}Lambda",
                function_name=function["name"],
                runtime=Runtime.PYTHON_3_11,
                handler="handler.lambda_handler",
                code=Code.from_asset(f"lambda/{function["name"]}"),
                role=lambda_role,
                log_group=log_group
            )

            api.root.add_resource(function["name"]).add_method("GET", LambdaIntegration(lambda_fn))

    def _load_functions_from_file(self, file_path):
        with open(file_path, "r") as f:
            return json.load(f)

5.4 app.py

app.pyでIamStackとAppStackをインスタンス化する。 IamStackで生成したLambda実行ロールをAppStackに渡してLambdaに紐付ける

from aws_cdk import App
from stacks.iam_stack import IamStack
from stacks.app_stack import AppStack

app = App()
iam_stack = IamStack(app, "IamStack")
AppStack(app, "AppStack", lambda_role=iam_stack.lambda_execution_role)

app.synth()

6. さいごに

このようにCDKを利用すれば、複数のLambdaを楽に構築することが可能になります。

Lambda, ApiGatewayやCloudWatch ロググループの作成やアラートは大量のリソースを一括で作成することが多くあります。

そういった要件にはCDKを利用するには力を発揮すると思います。