AWS CDK を使用して Lambda と Step Functions を作成する

AWS

はじめに

本記事では、AWS CDK (Cloud Development Kit) を使用して AWS Lambda 関数と AWS Step Functions ステートマシンを作成します。AWS CDKは、クラウドリソースを定義するためのオープンソースのソフトウェア開発フレームワークで、本記事では TypeScript を使用しています。

ディレクトリ構造

本プロジェクトのディレクトリ構造は以下の通りです:

.
├── bin
│   └── cdk_sfn.ts
├── cdk.json
├── jest.config.js
├── lambda
│   └── lambda_function.py
├── lib
│   └── cdk_sfn-stack.ts
├── node_modules
├── package.json
├── stepfunctions
│   └── asl.json
├── test
└── tsconfig.json

環境

本記事では以下のバージョンの AWS CDK を使用しています。

$ cdk version
2.80.0 (build bbdb16a)

Lambda 関数

デプロイする Lambda 関数は以下です。Python で記述しています。

import boto3

def update_cloudfront_distribution(event):
    try:
        distribution_id = event["distribution_id"]
        new_acl_id = event["new_acl_id"]
    except KeyError as e:
        return {"statusCode": 400, "body": f"Missing key in event: {e}"}

    client = boto3.client("cloudfront")

    try:
        dist_config_response = client.get_distribution_config(Id=distribution_id)
        dist_config = dist_config_response["DistributionConfig"]
        e_tag = dist_config_response["ETag"]

        dist_config["WebACLId"] = new_acl_id

        client.update_distribution(Id=distribution_id, IfMatch=e_tag, DistributionConfig=dist_config)
    except Exception as e:
        return {"statusCode": 500, "body": f"Failed to update distribution config: {e}"}

    return {"statusCode": 200, "body": f"Updated WAF ACL for distribution {distribution_id} to {new_acl_id}"}

def lambda_handler(event, context):
    print(event)
    response = update_cloudfront_distribution(event)
    return response

CDK のコード

以下に CDK のコードを示します。

  • Lambda のコードで CloudFront や WAF に操作を行っているため、
  • Escape Hatch を利用して、既存のASLを使用してステートマシンを作成します。Escape Hatch を使用することで、低レベルの CloudFormation リソースを直接操作することが可能になります。
import * as cdk from 'aws-cdk-lib';
import { ManagedPolicy, Role, IRole, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";
import { CfnStateMachine, Pass, StateMachine } from 'aws-cdk-lib/aws-stepfunctions';
import * as fs from 'fs';

const POLICY_ARN_WAF = "arn:aws:iam::aws:policy/AWSWAFFullAccess";
const POLICY_ARN_LAMBDA = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole";
const POLICY_ARN_CLOUDFRONT = "arn:aws:iam::aws:policy/CloudFrontFullAccess";

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

    const environment = this.node.tryGetContext('environment')
    const contextValues = this.node.tryGetContext(environment)

    const stepFunctionFile = fs.readFileSync(`./stepfunctions/asl.json`)

    const lambdaRole = this.createLambdaRole();
    const lambdaFunction = this.createLambdaFunction(lambdaRole);

    const commonRole = Role.fromRoleArn(this, 'CommonRole', `arn:aws:iam::${contextValues['account']}:role/stepfunctions-common-role`);
    this.createStateMachine(stepFunctionFile, commonRole);
  }

  private createLambdaRole(): Role {
    return new Role(this, "lambdaRole", {
      assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
      managedPolicies: [
        ManagedPolicy.fromManagedPolicyArn(this, "wafPolicy", POLICY_ARN_WAF),
        ManagedPolicy.fromManagedPolicyArn(this, "lambdaPolicy", POLICY_ARN_LAMBDA),
        ManagedPolicy.fromManagedPolicyArn(this, "cloudfrontPolicy", POLICY_ARN_CLOUDFRONT),
      ],
      description: "Basic Lambda Role",
    });
  }

  private createLambdaFunction(lambdaRole: Role): Function {
    return new Function(this, "python-function", {
      functionName: "switch_acl",
      runtime: Runtime.PYTHON_3_9,
      code: Code.fromAsset("lambda"),
      handler: "lambda_function.lambda_handler",
      role: lambdaRole,
      timeout: cdk.Duration.seconds(30)
    });
  }

  private createStateMachine(stepFunctionFile: Buffer, commonRole: IRole): StateMachine {
    const stateMachine = new StateMachine(this, 'MyStateMachine', {
      definition: new Pass(this, 'StartState'),
      role: commonRole,
      stateMachineName: "SwitchWafAcl"
    });

    const cfnStateMachine = stateMachine.node.defaultChild as CfnStateMachine;
    cfnStateMachine.definitionString = stepFunctionFile.toString();
    return stateMachine;
  }
}

おわりに

本記事では、AWS CDK を使用して AWS Lambda 関数と AWS Step Functions ステートマシンを作成する方法を説明しました。AWS CDK を使用することで、クラウドリソースのプロビジョニングを自動化し、繰り返し可能で信頼性の高い方法で行うことが可能です。
この記事がどなたかの参考になれば幸いです。

参考

コメント