2016-08-03 179 views
7

我正嘗試在CloudFormation模板中爲Lambda函數創建S3觸發器。 S3存儲桶已存在,並且正在創建Lambda函數。使用CloudFormation在S3存儲桶中創建Lambda通知

This表示無法使用CFT修改預先存在的基礎設施(本例中爲S3),但this似乎表示該存儲桶必須預先存在。

  1. 似乎無法使用CFT類型「AWS :: Lambda ...」創建觸發器,並且源服務需要創建觸發器。就我而言,這是s3存儲桶的NotificationConfiguration-LambdaConfiguration。這一切是否正確?

  2. 當我試圖將一個NotificationConfiguration添加到現有的帶有CFT的S3存儲桶時,它說我不能。有沒有辦法做到這一點?

+0

我相當肯定,而桶中有存在,它沒有創作模板之前存在。會在同一模板中的通知配置和lambda函數旁邊創建存儲桶以適合您的用例嗎?如果是這樣,這種方法比修改現有的基礎設施更容易幫助你。無論哪種方式都有解決方案,但其中一個更漂亮 –

+0

當您說'S3存儲桶已經存在'時,您是否還暗示該存儲桶是在CloudFormation之外創建的? – Aditya

回答

4

不幸的是,官方AWS::CloudFormation模板只允許您控制Amazon S3 NotificationConfiguration作爲父AWS::S3::Bucket資源,這意味着你不能這個配置連接到任何現有的桶的NotificationConfiguration property,你必須把它應用到CloudFormation管理的存儲桶可以工作。

解決方法是使用putBucketNotificationConfiguration JavaScript API調用直接將PUT Bucket Notification API調用作爲Lambda-backed Custom Resource。但是,由於修改S3存儲桶上的NotificationConfiguration僅限於存儲桶的創建者,因此還需要添加一個AWS::S3::BucketPolicy資源,以授予您的Lambda功能訪問s3:PutBucketNotification操作的權限。

以下是一個演示如何觸發每當一個文件被添加到現有 S3桶lambda函數,使用2 LAMBDA後援自定義資源(BucketConfiguration設置桶通知配置一個完整的,自包含的CloudFormation模板, S3Object將對象上傳到存儲區)和第三個Lambda函數(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