Search code examples
aws-lambdaamazon-cloudfrontserverless-frameworkserverless

The Lambda function associated with the CloudFront distribution is invalid or doesn't have the required permissions


So as a pretext, I've got less than no idea what to do about this. I've researched for about two hours and normally I would just keep going but none of the information I've found has been useful. I suspect this would have something to do with the YAML (serverless.yml) file but I am unsure. I've made several updates to the file so I will post the initial code and the current code though no difference has been made. The code worked flawlessly in development but throws errors in production. You can see https://www.evote.space to replicate this.

Current

myNextApplication:
  service: myService
  component: "@sls-next/[email protected]"
  provider:
    name: aws
    runtime: nodejs12.x
    stage: dev
    profile: evote
    iam:
    role: rolenamegoesherebutnotonstackoverflow
  inputs:
    domain: "evote.space"
  functions:
    createuser:
      handler: data.createuser
    readTable:
      handler: data.readTable
  resources:
    Resources:
      usersTable:
        Type: AWS::DynamoDB::Table
        Properties:
          TableName: Users
          AttributeDefinitions:
          - AttributeName: userHash
            AttributeType: N
          KeySchema:
          - AttributeName: userHash
            KeyType: HASH
      votersTable:
        Type: AWS::DynamoDB::Table
        Properties:
          TableName: Voters
          AttributeDefinitions:
          - AttributeName: voterHash
            AttributeType: N
          KeySchema:
          - AttributeName: voterHash
            KeyType: HASH
      electionsTable:
        Type: AWS::DynamoDB::Table
        Properties:
          TableName: Elections
          AttributeDefinitions:
          - AttributeName: electionHash
            AttributeType: N
          KeySchema:
          - AttributeName: electionHash
            KeyType: HASH
      ballotsTable:
        Type: AWS::DynamoDB::Table
        Properties:
          TableName: Ballots
          AttributeDefinitions:
          - AttributeName: ballotHash
            AttributeType: N
          KeySchema:
          - AttributeName: ballotHash
            KeyType: HASH

Initial (At first deployment with error)

myNextApplication:
  service: myService
  component: "@sls-next/[email protected]"
  provider:
    name: aws
    runtime: nodejs12.x
    stage: dev
    profile: evote
  inputs:
    domain: "evote.space"

My code base is massive and made up of many pages and components. So far all that I've made is a login function but on the sign up page where it calls the api to return users (for duplicate email validation), it returns the error we're all too familiar with "Unexpected token < in JSON at position 0" if you go back then load the page again you can get the console to reveal a source on that error which reads:

503 ERROR The request could not be satisfied. The Lambda function associated with the CloudFront distribution is invalid or doesn't have the required permissions. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner. If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation. Generated by cloudfront (CloudFront) Request ID: No0_qVJ3gcOpg48rMXqvgyipx4wKWmV-hRewQblZ-loyaaiVJLqGIA==

So yeah, if you can help, please do.

Edit: The code that's causing the issue is the following block

NewUser.getInitialProps = async ({ req }) => {
  if (req) {
    // this is server side
    return {
      users: await data.readTable("Users")
    };
  } else {
    // we are client side
    const response = await fetch("/api/users");
    return { users: await response.json() };
  }
};

And the api that handled this originally looked like this:

import data from "../../../data"

export default async (req, res) => {
  console.log("/api/users HIT!");
  res.status(200).json(await data.readTable("Users"));
};

But I changed it so that it can be marked as a lambda so now it looks like this (though it made no difference):

import data from "../../../data";

module.exports.read = async (event, context, callback) => {
  console.log("/api/users HIT!");
  callback(null, {statusCode: 200}).json(await data.readTable("Users"));
}

Solution

  • So after careful research I did the following and this is apparently a pretty common issue so I recommend anyone else suffering from this issue do exactly the following. Please remember this is with the serverless-nextjs component not just the serverless framework although the same may apply there:

    1. I carefully dug through the repository README and followed all instructions.
    2. I logged into the AWS Console and found what I thought was the primary issue but was actually a tertiary problem; The Lambda@Edge was only configured with basic CloudWatch Logging permissions. I updated it with all of the necessary privileges while ensuring best practices
    3. After updating your Lambda make sure you deploy it to Lambda@Edge (Under Actions Menu)
    4. Then I navigated to CloudWatch and checked the logs in the correct region and found errors after calling into the API.
    5. Somehow my DynamoDB tables got deleted or weren't properly replicated so I redeployed them from my SDK with npm run infra and after.

    This experience is always a good one to have. Your by no means invincible to really stupid errors in your code. And 99% of the time if you're stuck and thinking it's complex and that it's unrealistic that you'll fix it, step back and ask, "What's the dumbest thing it could be?"

    And it's always that.