log
码中赤兔

使用 CDK Custom Resource 实现自定义 AWS 设置

发布于 2025年4月25日
更新于 2025年4月25日
15 分钟阅读
AWS

在本篇文章中,我们将介绍如何通过 CDK 的 Custom Resource 功能,为 CDK 原生不支持的 AWS 资源或功能添加自定义配置。
举例来说,CDK 默认并不支持为已有的 S3 Bucket 添加事件通知(Notification)。下面的示例演示,如何在一个已存在的 S3 Bucket 上,通过 Custom Resource 动态添加 Notification 设置,使其在指定后缀的文件上传时,自动触发 Lambda 函数。


一、编写用于配置 S3 Notification 的 Lambda

首先,我们需要一个专门负责为 S3 Bucket 设置 Notification 的 Lambda 函数。这段代码会根据传入的后缀列表(suffixList),构造对应的 PutBucketNotificationConfigurationCommand 并发送给 S3。

// lambda/s3-notification-setting/index.ts
import { LambdaFunctionConfiguration, PutBucketNotificationConfigurationCommand, S3Client } from '@aws-sdk/client-s3';
import { CdkCustomResourceEvent, Context } from 'aws-lambda';

const createUpdateCommand = (suffixList: string[]) => {
  const configurations: LambdaFunctionConfiguration[] = suffixList.map((suffix) => ({
    LambdaFunctionArn: process.env.INVOKE_FUNC_ARN!,
    Events: ['s3:ObjectCreated:*'],
    Filter: {
      Key: {
        FilterRules: [{ Name: 'suffix', Value: suffix }],
      },
    },
  }));

  return new PutBucketNotificationConfigurationCommand({
    Bucket: process.env.TARGET_BUCKET_NAME!,
    NotificationConfiguration: {
      LambdaFunctionConfigurations: configurations,
    },
  });
};

export const handler = async (event: CdkCustomResourceEvent, context: Context) => {
  const suffixList = event.ResourceProperties.suffixList as string[];
  const bucketName = process.env.TARGET_BUCKET_NAME!;
  const s3Client = new S3Client({});
  let command = new PutBucketNotificationConfigurationCommand({
    Bucket: bucketName,
    NotificationConfiguration: {}, // 用于 Delete 操作
  });

  if (event.RequestType === 'Create' || event.RequestType === 'Update') {
    command = createUpdateCommand(suffixList);
  }

  await s3Client.send(command);

  return { PhysicalResourceId: `${bucketName}-notification` };
};

要点说明:

  • INVOKE_FUNC_ARN 环境变量指定要被触发的 Lambda ARN。
  • TARGET_BUCKET_NAME 环境变量指定目标 S3 Bucket 名称(或 ARN)。
  • 通过 CreateUpdate 请求类型,调用同一逻辑;删除(Delete)时则发送一个空配置,以清除 Notification。

二、在 CDK Stack 中集成 Custom Resource

接下来,在 CDK Stack 中创建两个 Lambda, 一个用于实际处理 S3 上传事件(s3-notification-invoke),另一个用于配置 Notification(s3-notification-setting)。然后,用 custom-resources.Provider 将配置 Lambda 注册为 Custom Resource 的事件处理程序。

import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Provider } from 'aws-cdk-lib/custom-resources';
import { Construct } from 'constructs';

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

    // 1. 处理 S3 上传事件的 Lambda
    const s3NotificationInvokeFunc = new lambda.Function(this, 's3-notification-invoke', {
      runtime: lambda.Runtime.NODEJS_22_X,
      code: lambda.Code.fromAsset('lambda/s3-notification-invoke'),
      handler: 'index.handler',
      functionName: 's3-notification-invoke',
    });

    // 2. 配置 S3 Notification 的 Custom Resource Lambda
    const s3NotificationSettingFunc = new lambda.Function(this, 's3-notification-setting', {
      runtime: lambda.Runtime.NODEJS_22_X,
      code: lambda.Code.fromAsset('lambda/s3-notification-setting'),
      handler: 'index.handler',
      functionName: 's3-notification-setting',
      environment: {
        INVOKE_FUNC_ARN: s3NotificationInvokeFunc.functionArn,
        TARGET_BUCKET_NAME: 'your-existing-bucket-name', // 替换为目标 Bucket 名称
      },
    });

    // 赋予 S3 调用目标 Lambda 的权限
    s3NotificationInvokeFunc.addPermission('AllowS3Invoke', {
      action: 'lambda:InvokeFunction',
      principal: new iam.ServicePrincipal('s3.amazonaws.com'),
      sourceArn: `arn:aws:s3:::your-existing-bucket-name`, // 替换为目标 Bucket ARN
    });

    // 3. 定义 Custom Resource Provider
    const provider = new Provider(this, 'S3NotificationProvider', {
      onEventHandler: s3NotificationSettingFunc,
    });

    // 4. 创建 Custom Resource,传入后缀列表参数
    new cdk.CustomResource(this, 'S3NotificationCustomResource', {
      serviceToken: provider.serviceToken,
      properties: {
        suffixList: ['.jpg', '.pdf'], // 根据需要添加后缀
      },
    });
  }
}

关键步骤:

  • 部署通知处理 Lambdas3-notification-invoke 用于处理来自 S3 的事件。
  • 部署配置 Lambdas3-notification-setting 负责对已有 Bucket 调用 PutBucketNotificationConfiguration
  • 添加权限:允许 S3 对 s3-notification-invoke Lambda 发起调用。
  • 注册 Custom Resource:CDK 在 deploy 时,自动调用配置 Lambda,完成 Notification 的创建、更新或删除。

三、总结

通过以上示例,我们利用 CDK 的 Custom Resource 机制,灵活地为 CDK 本身不支持的场景(如对已有资源进行额外配置)提供了可编程的解决方案。你可以将此模式推广到各种需要“落地”原生 SDK API、或自定义 AWS 资源行为的场景中,从而让 CDK 的能力更为强大与全面。

关于

分享技术见解、经验和思考的个人博客

联系方式

  • Email: hushukang_blog@proton.me
  • GitHub

© 2025 码中赤兔. 版权所有