Using lambda authorizer in serverless function exposed through API gateway - aws-lambda

I have written a lambda authorizer which returns response in this format -
{
principalId: 123345,
policyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/verifyToken-stage/GET/"
}
]
}
}
Note- My authorizer's name is verifyToken
In my different microservice(i.e Activity Logs) serverless.yml file
I am calling it like this -
service: activity-logs
frameworkVersion: '2'
resources:
Resources:
GatewayResponseUnauthorized:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Methods: "'*'"
gatewayresponse.header.Access-Control-Allow-Credentials: "'true'"
ResponseType: UNAUTHORIZED
RestApiId:
Ref: 'ApiGatewayRestApi'
provider:
name: aws
runtime: nodejs12.x
lambdaHashingVersion: 20201221
region: us-east-1
stage: ${opt:stage}
functions:
getActivityLogs:
handler: handler.getActivityLogs
environment:
NODE_ENV: ${opt:env}
timeout: 800
events:
- http:
path: /{user_id}
method: get
authorizer: arn:aws:lambda:us-east-1:123456789012:function:auth-${opt:stage}-verifyToken
cors: true
vpc:
securityGroupIds:
- sg-xxxxxxxx
- sg-xxxxxxxx
- sg-xxxxxxxx
- sg-xxxxxxxx
- sg-xxxxxxxx
subnetIds:
- subnet-xxxxxxxxxxxxxxxxx
- subnet-xxxxxxxxxxxxxxxxx
I have checked my authorizer separately, it is working and returning 200 with the above response. Similarly, my microservice is working without authorizer. But when authorizer is enabled in getActivityLogs, it is not letting my activity-logs execute a single line

I was using callback for returning response in async function and as per this doc, https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html
we can simply return our response if a function is async.

Related

attach authorizer to api gateway V2 route in aws cloudformation

How to attach authorizer to api gateway V2 route in aws cloudformation?
I am using Api Gateway v2 and cloudformation.
In AWS console it is just one click of one button "Attach Authorization" in "Routes" section
I am using simple authorizer:
My cloudformation looks like this:
Authorizer:
Type: 'AWS::ApiGatewayV2::Authorizer'
Properties:
ApiId: !Ref ApiGateway
AuthorizerPayloadFormatVersion: 2.0
AuthorizerResultTtlInSeconds: 5
AuthorizerType: REQUEST
AuthorizerUri: !Join
- ''
- - 'arn:'
- !Ref 'AWS::Partition'
- ':apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- 'arn:aws:lambda:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- :function:${stageVariables.AuthorizerFunctionName}
- /invocations
EnableSimpleResponses: true
IdentitySource:
- '$request.header.Authorization'
Name: !Sub ${ProjectName}-gateway-authorizer
MyRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref ApiGateway
AuthorizationType: CUSTOM
AuthorizerId: !Ref Authorizer
RouteKey: 'POST /posts/all'
Target: !Join
- /
- - integrations
- !Ref PostsLambdaIntegrationGet
Authorizer lambda body:
import json
# import jwt
def lambda_handler(event, context):
print('*********** The event is: ***************')
print(event)
print('headers is:')
print(event['headers'])
print('headers Authorization is:')
# !!!!! DONWCASE by postam or api !!!!! "A" -> "a"
print(event['headers']['authorization'])
if event['headers']['authorization'] == 'abc123':
response = {
"isAuthorized": True,
"context": {
"anyotherparam": "values"
}
}
else:
response = {
"isAuthorized": False,
"context": {
"anyotherparam": "values"
}
}
print('response is:')
print(response)
return response
BTW I do not see this option in cli apigatewayv2 cli documentation too:

How to pass the Arn of a Step function to another function in a CloudFormation stack?

I created a step function in a CloudFormation stack, and I need to call that function from another function in the stack once it's deployed. I have no idea how to do that, and I'm getting circular dependencies.
Basically I'm trying to pass an environment variable to the function that's the ARN of the step function.
Here's the CloudFormation code :
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
reports stack
Globals:
Function:
Timeout: 120
MemorySize: 2048
Runtime: python3.9
Environment:
Variables:
STEP_FUNCTION: !GetAtt Research.Arn ### =====> how do i do that ?
Parameters:
ProjectName:
Description: Name of the Project
Type: String
Default: Project
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
TracingEnabled: true
Cors:
AllowMethods: "'DELETE,GET,HEAD,OPTIONS,POST,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
List:
DependsOn: VideoResearch
Type: AWS::Serverless::Function
Properties:
CodeUri: functions/
Handler: get.get
Events:
List:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /reports
Method: GET
Policies:
(...)
################ STEP FUNCTION ################
Research:
Type: AWS::Serverless::Function
Properties:
CodeUri: functions/
Handler: runResearch.research
Policies:
(...)
StepFunctionToHandleResearchRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- !Sub states.${AWS::Region}.amazonaws.com
Action: "sts:AssumeRole"
Path: "/"
Policies:
(...)
#
StepFunctionToHandleVideosResearch:
Type: "AWS::StepFunctions::StateMachine"
Properties:
DefinitionString: !Sub |
{
"StartAt": "Research",
"States": {
"Research": {
"Type": "Task",
"Resource": "${VideoResearch.Arn}",
"End": true
}
}
}
RoleArn: !GetAtt [ StepFunctionToHandleResearchRole, Arn ]
Outputs:
(...)
# I also tried to export the arn of the function
In my function's code I have :
stepFunctionARN = os.environ['STEP_FUNCTION']
#Anthony B. posted the answer in the comments : the problem was related to the fact I added the dependency as global variables, it created a circular dependency (API=> local function <= API looking for function's ARN => doesn't work).
If you remove the global variable and add a local variable, with a "DependsOn: Research" than everything works.
API=>created => creates first function, get Arn => than creates other functions and provide ARN

Serverless Framework - Lambda#Edge Deployment for a predefined Cloud Front Distribution

I have been trying for a day to configure automating a lambda#Edge to be associated with a Distribution through the serverless framework but things aren't working well.
Here is the documentation and they said we can use a predefined cloud front distribution from resources but not shown how?
Here is my Resources.yml that include the S3 bucket and associated two distribution's origins to it:
Resources:
ResourcesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.resourcesBucketName}
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: ['PUT']
AllowedOrigins: ['*']
ResourcesBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ResourcesBucket
PolicyDocument:
Statement:
# Read permission for CloudFront
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment:
Fn::Join:
- ""
-
- "Identity for accessing CloudFront from S3 within stack "
-
Ref: "AWS::StackName"
- ""
# I can use this instead of Fn::Join !Sub 'Identity for accessing CloudFront from S3 within stack #{AWS::StackName}' Getting benefit of
# serverless-pseudo-parameters plugin
# Cloudfront distro backed by ResourcesBucket
ResourcesCdnDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
# S3 origin for private resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPrivate
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
# S3 origin for public resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPublic
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
Enabled: true
Comment: CDN for public and provate static content.
DefaultRootObject: index.html
HttpVersion: http2
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
TargetOriginId: S3OriginPublic
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
-
PathPattern: 'private/*'
TargetOriginId: S3OriginPrivate
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
-
PathPattern: 'public/*'
TargetOriginId: S3OriginPublic
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_200
Now I have all set regarding the CloudFront and I just want to add a lambda at the edge to authenticate my private content (The Origin with Id: S3OriginPrivate). So here is my serverless.yml file:
service: mda-app-uploads
plugins:
- serverless-offline
- serverless-pseudo-parameters
- serverless-iam-roles-per-function
custom:
stage: ${opt:stage, self:provider.stage}
resourcesBucketName: ${self:custom.stage}-mda-resources-bucket
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: us-east-1
versionFunctions: true
resources:
- ${file(resources/s3-cloudfront.yml)}
# functions:
functions:
mdaAuthEdge:
handler: mda-edge-auth.handler
events:
- cloudFront:
eventType: viewer-request
origin:
Id: S3OriginPrivate
When deploying I am getting this issue:
TypeError: Cannot read property 'replace' of undefined
This telling that this id already exists and can't be replaced as I think. My main focus is to get the lambda at edge deployed and associated with the cloud front within the serverless framework, so I made another trial to add almost everything to the cloud formation resources and depend only on the serverless framework in deploying the function and here was my serverless.yml and the resources file:
service: mda-app-uploads
plugins:
- serverless-offline
- serverless-pseudo-parameters
- serverless-iam-roles-per-function
custom:
stage: ${opt:stage, self:provider.stage}
resourcesBucketName: ${self:custom.stage}-mda-resources-bucket
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: us-east-1
versionFunctions: true
resources:
# Buckets
- ${file(resources/s3-cloudfront.yml)}
# functions:
functions:
mdaAuthEdge:
handler: mda-edge-auth.handler
role: LambdaEdgeFunctionRole
The resources:
Resources:
LambdaEdgeFunctionRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Sid: "AllowLambdaServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
- "edgelambda.amazonaws.com"
LambdaEdgeFunctionPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: MainEdgePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
Effect: "Allow"
Action:
- "lambda:GetFunction"
- "lambda:GetFunctionConfiguration"
Resource: !Ref MdaAuthAtEdgeLambdaFunction.Version #!Join [':', [!GetAtt MdaAuthAtEdgeLambdaFunction.Arn, '2']]
Roles:
- !Ref LambdaEdgeFunctionRole
ResourcesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.resourcesBucketName}
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: ['PUT']
AllowedOrigins: ['*']
ResourcesBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ResourcesBucket
PolicyDocument:
Statement:
# Read permission for CloudFront
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment:
Fn::Join:
- ""
-
- "Identity for accessing CloudFront from S3 within stack "
-
Ref: "AWS::StackName"
- ""
# I can use this instead of Fn::Join !Sub 'Identity for accessing CloudFront from S3 within stack #{AWS::StackName}' Getting benefit of
# serverless-pseudo-parameters plugin
# Cloudfront distro backed by ResourcesBucket
ResourcesCdnDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
# S3 origin for private resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPrivate
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
# S3 origin for public resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3-${self:provider.region}.amazonaws.com'
Id: S3OriginPublic
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
Enabled: true
Comment: CDN for public and provate static content.
DefaultRootObject: index.html
HttpVersion: http2
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
TargetOriginId: S3OriginPublic
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
-
PathPattern: 'private/*'
TargetOriginId: S3OriginPrivate
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
LambdaFunctionAssociations:
-
EventType: origin-request
LambdaFunctionARN: !Ref MdaAuthEdgeLambdaFunction.Version
#!Join [':', [!GetAtt MdaAuthAtEdgeLambdaFunction.Arn, '2']]
# arn:aws:lambda:eu-west-1:219511374676:function:mda-aws-functions-dev-authLambdaAtEdge:1
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
-
PathPattern: 'public/*'
TargetOriginId: S3OriginPublic
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_200
But I've faced many errors related to defining the version and so on. I searched, debugged, and investigated that for many hours but seems hard configuration. Any help on how to get lambda edge works with predefined cloud front through the serverless framework?
It's a bit tricky to do so using a serverless framework but I solved it by combining cloud formation with the serverless framework. I have the answer here to another question which contains a full description of how to do so:
How to access AWS CloudFront that connected with S3 Bucket via Bearer token of a specific user (JWT Custom Auth)
I don't want to repeat everything again here and also I found the question so important and facing many people without a concrete solution so pleas let me know in case you are facing any issue.
The approach is to just create the function inside the serverless.yml then inside the cloud formation you can do all the magic of creating the versions, roles and another function that will help you publish your arn and use it dynamically.
Here is my Serverless.yml:
service: mda-app-uploads
plugins:
- serverless-offline
- serverless-pseudo-parameters
- serverless-iam-roles-per-function
- serverless-bundle
custom:
stage: ${opt:stage, self:provider.stage}
resourcesBucketName: ${self:custom.stage}-mda-resources-bucket
resourcesStages:
prod: prod
dev: dev
resourcesStage: ${self:custom.resourcesStages.${self:custom.stage}, self:custom.resourcesStages.dev}
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: us-east-1
versionFunctions: true
functions:
oauthEdge:
handler: src/mda-edge-auth.handler
role: LambdaEdgeFunctionRole
memorySize: 128
timeout: 5
resources:
- ${file(resources/s3-cloudfront.yml)}
Here is my resources/s3-cloudfront.yml:
Resources:
AuthEdgeLambdaVersion:
Type: Custom::LatestLambdaVersion
Properties:
ServiceToken: !GetAtt PublishLambdaVersion.Arn
FunctionName: !Ref OauthEdgeLambdaFunction
Nonce: "Test"
PublishLambdaVersion:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Runtime: nodejs12.x
Role: !GetAtt PublishLambdaVersionRole.Arn
Code:
ZipFile: |
const {Lambda} = require('aws-sdk')
const {send, SUCCESS, FAILED} = require('cfn-response')
const lambda = new Lambda()
exports.handler = (event, context) => {
const {RequestType, ResourceProperties: {FunctionName}} = event
if (RequestType == 'Delete') return send(event, context, SUCCESS)
lambda.publishVersion({FunctionName}, (err, {FunctionArn}) => {
err
? send(event, context, FAILED, err)
: send(event, context, SUCCESS, {FunctionArn})
})
}
PublishLambdaVersionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: PublishVersion
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: lambda:PublishVersion
Resource: '*'
LambdaEdgeFunctionRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Sid: "AllowLambdaServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "lambda.amazonaws.com"
- "edgelambda.amazonaws.com"
LambdaEdgeFunctionPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: MainEdgePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
Effect: "Allow"
Action:
- "lambda:GetFunction"
- "lambda:GetFunctionConfiguration"
Resource: !GetAtt AuthEdgeLambdaVersion.FunctionArn
Roles:
- !Ref LambdaEdgeFunctionRole
ResourcesBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.resourcesBucketName}
AccessControl: Private
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: ['PUT']
AllowedOrigins: ['*']
ResourcesBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: ResourcesBucket
PolicyDocument:
Statement:
# Read permission for CloudFront
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
- Action: s3:PutObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
AWS: !GetAtt LambdaEdgeFunctionRole.Arn
- Action: s3:GetObject
Effect: "Allow"
Resource:
Fn::Join:
- ""
-
- "arn:aws:s3:::"
-
Ref: "ResourcesBucket"
- "/*"
Principal:
AWS: !GetAtt LambdaEdgeFunctionRole.Arn
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment:
Fn::Join:
- ""
-
- "Identity for accessing CloudFront from S3 within stack "
-
Ref: "AWS::StackName"
- ""
# Cloudfront distro backed by ResourcesBucket
ResourcesCdnDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
# S3 origin for private resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3.amazonaws.com'
Id: S3OriginPrivate
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
# S3 origin for public resources
- DomainName: !Sub '${self:custom.resourcesBucketName}.s3.amazonaws.com'
Id: S3OriginPublic
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/#{CloudFrontOriginAccessIdentity}'
Enabled: true
Comment: CDN for public and provate static content.
DefaultRootObject: index.html
HttpVersion: http2
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
TargetOriginId: S3OriginPublic
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
-
PathPattern: 'private/*'
TargetOriginId: S3OriginPrivate
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
LambdaFunctionAssociations:
-
EventType: viewer-request
LambdaFunctionARN: !GetAtt AuthEdgeLambdaVersion.FunctionArn
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
-
PathPattern: 'public/*'
TargetOriginId: S3OriginPublic
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
ForwardedValues:
QueryString: false
Headers:
- Origin
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_200
But you will find the full description in my other question's answer.

AWS SAM cloudformation: API Gateway can't invoke lambda (AWS::Serverless::Function )

I created a template.yaml file to declare a simple lambda function that is invoked by api gateway. When I try to invoke the function from the api gateway url the request fails with {"message": "Internal server error"} and in cloudwatch api gateway logs I see the error message Invalid permissions on Lambda function.
This is my template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
AliasName:
Type: String
Default: dev
Resources:
DynamoDBTenantTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: clientApiKey
AttributeType: S
KeySchema:
- AttributeName: clientApiKey
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
TableName: !Sub "authtable-${AliasName}"
AmeAuthenticatorLambda:
Type: AWS::Serverless::Function
Properties:
Handler: authenticator.handler
Policies: AmazonDynamoDBFullAccess
Runtime: nodejs8.10
CodeUri: src/
Environment:
Variables:
TABLE_NAME: !Sub "authtable-${AliasName}"
Events:
AuthenticatorEvent:
Type: Api
Properties:
Path: /authentication/
Method: POST
The SAM Documentation says that the syntax above is able to create the necessary permissions and API declaration implicitly.
I also followed an example from AWS website.
If I add to the template.yaml file a lambda:InvokeFunction permission then the invocation works, but by reading the documentation doing that should not be necessary.
What can be going wrong?

How do I cloudform an API gateway resource with a lambda proxy integration

I've been trying to work out how to express (in cloudformation) an API Gateway Resource that has a Lambda function integration type using the Lambda Proxy integration.
This is easy to do in the AWS console as there is a check box that you can select:
However there is no corresponding field in the AWS::ApiGateway::Method CloudFormation resource (it should be in the Integration property).
How can I configure this in cloudformation?
The Integration type should be set to AWS_PROXY. An example snippet of a method from a working YAML CloudFormation template is below.
ProxyResourceAny:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: ANY
ResourceId:
Ref: ProxyResource
RestApiId:
Ref: API
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub
- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Arn}/invocations
- Arn:
Fn::GetAtt:
- RestorerLambda
- Arn
It's worth saying how a I figured this out...
After scratching my head for a while I examined the output of the aws apigateway get-method CLI command for a method that was configured this way using the console. That gave me the following JSON and I realised that the checkbox might be encoded into the type. I tested my assumption and came up with the CloudFormation above.
{
"apiKeyRequired": false,
"httpMethod": "ANY",
"methodIntegration": {
"integrationResponses": {
"200": {
"responseTemplates": {
"application/json": null
},
"statusCode": "200"
}
},
"passthroughBehavior": "WHEN_NO_MATCH",
"cacheKeyParameters": [],
"uri": "arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:XXXXXXXXX:function:Shildrew-Restorer-Play-Lambda/invocations",
"httpMethod": "POST",
"cacheNamespace": "64bl3tgw4g",
"type": "AWS_PROXY"
},
"requestParameters": {},
"authorizationType": "NONE"
}
I have solved this same issue by simple changing the
Integration:
Type: AWS_PROXY
To
Integration:
Type: AWS
The cloud formation documentation currently is scarce and the API gateway cloudformation documentation doesn't match up to what can be seen on the console which hinders anyone who is trying to resolve an issue.
Hope this helps!
We faced this exact issue. We are using Ansible for our Infrastructure. Could apply to CLI or Cloudformation or even the SDK
The solution to our problem was to make sure that the Lambda policy was defined in a granular manner for the endpoint verbs in API Gateway for the lambda you are attempting to use.
For instance, We had multiple routes. Each route(or sets of routes) needs its own lambda policy defined that allows lambda:InvokeFunction. This is defined in the Lambda Policy module for Ansible. With this, the lambda trigger was enabled automatically.
There are two ways to achieve this:
Method 1: By defining the "Lambda", "API Gateway", "API Resource" and "API Methods". Linking the Lambda using the URI statement under "API Method".
MyLambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: "Node.js Express REST API"
FunctionName: "get_list_function" (The name in AWS console)
Handler: lambda.handler
Runtime: nodejs12
MemorySize: 128
Role: <ROLE ARN>
Timeout: 60
apiGateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "example-api-gw"
Description: "Example API"
ProxyResource:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
PathPart: '{proxy+}' OR "a simple string like "PetStore"
apiGatewayRootMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: NONE
HttpMethod: ANY
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
IntegrationResponses:
- StatusCode: 200
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}/invocations
ResourceId: !Ref ProxyResource
RestApiId: !Ref "apiGateway"
METHOD 2: Define "API Gateway" and "Lambda". In Lambda definitions, call Events of type API.
CoreApi:
Type: 'AWS::Serverless::Api'
Properties:
StageName: dev
Name: SaaSAPI
EndpointConfiguration: Regional
Cors:
AllowMethods: '''POST, GET, OPTIONS, PATCH, DELETE, PUT'''
AllowHeaders: '''Content-Type, X-Amz-Date, Authorization, X-Api-Key, x-requested-with'''
AllowOrigin: '''*'''
MaxAge: '''600'''
MyLambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: "Node.js Express REST API"
FunctionName: "get_list_function"
Handler: lambda.handler
Runtime: nodejs12
MemorySize: 128
Role: <ROLE ARN>
Timeout: 60
Events:
ProxyResourceA:
Type: Api
Properties:
Path: /Departments
Method: GET
RestApiId: !Ref CoreApi
ProxyResourceB:
Type: Api
Properties:
Path: /Departments (This becomes the resource in API Gateway)
Method: POST
RestApiId: !Ref CoreApi
(You can link multiple methods with the same lambda, provide a unique name to each event, like ProxyResoruceA & ProxyResourceB)

Resources