amazon-web-services - que - ec2 vs s3
Habilitar la funciĆ³n Lambda a un cubo S3 usando la formaciĆ³n de nubes (5)
Estamos creando un cubo S3 utilizando una plantilla CloudFormation. Me gustaría asociar (Agregar un evento a un grupo de S3) una función Lambda cada vez que se agregue un archivo al grupo de S3.
¿Cómo es posible a través de las plantillas de CloudFormation. ¿Cuáles son las propiedades que deben usarse en CloudFormation?
Aquí hay una plantilla completa y autónoma de CloudFormation que muestra cómo activar una función Lambda cada vez que se agrega un archivo a un grupo de S3:
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
Type: String
Resources:
Bucket:
Type: AWS::S3::Bucket
DependsOn: BucketPermission
Properties:
BucketName: !Ref BucketName
NotificationConfiguration:
LambdaConfigurations:
- Event: ''s3:ObjectCreated:*''
Function: !GetAtt BucketWatcher.Arn
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
Properties:
ServiceToken: !GetAtt S3ObjectFunction.Arn
Bucket: !Ref Bucket
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) {
console.log("Request received:/n", JSON.stringify(event));
var responseData = {};
if (event.RequestType == ''Create'') {
var params = {
Bucket: event.ResourceProperties.Bucket,
Key: event.ResourceProperties.Key,
Body: event.ResourceProperties.Body
};
s3.putObject(params).promise().then(function(data) {
response.send(event, context, response.SUCCESS, responseData);
}).catch(function(err) {
console.log(JSON.stringify(err));
response.send(event, context, response.FAILED, responseData);
});
} else if (event.RequestType == ''Delete'') {
var deleteParams = {
Bucket: event.ResourceProperties.Bucket,
Key: event.ResourceProperties.Key
};
s3.deleteObject(deleteParams).promise().then(function(data) {
response.send(event, context, response.SUCCESS, responseData);
}).catch(function(err) {
console.log(JSON.stringify(err));
response.send(event, context, response.FAILED, responseData);
});
} else {
response.send(event, context, response.SUCCESS, responseData);
}
};
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}"
Outputs:
Result:
Value: !GetAtt Wait.Data
En los documentos de AWS se establece claramente que AWS :: S3 :: Bucket se utiliza para crear un recurso. Si ya tenemos un depósito, no podemos modificarlo para agregar NotificationConfiguration. Por lo tanto, el cubo S3 no debe existir para que la plantilla anterior funcione. Deje que CloudFormation cree todos los recursos, incluido el S3 bucket.
He agregado debajo de la permanente perm junto con la configuración de notificación en la nube que se usa para crear la cubeta S3 ... ¡funcionó!
"bucketperm": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:invokeFunction",
"FunctionName": "<arnvalue>",
"Principal": "s3.amazonaws.com"
}
}
Necesita una propiedad NotificationConfiguration en su plantilla de CloudFormation. Desafortunadamente, parece que el cubo ya existe. Para solucionar esto, puede crear una pila inicial y luego actualizarla con NotificationConfiguration. Por ejemplo:
// template1.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"mylambda": {
"Type": "String"
}
},
"Resources": {
"bucketperm": {
"Type": "AWS::Lambda::Permission",
"Properties" : {
"Action": "lambda:InvokeFunction",
"FunctionName": {"Ref": "mylambda"},
"Principal": "s3.amazonaws.com",
"SourceAccount": {"Ref": "AWS::AccountId"},
"SourceArn": { "Fn::Join": [":", [
"arn", "aws", "s3", "" , "", {"Ref" : "mybucket"}]]
}
}
},
"mybucket": {
"Type": "AWS::S3::Bucket"
}
}
}
// template2.json -- adds the ConfigurationNotification
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"mylambda": {
"Type": "String"
}
},
"Resources": {
"bucketperm": {
"Type": "AWS::Lambda::Permission",
"Properties" : {
"Action": "lambda:InvokeFunction",
"FunctionName": {"Ref": "mylambda"},
"Principal": "s3.amazonaws.com",
"SourceAccount": {"Ref": "AWS::AccountId"},
"SourceArn": { "Fn::Join": [":", [
"arn", "aws", "s3", "" , "", {"Ref" : "mybucket"}]]
}
}
},
"mybucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"NotificationConfiguration": {
"LambdaConfigurations": [
{
"Event" : "s3:ObjectCreated:*",
"Function" : {"Ref": "mylambda"}
}
]
}
}
}
}
}
Puede utilizar la herramienta aws CLI para crear la pila de esta manera:
$ aws cloudformation create-stack --stack-name mystack --template-body file://template1.json --parameters ParameterKey=mylambda,ParameterValue=<lambda arn>
# wait until stack is created
$ aws cloudformation update-stack --stack-name mystack --template-body file://template2.json --parameters ParameterKey=mylambda,ParameterValue=<lambda arn>
Sí, es posible a través de Cloudformation, y lo que necesita configurar son:
1) AWS::S3::Bucket
recurso AWS::S3::Bucket
y,
2) Configuración de NotificationConfiguration
(use LambdaConfigurations
en este caso) para el recurso s3 anterior.
Documentación relacionada que necesita: