AWS Lambda S3.getObject throws “Access Denied”, but only when running locally

I am using AWS Lambda and serverless framework to build a service which uses S3 to store a file.
The lambda function ("hello") works perfectly when deployed to the cloud (it has an http endpoint, I invoke it from the browser).
On the contrary, when invoked locally (serverless invoke local --function hello) I get "access denied" error:

{
    "errorMessage": "error getting object from S3: AccessDenied: Access Denied",
    "errorType": "Error",
    "stackTrace": [
        "Error: error getting object from S3: AccessDenied: Access Denied",
        "    at module.exports.hello (/....js:24:9)",
        "    at processTicksAndRejections (internal/process/task_queues.js:93:5)"
    ]
}

This is my simplified function:

'use strict';

const aws = require("aws-sdk");
const s3 = new aws.S3({ region: "eu-west-1" });
const bucket = "my-bucket-dev";
const key = "ads.json";

module.exports.hello = async (event) => {
  let obj = null;
  try {
    return await s3.getObject({ Bucket: bucket, Key: key }).promise();
  } catch(err) {
    throw new Error(`error getting object from S3: ${err}`);
  }
  return {
    statusCode: 200,
    body: JSON.stringify({
        message: `Hello function executed successfully!`,
        obj,
        input: event,
      },
      null,
      2
    ),
  };
}

This is my serveless.yml (the relevant parts):

service: my-service
frameworkVersion: '2'  
provider:
  name: aws
  runtime: nodejs12.x
  profile: serverless
  lambdaHashingVersion: 20201221
  apiGateway:
    shouldStartNameWithService: true
  stage: ${opt:stage, "dev"}
  region: eu-west-1
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - "s3:ListBucket"
          Resource: { "Fn::Join": [ "", [ "arn:aws:s3:::", "my-service-dev" ] ] }
        - Effect: "Allow"
          Action:
            - "s3:GetObject"
            - "s3:PutObject"
            - "s3:DeleteObject"
          Resource: { "Fn::Join": [ "", [ "arn:aws:s3:::", "my-service-dev", "/*" ] ] }
functions:
  hello:
    handler: index.hello
    events:
      - http:
          method: get
          path: hello
      - s3: my-bucket-dev

I did read somewhere that IAM role statements differ for local and cloud execution, but if it right, I'm a bit confused...
Can anybody clarify?
How should I correct my serverless.yml to avoid local invocation errors?

Answers 1

  • To quote the documentation for invoke-local:

    When you use serverless invoke local, the situation is quite different: the role isn't available (the function is executed on your local machine), so unless you set a different user directly in the code (or via a key pair of environment variables), the AWS SDK will use the default profile specified inside your AWS credential configuration file.

    So, for local invocation, either configure local AWS credentials appropriately in ~/.aws/credentials or provide them via environment variables.


Related Articles