46

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

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Scotty Waggoner
  • 3,083
  • 2
  • 26
  • 40

11 Answers11

78

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>"
        }
      }
    }
  ]
}
  • 21
    To where should I add the above policy document? – gayashanbc Jun 12 '18 at 07:53
  • The object `AWS::Lambda::Permission` has properties like `SourceArn` etc. according to the S3 documentation. I don't see a policy document prop there. See also https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-permission.html – Tom Aug 15 '18 at 15:46
  • He is more likely to get this error from an implicit circular dependency as described at https://aws.amazon.com/premiumsupport/knowledge-center/unable-validate-circular-dependency-cloudformation/ – Tom Aug 15 '18 at 16:10
  • Great, add permission firstly and then create notification configuration! – zgcharley Aug 29 '18 at 14:42
  • 7
    we are facing this same issue.where do we need to add this policy? – kalyan Mar 07 '19 at 18:10
  • 1
    For anyone still wondering where to add: Use the AWS CLI to do this. Examples are here: https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-serviceinvoke – Rahul Bharadwaj Dec 08 '20 at 06:04
  • 2
    WHERE TO ADD: Go to Lambda > Select your function -> Configuration -> Permissions -> Resource-Based Policy -> Add Permissions -> AWS Service -> S3 -> populate the wizard, use `lambda:InvokeFunction` – oozie _at_ concourse.farm Apr 15 '21 at 16:12
  • 2
    How to create in `terraform` : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission – grnc Nov 16 '21 at 12:50
  • The Resource Policy solution, applies to a DIFFERENT Lambda Trigger error: Not authorized to invoke function https://aws.amazon.com/premiumsupport/knowledge-center/lambda-invoke-error-s3-bucket-permission/ Error here is: "Error Code: InvalidArgument" Keep in mind that the part that says "Unable to validate the following destination configurations" is a generic error that points to many possible causes and it shouldn't be considered as the definite one. For our case it couldn't be the Resource policy because the lambda ALREADY has a trigger on the SAME bucket to a different subfolder. – eco Dec 29 '21 at 18:51
  • I have just added the below code and it worked, thank you @davor.obilinovic ``` RawInputBucketS3LambdaInvokePermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt MyLambda.Arn Principal: s3.amazonaws.com SourceAccount: !Sub ${AWS::AccountId} SourceArn: !Sub arn:aws:s3:::${CustomerName}-${EnvType}-rawinputbucket ``` – Jagadeesh Jan 11 '22 at 13:01
  • See my answer below (https://stackoverflow.com/a/74144579/771650) for an explanation of how existing event notifications can also interfere. – Devesh Oct 20 '22 at 18:20
11

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);
    }
  });
}
Scotty Waggoner
  • 3,083
  • 2
  • 26
  • 40
  • 1
    OMG!!! It worked for me! The documentation example is totally wrong, and wasted my half day ! https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html#Lambda.Client.add_permission – Zhli Apr 23 '19 at 11:53
  • Thanks a lot! I'm sure it worked for some time with '/*' but after an update I got errors. After chaning it it works again! – Thomas Jun 28 '19 at 12:00
2

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-

2

You have to add s3 resource based policy on lambda too. From your Lambda, go to Configurations > Permissions > Resource-based policy enter image description here.

Explained in more details here

Anum Sheraz
  • 2,383
  • 1
  • 29
  • 54
1

It may be helpful to look here: AWS Lambda : creating the trigger

Theres kind of an unclear error when you have conflicting events in a bucket. You need to purge the other events to create a new one.

tylercomp
  • 871
  • 3
  • 13
  • 25
1

For me it was a totally different issue causing the "Unable to validate the following destination configurations" error. Apparently, there was an old Lambda function on the same bucket, that was dropped a while before. However, AWS does not always remove the event notification from the S3 bucket, even if the old Lambda and its trigger are long gone.

This causes the conflict and the strange error message.

Resolution - Navigate to the S3 bucket => Properties => Event notifications, and drop any old remaining events still defined.

After this, everything went back to normal and worked like a charm.

Good luck!

1

I faced the same com.amazonaws.services.s3.model.AmazonS3Exception: Unable to validate the following destination configurations error when I tried to execute putBucketNotificationConfiguration

Upon checking around, I found that every time you update the bucket notification configuration, AWS will do a test notification check on all the existing notification configurations. If any of the test fails, say for the reason such as you removed the destination lambda or SNS topic of an older configuration, AWS will fail the entire bucket notification configuration request with above exception.

To address this, either identify/fix the configuration that is failing the test or remove all the existing configurations(if plausible) in the bucket using aws s3api put-bucket-notification-configuration --bucket=myBucketName --notification-configuration="{}" and then try updating the bucket configuration.

Saddam
  • 11
  • 1
0

The Console is another way to allow s3 to invoke a lambda:

Note

When you add a trigger to your function with the Lambda console, the console updates the function's resource-based policy to allow the service to invoke it. To grant permissions to other accounts or services that aren't available in the Lambda console, use the AWS CLI.

So you just need to add and configure an s3 trigger to your lambda from aws console

https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html

Spiff
  • 3,873
  • 4
  • 25
  • 50
  • 1
    True. Initially I thought I had to add a new permission entry for every bucket. My buckets were dynamically created. Omitting the `SourceArn` and adding only a single permission for all buckets is the solution and therefore creating a single permission through the web console should work just as well. – Scotty Waggoner Apr 22 '19 at 20:58
0

For me it was Lambda expecting permission over the entire bucket and not the bucket and keys

supa_command
  • 119
  • 2
  • 7
0

If the accepted answer by @davor.obilinovic (https://stackoverflow.com/a/47674337) still doesn't fix it, note that you'll get this error even if an existing event notification is now invalid.

Ex. Let's say you configured bucketA/prefix1 to trigger lambda1, bucketA/prefix2 to trigger lambda2. After a while you decide to delete lambda2 but don't remove the event notification for bucketA/prefix2.

Now if you try to configure bucketA/prefix3 to trigger lambda3, you'll see this error "Unable to validate the following destination configurations" even though you are trying to only add lambda3 and lambda3 is configured correctly as @davor.obilinovic answered.

Additional Context: The reason for this behavior is because AWS does not have a "add_event_notification" api. They only have a "put_bucket_notification" which takes in the complete list of all the old event notifications plus the new one that we want to add. So each time we want to add an event notification, we have to send the entire list and they validate the entire list. It would have been easier/clearer had they specified which "following destination" they were referring to in their error message.

Devesh
  • 859
  • 8
  • 17
0

for my case, delete all unused event that was setup in S3 bucket

Tuan Anh
  • 88
  • 6