I have an AWS CloudFormation template written in yaml that successfully creates all the resources I need and sets up the API Gateway to call the lambda file successfully except that the API Gateway doesn't have permission to call the lambda function automatically.
Currently, I have to go into the API Gateway and give the permission manually. Before I do that, I get an error with code 500, and afterwards I get a code of 200 and the result I want.
Add Permission to Lambda Function
At the bottom of my template I tried making a permission based on a few other Stack Overflow posts I saw on this topic, but I couldn't get them to work after hours of trying different answers and formats. I put the full template below.
AWSTemplateFormatVersion: 2010-09-09
Resources:
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service:
- 'lambda.amazonaws.com'
- 'comprehend.amazonaws.com'
- 'apigateway.amazonaws.com'
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/ComprehendFullAccess'
- 'arn:aws:iam::aws:policy/service-role/ComprehendDataAccessRolePolicy'
- 'arn:aws:iam::aws:policy/AmazonS3FullAccess'
RoleName: LambdaRole
SentimentFunction:
Type: AWS::Lambda::Function
DependsOn:
- LambdaRole
Properties:
Code:
ZipFile: |
import boto3
client = boto3.client('comprehend')
def lambda_handler(event, context):
result = {
'Sentiment': client.detect_sentiment(Text = event['inputTranscript'], LanguageCode='en')['Sentiment'],
'SentimentScore': client.detect_sentiment(Text = event['inputTranscript'], LanguageCode='en')['SentimentScore']
}
return result
FunctionName: 'SentimentFunction'
Handler: index.lambda_handler
Role: !GetAtt LambdaRole.Arn
Runtime: 'python3.9'
SentimentSchedule:
Type: AWS::Events::Rule
Properties:
Description: "Schedule to trigger SentimentFunction every minute"
ScheduleExpression: "cron(* * * * ? *)"
Targets:
- Arn: !GetAtt SentimentFunction.Arn
Id: "SentimentFunction"
SentimentAPI:
Type: AWS::ApiGateway::RestApi
Properties:
EndpointConfiguration:
Types:
- REGIONAL
Name: SentimentAPI
RestApiDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- SentimentAPI
- SentimentAnalysis
Properties:
RestApiId: !Ref SentimentAPI
StageName: dev
SentimentGatewayResource:
Type: AWS::ApiGateway::Resource
DependsOn:
- SentimentAPI
Properties:
ParentId: !GetAtt SentimentAPI.RootResourceId
PathPart: analysis
RestApiId: !Ref SentimentAPI
SentimentAnalysis:
Type: AWS::ApiGateway::Method
DependsOn:
- SentimentGatewayResource
- SentimentAPI
- SentimentFunction
Properties:
ApiKeyRequired: false
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: GET
IntegrationResponses:
- StatusCode: '200'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
application/json : '{"inputTranscript": "I want something"}'
Type: AWS
Uri:
!Join
- ''
- - 'arn:'
- !Ref AWS::Partition
- ':apigateway:'
- !Ref AWS::Region
- :lambda:path/2015-03-31/functions/
- !GetAtt SentimentFunction.Arn
- /invocations
MethodResponses:
- ResponseModels:
application/json : Empty
StatusCode: '200'
ResourceId: !Ref SentimentGatewayResource
RestApiId: !Ref SentimentAPI
# vvvvvvvv This doesn't work vvvvvvvv
GWAuth:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerUri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SentimentFunction.Arn}/invocations"
RestApiId: !Ref SentimentAPI
Type: "REQUEST"
IdentitySource: method.request.header.authorization
Name: custom_auth
GWAuthPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt SentimentFunction.Arn
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${SentimentAPI}/authorizers/${GWAuth}"
I'm very new to AWS, so I'm sure my template is badly formed to begin with. This error has been holding me back from making more progress. Any help is very much appreciated!
Lambda call failing before manual permissions granted
I have an API to lambda setup which uses events
property in the Lambda:
RetrieverLambda:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
STAGE:
Ref: Stage
ACCOUNT:
Ref: AWS::AccountId
REGION:
Ref: AWS::Region
CodeUri:
Bucket: {'Fn::If': ['UseBatsKey', 'BATS::SAM::CodeS3Bucket', {"Fn::ImportValue": {Ref: 'DeploymentBucketImportName'}}]}
Key: BATS::SAM::CodeS3Key
Events:
APIG:
Properties:
Method: ANY
Path: /
RestApiId: {Ref: LambdaAPIDefinition}
Type: Api
proxy:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY
RestApiId: {Ref: LambdaAPIDefinition}
Handler: ...
.....
And the Api Gateway is defined similar to how you have done:
LambdaAPIDefinition:
Type: 'AWS::Serverless::Api'
Properties:
StageName: {Ref: Stage}
EndpointConfiguration: REGIONAL
Auth:
DefaultAuthorizer: AWS_IAM
InvokeRole: NONE # removal of this will cause deployment failure with the error
#'Caller provided credentials not allowed when resource policy is set'
ResourcePolicy:
CustomStatements:
- Effect: 'Allow'
Action: 'execute-api:Invoke'
Resource: ['execute-api:/*/*/*']
Principal:
AWS:
- !Sub "arn:aws:iam::${AWS::AccountId}:user/ApiUser"
In the above, I am using an IAM
user instead of a role. But I dont think that matters here.
I would think, adding the event trigger to your Lambda construct should fix it.