Comment puis-je abonner une fonction AWS Lambda à une source d'événement basée sur l'envoi dans AWS CloudFormation ?

Date de la dernière mise à jour : 23/08/2019

Je ne parviens pas à abonner ma fonction AWS Lambda à une notification d'événement Amazon Simple Storage Service (Amazon S3) ou à une rubrique Amazon Simple Notification Service (Amazon SNS) dans ma pile AWS CloudFormation. Si j'utilise la ressource AWS::Lambda::EventSourceMapping, je reçois le message d'erreur suivant : « Unrecognized event source, must be kinesis or dynamodb stream. » (« Source d'événement inconnue ; celle-ci doit être un flux Kinesis ou DynamoDB »)

Brève description

La ressource AWS::Lambda::EventSourceMapping est conçue pour les sources d'événement basées sur l'extraction, tels que les flux d'événement Amazon DynamoDB et Amazon Kinesis. Grâce aux sources d'événement basées sur l'envoi, comme les notifications d'événement Amazon S3 ou les messages Amazon SNS, la source d'événement est responsable de l'invocation de la fonction Lambda. Pour transmettre une source d'événement d'envoi pour invoquer une fonction Lambda, la stratégie de ressource de la fonction doit autoriser une source d'événement prise en charge.

Résolution

Dans votre modèle AWS CloudFormation, ajoutez une stratégie basée sur les ressources à l'aide de la ressource AWS::Lambda::Permission.

Par exemple, la stratégie suivante basée sur les ressources permet à une rubrique Amazon SNS d'invoquer une fonction Lambda :

"LambdaResourcePolicy": {
  "Type": "AWS::Lambda::Permission",
  "Properties": {
    "FunctionName" : { "Ref" : "MyFunction" },
    "Principal": "sns.amazonaws.com",
    "Action": "lambda:InvokeFunction",
    "SourceArn" : { "Ref" : "MySNSTopic" }
  }
}

Pour une source d'événement de rubrique Amazon SNS, vous devez définir une stratégie de rubrique avec les autorisations requises.

Pour une source d'événement Amazon S3, vous devez disposer d'une instruction de configuration de notification qui abonne la fonction Lambda au compartiment Amazon S3. Voici un exemple :

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "BucketPrefix": {
      "Type": "String",
      "Default": "test-bucket-name"
    }
  },
  "Resources": {
    "EncryptionServiceBucket": {
      "DependsOn": "LambdaInvokePermission",
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::Sub": "${BucketPrefix}-encryption-service"
        },
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Function": {
                "Fn::GetAtt": [
                  "AppendItemToListFunction",
                  "Arn"
                ]
              },
              "Event": "s3:ObjectCreated:*",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "suffix",
                      "Value": "zip"
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    },
    "LambdaInvokePermission": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "AppendItemToListFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": "s3.amazonaws.com",
        "SourceAccount": {
          "Ref": "AWS::AccountId"
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:aws:s3:::",
              {
                "Fn::Sub": "${BucketPrefix}-encryption-service"
              }
            ]
          ]
        }
      }
    },
    "AppendItemToListFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Role": {
          "Fn::GetAtt": [
            "LambdaExecutionRole",
            "Arn"
          ]
        },
        "Code": {
          "ZipFile": {
            "Fn::Join": [
              "",
              [
                "exports.handler = function(event, context) {",
                "console.log('Received event: ', JSON.stringify(event, null, 2));",
                "};"
              ]
            ]
          }
        },
        "Runtime": "nodejs8.10"
      }
    },
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "Policies": [
          {
            "PolicyName": "root",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "logs:*"
                  ],
                  "Resource": "arn:aws:logs:*:*:*"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

Dans l'exemple précédent, le compartiment S3 et la configuration des notifications sont créés concomitamment. L'exemple évite une dépendance circulaire en utilisant la fonction intrinsèque Fn::Join et l'attribut DependsOn pour créer les ressources dans l'ordre suivant : 1) rôle IAM, 2) fonction Lambda, 3) autorisation Lambda, puis 4) compartiment S3. Pour plus d'informations, consultez Comment éviter une dépendance circulaire lors du déploiement d'une fonction Lambda avec CloudFormation ?


Cet article vous a-t-il été utile ?

Cette page peut-elle être améliorée ?


Vous avez besoin d'aide ?