Comment résoudre l'erreur que je reçois lorsque j'abonne une fonction AWS Lambda à une source d'événement basée sur la technologie push dans AWS CloudFormation ?

Date de la dernière mise à jour : 15/10/2020

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, telles 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. Par 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::Sub": "arn:aws:s3:::${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:*:*:log-group:/path/<log-group-name>:log-stream:<log-stream-name>"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

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 la section Comment éviter que le message d'erreur « Unable to validate the following destination configurations » (Impossible de valider les configurations des destinations suivantes) avec les notifications d'événements Lambda dans AWS CloudFormation ?


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


Besoin d'aide pour une question technique ou de facturation ?