Создайте уведомление Lambda в ведро S3 с облачным флагом

Я пытаюсь создать триггер S3 для функции Lambda в шаблоне CloudFormation. Ведро S3 уже существует, и создается функция Lambda.

В этом говорится, что невозможно изменить ранее существовавшую инфраструктуру (S3 в этом случае) с помощью CFT, но this кажется, что ведро должно быть уже существующим.

  • Кажется, что триггер не может быть создан с использованием типа CFT "AWS:: Lambda..." и что службе-источнику необходимо создать триггер. В моем случае это NotificationConfiguration-LambdaConfiguration для ведра s3. Правильно ли это?

  • Когда я пытаюсь добавить NotificationConfiguration в существующий ведро S3 с CFT, он говорит, что я не могу. Есть ли способ сделать это?

Ответ 1

К сожалению, официальный шаблон AWS::CloudFormation позволяет вам управлять Amazon S3 NotificationConfiguration как свойство NotificationConfiguration родительского ресурса AWS::S3::Bucket, что означает, что вы не можете присоединить эту конфигурацию к любому существующему сегменту, у вас есть применить его к управляемой CloudFormation корзине, чтобы он работал.

Обходной путь - реализовать API- вызов PUT Bucket Notification напрямую как пользовательский ресурс с лямбда-поддержкой, используя API-вызов putBucketNotificationConfiguration. Однако, поскольку изменение NotificationConfiguration в сегментах S3 ограничено создателем сегмента, вам также необходимо добавить ресурс AWS::S3::BucketPolicy предоставляющий доступ к вашей функции Lambda, к действию s3:PutBucketNotification.

Здесь полный, автономный шаблон CloudFormation, который демонстрирует, как запускать лямбда-функцию всякий раз, когда файл добавляется в существующую BucketConfiguration S3, используя 2 настраиваемых ресурса с лямбда-поддержкой (BucketConfiguration для настройки конфигурации уведомлений S3Object, S3Object для загрузки объекта в BucketWatcher) и третья лямбда-функция (BucketWatcher для запуска условия BucketWatcher при BucketWatcher в BucketWatcher).

Launch Stack

Description: Upload an object to an S3 bucket, triggering a Lambda event, returning the object key as a Stack Output.
Parameters:
  Key:
    Description: S3 Object key
    Type: String
    Default: test
  Body:
    Description: S3 Object body content
    Type: String
    Default: TEST CONTENT
  BucketName:
    Description: S3 Bucket name (must already exist)
    Type: String
Resources:
  BucketConfiguration:
    Type: Custom::S3BucketConfiguration
    DependsOn:
    - BucketPermission
    - NotificationBucketPolicy
    Properties:
      ServiceToken: !GetAtt S3BucketConfiguration.Arn
      Bucket: !Ref BucketName
      NotificationConfiguration:
        LambdaFunctionConfigurations:
        - Events: ['s3:ObjectCreated:*']
          LambdaFunctionArn: !GetAtt BucketWatcher.Arn
  S3BucketConfiguration:
    Type: AWS::Lambda::Function
    Properties:
      Description: S3 Object Custom Resource
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          var AWS = require('aws-sdk');
          var s3 = new AWS.S3();
          exports.handler = function(event, context) {
            var respond = (e) => response.send(event, context, e ? response.FAILED : response.SUCCESS, e ? e : {});
            process.on('uncaughtException', e=>failed(e));
            var params = event.ResourceProperties;
            delete params.ServiceToken;
            if (event.RequestType === 'Delete') {
              params.NotificationConfiguration = {};
              s3.putBucketNotificationConfiguration(params).promise()
                .then((data)=>respond())
                .catch((e)=>respond());
            } else {
              s3.putBucketNotificationConfiguration(params).promise()
                .then((data)=>respond())
                .catch((e)=>respond(e));
            }
          };
      Timeout: 30
      Runtime: nodejs4.3
  BucketPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref BucketWatcher
      Principal: s3.amazonaws.com
      SourceAccount: !Ref "AWS::AccountId"
      SourceArn: !Sub "arn:aws:s3:::${BucketName}"
  BucketWatcher:
    Type: AWS::Lambda::Function
    Properties:
      Description: Sends a Wait Condition signal to Handle when invoked
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          exports.handler = function(event, context) {
            console.log("Request received:\n", JSON.stringify(event));
            var responseBody = JSON.stringify({
              "Status" : "SUCCESS",
              "UniqueId" : "Key",
              "Data" : event.Records[0].s3.object.key,
              "Reason" : ""
            });
            var https = require("https");
            var url = require("url");
            var parsedUrl = url.parse('${Handle}');
            var options = {
                hostname: parsedUrl.hostname,
                port: 443,
                path: parsedUrl.path,
                method: "PUT",
                headers: {
                    "content-type": "",
                    "content-length": responseBody.length
                }
            };
            var request = https.request(options, function(response) {
                console.log("Status code: " + response.statusCode);
                console.log("Status message: " + response.statusMessage);
                context.done();
            });
            request.on("error", function(error) {
                console.log("send(..) failed executing https.request(..): " + error);
                context.done();
            });
            request.write(responseBody);
            request.end();
          };
      Timeout: 30
      Runtime: nodejs4.3
  Handle:
    Type: AWS::CloudFormation::WaitConditionHandle
  Wait:
    Type: AWS::CloudFormation::WaitCondition
    Properties:
      Handle: !Ref Handle
      Timeout: 300
  S3Object:
    Type: Custom::S3Object
    DependsOn: BucketConfiguration
    Properties:
      ServiceToken: !GetAtt S3ObjectFunction.Arn
      Bucket: !Ref BucketName
      Key: !Ref Key
      Body: !Ref Body
  S3ObjectFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: S3 Object Custom Resource
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          var AWS = require('aws-sdk');
          var s3 = new AWS.S3();
          exports.handler = function(event, context) {
            var respond = (e) => response.send(event, context, e ? response.FAILED : response.SUCCESS, e ? e : {});
            var params = event.ResourceProperties;
            delete params.ServiceToken;
            if (event.RequestType == 'Create' || event.RequestType == 'Update') {
              s3.putObject(params).promise()
                .then((data)=>respond())
                .catch((e)=>respond(e));
            } else if (event.RequestType == 'Delete') {
              delete params.Body;
              s3.deleteObject(params).promise()
                .then((data)=>respond())
                .catch((e)=>respond(e));
            } else {
              respond({Error: 'Invalid request type'});
            }
          };
      Timeout: 30
      Runtime: nodejs4.3
  LambdaExecutionRole:
    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}/${Key}"
  NotificationBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref BucketName
      PolicyDocument:
        Statement:
          - Effect: "Allow"
            Action:
            - 's3:PutBucketNotification'
            Resource: !Sub "arn:aws:s3:::${BucketName}"
            Principal:
              AWS: !GetAtt LambdaExecutionRole.Arn
Outputs:
  Result:
    Value: !GetAtt Wait.Data

Ответ 2

не могли бы вы помочь, как я могу создать событие для корзины S3, которая уже существует, я не хочу перезаписывать его

    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${ProductFeedBucket}
#       NotificationConfiguration:
#         LambdaConfigurations:
#         - 
#           Function: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:function_name'
#           Event: "s3:ObjectCreated:*"
#           Filter:
#             S3Key:
#               Rules:
#               - 
#                 Name: suffix
#                 Value: .json

#   LambdaInvokePermission:
#     Type: AWS::Lambda::Permission
#     Properties:
#       FunctionName: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:function_name'
#       Action: "lambda:InvokeFunction"
#       Principal: "s3.amazonaws.com"
#       SourceArn: !Sub 'arn:aws:s3:::${ProductFeedBucket}' '''