AccessDeniedException

AWS Lambda Role Permission Denied — Real Fix

Server & Cloud Intermediate 👁 16 views 📅 Jun 28, 2026

Your Lambda function can't call another AWS service. You need to add the right IAM policy. Here's the exact fix.

You set up a Lambda function, it runs fine locally, but in the AWS cloud it throws an AccessDeniedException. That's annoying. The Lambda execution role doesn't have permission to call the service you're trying to hit — like S3, DynamoDB, or Secrets Manager. Let's fix it.

The Quick Fix

  1. Go to the AWS IAM consoleRoles.
  2. Find your Lambda's execution role. Name looks like your-function-name-role-xxxxx.
  3. Click Add permissionsAttach policies.
  4. Search for the service you need. For S3 read access, pick AmazonS3ReadOnlyAccess. For DynamoDB, pick AmazonDynamoDBReadOnlyAccess or a custom one.
  5. Attach it. Done.

That's it. Your Lambda now has permission. But attaching a full managed policy is lazy. Better to create a custom policy with only the actions and resources you need. Here's an example for S3:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ]
    }
  ]
}

Attach that custom policy instead of the wide AmazonS3ReadOnlyAccess. You control exactly what your function can touch.

Why This Works

What's actually happening here is that every AWS Lambda has an execution role — an IAM role that the Lambda assumes when it runs. This role has a trust policy that says "AWS Lambda can assume me." Then it has permission policies that say what the Lambda can do.

When your Lambda calls s3.getObject(), it makes an API request to S3. S3 checks: "Does this caller have permission?" It looks at the AWS credentials passed in the request headers. Those credentials belong to the execution role. If the role's policies don't include s3:GetObject on that bucket, S3 returns AccessDenied.

Adding the policy to the role is the only fix. No other way around it. You can't add permissions directly to the Lambda function — that's not how AWS works. The role is the single point of truth.

The reason step 3 works is that IAM evaluates all policies attached to the role. As soon as you attach a policy that allows the action, the next invocation of your Lambda will have permission. No restart needed. IAM updates propagate within a few seconds.

Less Common Variations

Sometimes the obvious fix doesn't work. Here are three edge cases:

1. Trust Policy Missing

The execution role must have a trust policy that allows Lambda to assume it. Without it, the Lambda can't even get credentials. Symptoms: you see AccessDeniedException on every API call, even S3 or DynamoDB calls that you've allowed. Check the role's Trust Relationships tab. It should look like:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

If it's missing or has the wrong service (like ec2.amazonaws.com), fix it. Replace the principal with lambda.amazonaws.com.

2. Resource-Based Policies

Some services, like S3 and SNS, also check resource-based policies in addition to the caller's IAM role. For example, an S3 bucket can have a bucket policy that denies access to all principals except a specific role. Even if your Lambda's execution role has s3:GetObject, the bucket policy can still block it. Check the bucket's Permissions tab for a Deny statement that affects your Lambda.

Fix: either modify the bucket policy to allow the Lambda's role, or remove the deny statement. You can identify the role's ARN from the IAM console — it looks like arn:aws:iam::123456789012:role/your-role-name.

3. VPC and Internet Access

If your Lambda runs inside a VPC (you attached it to private subnets), it can't reach public AWS service endpoints over the internet. The calls to S3 or DynamoDB go through the public internet, not through the VPC. You'll see timeouts or AccessDeniedException because the request never reaches the service.

Fix: add a VPC endpoint for the service you need (like com.amazonaws.region.s3 for S3). Or put a NAT gateway in the VPC so the Lambda can route traffic out. Without one of these, the Lambda can't talk to any AWS service that's not inside the VPC.

Test this by removing the VPC configuration from your Lambda temporarily. If it works without VPC, you know the issue is network, not permissions.

Prevention Tips

Don't learn this the hard way. Here's what to do for new Lambdas:

  • Create a custom policy per function. Start with the least privilege — only the actions and resources you know the function needs. You can always expand later.
  • Test locally using AWS SAM or LocalStack. These simulate IAM and catch permission errors before you deploy. But be careful — LocalStack doesn't enforce IAM exactly like real AWS.
  • Use AWS CloudTrail to see the exact API call that failed. In the CloudTrail console, filter by errorCode = AccessDenied. This shows you what action and resource your Lambda tried to use. Then create the policy for exactly that action.
  • Write a policy validation in your CI/CD pipeline. Tools like policy_sentry or cfn-lint can check your IAM policies for common mistakes — like missing actions or wrong resource ARNs.

One more thing: never attach the AdministratorAccess policy to a Lambda role. You'll fix the permission denied error, but you'll also give the function full access to your AWS account. That's a security nightmare if your Lambda code gets compromised.

Stick to least privilege. Your future self (and your security team) will thank you.

Was this solution helpful?