首页 > 解决方案 > Cloud Formation:S3 存储桶和 Lambda 的独立 cloudformation 模板

问题描述

我创建了一个 cloudformation 模板来配置一个 S3 存储桶,其中包含一个将调用 lambda 函数的事件通知。每当在存储桶中创建新对象时,都会触发 Lamba。我遇到的问题是,当我删除堆栈时,存储桶也被删除了。出于调试和测试目的,我不得不删除堆栈。

AWSTemplateFormatVersion: '2010-09-09'
Description: Upload an object to an S3 bucket, triggering a Lambda event, returning the object key as a Stack Output.
Parameters:
  Body:
    Description: Stack to create s3 bucket and the lambda trigger
    Type: String
    Default: Test
  BucketName:
    Description: S3 Bucket name
    Type: String
    Default: image-process-bucket

Resources:
  ImageProcessorExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: 
            Service: lambda.amazonaws.com
          Action: 'sts:AssumeRole'
      Path: /
      ManagedPolicyArns:
      - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
      - PolicyName: S3Policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - 's3:PutObject'
            - 'S3:DeleteObject'
            Resource: !Sub "arn:aws:s3:::${BucketName}/*"

  ImageProcessor:
    Type: AWS::Lambda::Function
    Properties:
      Description: Prints the filename
      Handler: imageProcessor.handler
      Role: !GetAtt ImageProcessorExecutionRole.Arn
      Code: .
      Runtime: nodejs12.x
      Environment:
        Variables:
          BucketName:
            Ref: BucketName

  Bucket:
    Type: AWS::S3::Bucket
    DependsOn: BucketPermission
    Properties:
      BucketName: !Ref BucketName
      NotificationConfiguration:
        LambdaConfigurations:
        - Event: 's3:ObjectCreated:*'
          Function: !GetAtt ImageProcessor.Arn

  BucketPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref ImageProcessor
      Principal: s3.amazonaws.com
      SourceAccount: !Ref "AWS::AccountId"
      SourceArn: !Sub "arn:aws:s3:::${BucketName}"

为了解决这个问题,我使用 Outputs 将两个资源分开在单独的模板上。问题是我无法删除 Lambda 函数堆栈,因为它被 Bucket 堆栈引用。

  1. 我想知道什么是正确的方法。是否真的需要将这两种资源分开。我相信需要经常更改 lambda 函数。
  2. 如果是,那么正确的方法是什么。
  3. 如果不是,我应该如何处理进行更改的必要性。
  4. 使用 Outputs 和 Imports 的方法将始终创建依赖项并且不允许删除。这是任何资源中的通用行为。在这种情况下我们如何处理删除。使用这种方法好不好
Description: Upload an object to an S3 bucket, triggering a Lambda event, returning the object key as a Stack Output.
Parameters:
 Body:
   Description: Stack to create s3 bucket and the lambda trigger
   Type: String
   Default: Test
 BucketName:
   Description: S3 Bucket name
   Type: String
   Default: image-process-bucket

Resources:
 ImageProcessorExecutionRole:
   Type: AWS::IAM::Role
   Properties:
     AssumeRolePolicyDocument:
       Version: '2012-10-17'
       Statement:
       - Effect: Allow
         Principal: 
           Service: lambda.amazonaws.com
         Action: 'sts:AssumeRole'
     Path: /
     ManagedPolicyArns:
     - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
     Policies:
     - PolicyName: S3Policy
       PolicyDocument:
         Version: '2012-10-17'
         Statement:
         - Effect: Allow
           Action:
           - 's3:PutObject'
           - 'S3:DeleteObject'
           Resource: !Sub "arn:aws:s3:::${BucketName}/*"

 ImageProcessor:
   Type: AWS::Lambda::Function
   Properties:
     Description: Prints the filename
     Handler: imageProcessor.handler
     Role: !GetAtt ImageProcessorExecutionRole.Arn
     Code: .
     Runtime: nodejs12.x
     Environment:
       Variables:
         BucketName:
           Ref: BucketName
Outputs:
 ImageProcessingARN:
   Description: ARN of the function
   Value: 
     Fn::Sub: ${ImageProcessor.Arn}
   Export:
     Name: ImageProcessingARN
 ImageProcessingName:
   Description: Name of the function
   Value: !Ref ImageProcessor
   Export:
     Name: ImageProcessingName
AWSTemplateFormatVersion: '2010-09-09'
Description: Test
Parameters:
  BucketName:
    Description: Name of the bucket
    Type: String
    Default: imageprocess-bucket

Resources:
  Bucket:
    Type: AWS::S3::Bucket
    DependsOn: BucketPermission
    Properties:
      BucketName: !Ref BucketName
      NotificationConfiguration:
        LambdaConfigurations:
        - Event: 's3:ObjectCreated:*'
          Function: 
            Fn::ImportValue: ImageProcessingARN

  BucketPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: 
        Fn::ImportValue:  ImageProcessingName
      Principal: s3.amazonaws.com
      SourceAccount: !Ref "AWS::AccountId"
      SourceArn: !Sub "arn:aws:s3:::${BucketName}"

标签: amazon-web-servicesamazon-s3aws-lambdaamazon-cloudformation

解决方案


  1. 没有正确的方法,它几乎总是取决于您的独特情况。严格来说,不需要将不同 CloudFormation 模板中的资源分开。变化很大的 lambda 函数也不是分离资源的充分理由。
  2. 您似乎在两个不同的堆栈中正确地分离了资源。您只是不喜欢必须先删除 S3 存储桶,因为这会使调试更加困难。

  3. 如果我的假设是正确的,即您想频繁地删除或更新 Lambda CloudFormation 堆栈而不想删除 S3 存储桶,那么对于这个问题至少有 2 种解决方案:

    • 在您的 S3 存储桶上放置一个删除策略和一个UpdateReplacePolicy 。通过添加这些策略,您可以删除 CloudFormation 堆栈,同时保留 S3 存储桶。这将允许您将 S3 存储桶和 Lambda 函数保留在一个 CloudFormation 模板中。要再次创建新堆栈,请从模板中删除 S3 存储桶资源,稍后手动将资源重新导入 CloudFormation 堆栈。
    • 使用队列配置作为通知配置。如果您计划将 CloudFormation 模板分离为 S3 存储桶模板和 Lambda 函数模板(基于更改频率和两个模板之间的依赖关系的决定),这是一个很好的方法。在 S3 存储桶模板中放置一个SQS 队列。基于 S3 存储桶模板创建 CloudFormation 堆栈。在 Lambda 函数堆栈中使用 SQS arn(作为 CloudFormation 模板配置参数或使用 ImportValue 内部函数)并让SQS 触发 Lambda 函数. 我认为这是最好的方法,因为您现在可以删除 Lambda 函数堆栈,而无需删除 S3 存储桶堆栈。通过这种方式,您可以有效地减少两个 CloudFormation 堆栈之间的耦合,因为您使 S3 存储桶堆栈中的 SQS 不知道潜在的 Lambda 函数侦听器。

    4:我觉得还是可以先删除S3存储桶CloudFormation栈,再删除Image Processing Lambda CloudFormation栈。尽管我认为这不是您通常想要做的事情。


推荐阅读