S3 Bucket Lambda Event: Unable to validate the following destination configurations

46,501

Solution 1

You are getting this message because your s3 bucket is missing permissions for invoking your lambda function.

According to AWS documentation! there are two types of permissions required:

  1. Permissions for your Lambda function to invoke services
  2. Permissions for Amazon S3 to invoke your Lambda function

You should create an object of type 'AWS::Lambda::Permission' and it should look similar to this:

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "<optional>",
      "Effect": "Allow",
      "Principal": {
        "Service": "s3.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "<ArnToYourFunction>",
      "Condition": {
        "StringEquals": {
          "AWS:SourceAccount": "<YourAccountId>"
        },
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:s3:::<YourBucketName>"
        }
      }
    }
  ]
}

Solution 2

Finally looked at this again after a year. This was a hackathon project from last year that we revisted. @davor.obilinovic's answer was very helpful in pointing me to the Lambda permission I needed to add. Still took me a little bit to figure out exactly what I needed it to look like.

Here are the AWS JavaScript SDK and Lambda API docs https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#addPermission-property https://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html

The JS SDK docs have this line:

SourceArn: "arn:aws:s3:::examplebucket/*",

I couldn't get it working for the longest time and was still getting the Unable to validate the following destination configurations error.

Changing it to

SourceArn: "arn:aws:s3:::examplebucket",

fixed that issue. The /* was apparently wrong and I should have looked at the answer I got here more closely but was trying to follow the AWS docs.

After developing for a while and creating lots of buckets, Lambda permissions and S3 Lambda notifications, calling addPermission started throwing a The final policy size (...) is bigger than the limit (20480). Adding new, individual, permissions for each bucket adds them to the bottom of the Lambda Function Policy and apparently that policy has a max size.

The policy doesn't seem editable in the AWS Management Console so I had fun deleting each entry with the SDK. I copied the policy JSON, pulled the Sids out and called removePermission in a loop (which threw rate limit errors and I had to run it many times).

Finally I discovered that omitting the SourceArn key will give Lambda permission to all S3 buckets.

Here's my final code using the SDK to add the permission I needed. I just ran this once for my function.

const aws = require('aws-sdk');

aws.config.update({
  accessKeyId:     process.env.AWS_ACCESS,
  secretAccessKey: process.env.AWS_SECRET,
  region:          process.env.AWS_REGION,
});

// Creates Lambda Function Policy which must be created once for each Lambda function
// Must be done before calling s3.putBucketNotificationConfiguration(...)
function createLambdaPermission() {
  const lambda = new aws.Lambda();

  const params = {
    Action:        'lambda:InvokeFunction',
    FunctionName:  process.env.AWS_LAMBDA_ARN,
    Principal:     's3.amazonaws.com',
    SourceAccount: process.env.AWS_ACCOUNT_ID,
    StatementId:   `example-S3-permission`,
  };

  lambda.addPermission(params, function (err, data) {
    if (err) {
      console.log(err);
    } else {
      console.log(data);
    }
  });
}

Solution 3

If it still useful for someone, this is how I add the permission to the lambda function using java:

AWSLambda client = AWSLambdaClientBuilder.standard().withRegion(clientRegion).build();

AddPermissionRequest requestLambda = new AddPermissionRequest()
                    .withFunctionName("XXXXX")
                    .withStatementId("XXXXX")
                    .withAction("lambda:InvokeFunction")
                    .withPrincipal("s3.amazonaws.com")
                    .withSourceArn("arn:aws:s3:::XXXXX" )
                    .withSourceAccount("XXXXXX");

                    client.addPermission(requestLambda);

Please check https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/lambda/AWSLambda.html#addPermission-com.amazonaws.services.lambda.model.AddPermissionRequest-

Share:
46,501
Scotty Waggoner
Author by

Scotty Waggoner

Updated on December 11, 2021

Comments

  • Scotty Waggoner
    Scotty Waggoner over 2 years

    I'm trying to create an S3 bucket and immediately assign a lambda notification event to it.

    Here's the node test script I wrote:

    const aws = require('aws-sdk');
    const uuidv4 = require('uuid/v4');
    
    aws.config.update({
      accessKeyId: 'key',
      secretAccessKey:'secret',
      region: 'us-west-1'
    });
    
    const s3 = new aws.S3();
    
    const params = {
      Bucket: `bucket-${uuidv4()}`,
      ACL: "private",
      CreateBucketConfiguration: {
        LocationConstraint: 'us-west-1'
      }
    };
    
    s3.createBucket(params, function (err, data) {
      if (err) {
        throw err;
      } else {
        const bucketUrl = data.Location;
    
        const bucketNameRegex = /bucket-[a-z0-9\-]+/;
        const bucketName = bucketNameRegex.exec(bucketUrl)[0];
    
        const params = {
          Bucket: bucketName,
          NotificationConfiguration: {
            LambdaFunctionConfigurations: [
              {
                Id: `lambda-upload-notification-${bucketName}`,
                LambdaFunctionArn: 'arn:aws:lambda:us-west-1:xxxxxxxxxx:function:respondS3Upload',
                Events: ['s3:ObjectCreated:CompleteMultipartUpload']
              },
            ]
          }
        };
    
        // Throws "Unable to validate the following destination configurations" until an event is manually added and deleted from the bucket in the AWS UI Console
        s3.putBucketNotificationConfiguration(params, function(err, data) {
          if (err) {
            console.error(err);
            console.error(this.httpResponse.body.toString());
          } else {
            console.log(data);
          }
        });
      }
    });
    

    The creation works fine but calling s3.putBucketNotificationConfiguration from the aws-sdk throws:

    { InvalidArgument: Unable to validate the following destination configurations
        at Request.extractError ([...]/node_modules/aws-sdk/lib/services/s3.js:577:35)
        at Request.callListeners ([...]/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
        at Request.emit ([...]/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
        at Request.emit ([...]/node_modules/aws-sdk/lib/request.js:683:14)
        at Request.transition ([...]/node_modules/aws-sdk/lib/request.js:22:10)
        at AcceptorStateMachine.runTo ([...]/node_modules/aws-sdk/lib/state_machine.js:14:12)
        at [...]/node_modules/aws-sdk/lib/state_machine.js:26:10
        at Request.<anonymous> ([...]/node_modules/aws-sdk/lib/request.js:38:9)
        at Request.<anonymous> ([...]/node_modules/aws-sdk/lib/request.js:685:12)
        at Request.callListeners ([...]/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
      message: 'Unable to validate the following destination configurations',
      code: 'InvalidArgument',
      region: null,
      time: 2017-11-10T02:55:43.004Z,
      requestId: '9E1CB35811ED5828',
      extendedRequestId: 'tWcmPfrAu3As74M/0sJL5uv+pLmaD4oBJXwjzlcoOBsTBh99iRAtzAloSY/LzinSQYmj46cwyfQ=',
      cfId: undefined,
      statusCode: 400,
      retryable: false,
      retryDelay: 4.3270874729153475 }
    
    <?xml version="1.0" encoding="UTF-8"?>
    <Error>
        <Code>InvalidArgument</Code>
        <Message>Unable to validate the following destination configurations</Message>
        <ArgumentName1>arn:aws:lambda:us-west-1:xxxxxxxxxx:function:respondS3Upload, null</ArgumentName1>
        <ArgumentValue1>Not authorized to invoke function [arn:aws:lambda:us-west-1:xxxxxxxxxx:function:respondS3Upload]</ArgumentValue1>
        <RequestId>9E1CB35811ED5828</RequestId>
        <HostId>tWcmPfrAu3As74M/0sJL5uv+pLmaD4oBJXwjzlcoOBsTBh99iRAtzAloSY/LzinSQYmj46cwyfQ=</HostId>
    </Error>
    

    I've run it with a role assigned to lambda with what I think are all the policies it needs. I could be missing something. I'm using my root access keys to run this script.

    Role

    I've thought it might be a timing error where S3 needs time to create the bucket before adding the event, but I've waited a while, hardcoded the bucket name, and run my script again which throws the same error.

    The weird thing is that if I create the event hook in the S3 UI and immediately delete it, my script works if I hardcode that bucket name into it. It seems like creating the event in the UI adds some needed permissions but I'm not sure what that would be in the SDK or in the console UI.

    S3 Event Config

    Any thoughts or things to try? Thanks for your help