How to Fix API Gateway Lambda Execution Role Missing Permissions (lambda:InvokeFunction Error)
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins
TL;DR
- What broke: API Gateway's execution role (or the Lambda resource-based policy) is missing
lambda:InvokeFunction, causing every API call to return500 Internal Server ErrorwithAccessDeniedExceptionin CloudWatch. - How to fix it: Either add
lambda:InvokeFunctionto the API Gateway IAM execution role scoped to the target Lambda ARN, or add a Lambda resource-based policy grantingapigateway.amazonaws.cominvoke rights. - Fast path: Use our Client-Side Sandbox below to auto-refactor this — paste your IAM policy or Terraform
aws_iam_policy_documentblock and get the corrected output instantly.
The Incident (What Does the Error Mean?)
Raw error surfaced in CloudWatch Logs for the API Gateway stage:
{
"message": "Wed, 05 Jun 2024 14:22:11 GMT : Execution failed due to configuration error:
Invalid permissions on Lambda function",
"status": 500
}
And in the Lambda invocation log or X-Ray trace:
User: arn:aws:sts::123456789012:assumed-role/APIGatewayExecutionRole/AmazonAPIGateway-my-api
is not authorized to perform: lambda:InvokeFunction
on resource: arn:aws:lambda:us-east-1:123456789012:function:my-function
because no identity-based policy allows the lambda:InvokeFunction action
Immediate consequence: 100% of requests to this API stage fail. No traffic reaches Lambda. The API is fully down.
The Attack Vector / Blast Radius
This is a misconfiguration causing a full service outage, not a security vulnerability in the traditional sense — but the blast radius is total for the affected API stage.
Cascading failure chain:
- API Gateway attempts to assume its execution role and call
lambda:InvokeFunction. - IAM denies the call — implicit deny wins over any resource-based policy that may exist.
- API Gateway returns
500to the client. No retry logic at the gateway layer rescues this. - If this API backs a downstream service (e.g., a mobile app, a webhook receiver, a B2B integration), all dependent systems fail simultaneously.
Why this happens in production unexpectedly:
- Terraform
aws_lambda_permissionresource was removed or never applied. - The Lambda function was redeployed with a new ARN (e.g., alias or version change) and the permission was not updated.
- IAM role was rotated or recreated during a security remediation and the
lambda:InvokeFunctionstatement was dropped. - Cross-account Lambda invocation where the resource-based policy principal is wrong.
How to Fix It (The Solution)
There are two valid trust models for API Gateway invoking Lambda. Use one, not both inconsistently.
Option A — Lambda Resource-Based Policy (Recommended, Least Privilege)
This grants API Gateway permission at the Lambda resource level. No changes to the execution role needed.
AWS CLI:
- # No resource-based policy exists — Lambda rejects the invocation
+ aws lambda add-permission \
+ --function-name my-function \
+ --statement-id apigateway-invoke-permission \
+ --action lambda:InvokeFunction \
+ --principal apigateway.amazonaws.com \
+ --source-arn "arn:aws:execute-api:us-east-1:123456789012:abc123def4/*/POST/my-resource"
Terraform (Enterprise Best Practice — scope to exact method/path ARN):
- resource "aws_lambda_permission" "apigw" {
- statement_id = "AllowAPIGatewayInvoke"
- action = "lambda:InvokeFunction"
- function_name = aws_lambda_function.my_function.function_name
- principal = "apigateway.amazonaws.com"
- # MISSING: source_arn — allows ANY API Gateway in the account to invoke
- }
+ resource "aws_lambda_permission" "apigw" {
+ statement_id = "AllowAPIGatewayInvoke"
+ action = "lambda:InvokeFunction"
+ function_name = aws_lambda_function.my_function.function_name
+ principal = "apigateway.amazonaws.com"
+ source_arn = "${aws_api_gateway_rest_api.my_api.execution_arn}/*/*"
+ }
Option B — IAM Execution Role Policy (When Using IAM Auth on the Integration)
If your API Gateway integration type is AWS with credentials pointing to an IAM role:
{
"Version": "2012-10-17",
"Statement": [
- {
- "Effect": "Allow",
- "Action": "lambda:*",
- "Resource": "*"
- }
+ {
+ "Effect": "Allow",
+ "Action": "lambda:InvokeFunction",
+ "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
+ }
]
}
The
-example above also shows a common over-permissioning anti-pattern (lambda:*on*) that passes IAM Access Analyzer but violates least privilege. Lock it to the exact function ARN andlambda:InvokeFunctiononly.
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov — catch missing source_arn on Lambda permissions:
# .checkov.yml
checks:
- CKV_AWS_45 # Ensure Lambda function has a resource-based policy
- CKV_AWS_116 # Ensure Lambda permission has source_arn condition
2. OPA/Conftest policy — enforce source_arn is always set:
package terraform.aws.lambda_permission
deny[msg] {
r := input.resource.aws_lambda_permission[name]
not r.source_arn
msg := sprintf("aws_lambda_permission.%v must define source_arn to prevent confused deputy attacks", [name])
}
3. Terraform plan validation in CI:
# In your GitHub Actions / GitLab CI pipeline
terraform plan -out=tfplan
checkov -f tfplan --framework terraform_plan --check CKV_AWS_116
4. AWS Config Rule — continuous compliance:
Enable lambda-function-public-access-prohibited and write a custom Config rule that alerts when any Lambda function has Principal: "*" without a Condition block scoping the aws:SourceArn.
5. Integration test in pipeline: After terraform apply in staging, run a smoke test that actually invokes the API endpoint and asserts HTTP 200. A missing permission is caught in staging, not prod.