I have an API Gateway resource manually built that looks like:
GET
/assets/{items} - (points to S3 bucket)
/{proxy+} - points to Lambda function
I would like to mimic this setup in a Cloudformation YAML template but unsure how to do so. Here is the current template I'm working with (partially reduced for brevity):
AWSTemplateFormatVersion: 2010-09-09
Parameters:
apiGatewayStageName:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
Default: call
lambdaFunctionName:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
Default: my-function
s3BucketName:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
Resources:
apiGateway:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: my-api
Description: My API
Metadata:
...
apiGatewayRootMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
HttpMethod: POST
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
ResourceId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
Metadata:
...
apiGatewayDeployment:
Type: 'AWS::ApiGateway::Deployment'
DependsOn:
- apiGatewayRootMethod
- apiGatewayGETMethod
Properties:
RestApiId: !Ref apiGateway
StageName: !Ref apiGatewayStageName
Metadata:
...
lambdaFunction:
Type: 'AWS::Lambda::Function'
Properties:
...
lambdaApiGatewayInvoke:
...
lambdaIAMRole:
...
lambdaLogGroup:
...
apiGatewayGETMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
ResourceId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
Metadata:
'AWS::CloudFormation::Designer':
id: 1a329c4d-9d18-499e-b852-0e361af324f4
s3Bucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Ref s3BucketName
Metadata:
...
Outputs:
apiGatewayInvokeURL:
Value: !Sub >-
https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}
lambdaArn:
Value: !GetAtt lambdaFunction.Arn
That is the result of lots of tweaking and me not having any prior CloudFormation knowledge besides going over the official docs. When the stack behind that template gets created, its API Gateway resource looks like:
The POST action is unnecessary, and only there from trial and error. The GET resource is the only important one as application returned by the Lambda function isn't doing any post requests yet.
The GET one must be created from this portion of the Stack:
apiGatewayGETMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
ResourceId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
What must be done to make it so that GET resource has the nested /assets/{items} path pointing to an S3 bucket and the {proxy+} path pointing to a Lambda? Do I need to specify separate sibling resources for those paths like apiGatewayAssets and apiGatewayLambdaProxy then connect them to apiGatewayGETMethod somehow?
2020-05-17 Update
The current part tripping me up is this resource:
apiGatewayAssetsItemsResourceMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !Ref apiGatewayAssetsItemsResource
RestApiId: !Ref apiGateway
AuthorizationType: NONE
HttpMethod: GET
Integration:
Type: AWS
Credentials: arn:aws:iam::XXXXXX:role/AnExistingRole
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
RequestParameters:
integration.request.path.item: 'method.request.path.item'
method.request.path.item: true
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:s3:path/${s3BucketName}/{item}
That leads to a CloudFormation stack creation error with the status reason being Invalid mapping expression specified: Validation Result: warnings : [], errors : [Invalid mapping expression parameter specified: method.request.path.item] (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: XXXXXX)
However, if I attempt to create it with the exact same resource minus the RequestParameters entry, it gets created successfully. Although when viewing that API Gateway GET method in the console, it's missing the Paths: item line inside the Integration Request box. Full template I'm currently using:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
apiGatewayStageName:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
Default: call
lambdaFunctionName:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
Default: my-function
s3BucketName:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
Resources:
apiGateway:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: my-api
Description: My API
apiGatewayDeployment:
Type: 'AWS::ApiGateway::Deployment'
DependsOn:
- apiGatewayGETMethod
Properties:
RestApiId: !Ref apiGateway
StageName: !Ref apiGatewayStageName
lambdaFunction:
...
lambdaApiGatewayInvoke:
...
lambdaIAMRole:
...
lambdaLogGroup:
...
apiGatewayGETMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
ResourceId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
s3Bucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Ref s3BucketName
BucketPolicy:
...
apiGatewayAssetsResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref apiGateway
ParentId: !GetAtt
- apiGateway
- RootResourceId
PathPart: assets
apiGatewayAssetsItemsResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref apiGateway
PathPart: '{item}'
ParentId: !Ref apiGatewayAssetsResource
apiGatewayAssetsItemsResourceMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !Ref apiGatewayAssetsItemsResource
RestApiId: !Ref apiGateway
AuthorizationType: NONE
HttpMethod: GET
Integration:
Type: AWS
Credentials: arn:aws:iam::XXXXXX:role/AnExistingRole
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:s3:path/${s3BucketName}/{item}
apiGatewayLambdaResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref apiGateway
PathPart: '{proxy+}'
ParentId: !GetAtt
- apiGateway
- RootResourceId
apiGatewayLambdaResourceMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
RestApiId: !Ref apiGateway
ResourceId: !Ref apiGatewayLambdaResource
HttpMethod: ANY
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: GET
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
Outputs:
apiGatewayInvokeURL:
Value: !Sub >-
https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}
lambdaArn:
Value: !GetAtt lambdaFunction.Arn
So what you need to do for this is the following:
Create a AWS::ApiGateway::Resource with a PathPart of assets, this will use a ParentId of the RootResourceId attr from your Rest API
Create a AWS::ApiGateway::Resource with a PathPart of {item}, this will use a ParentId of the assets Resource above.
Create a AWS::ApiGateway::Method for the ResourceId of the resource above. This will use the HTTP_PROXY and set the Uri to be the S3 bucket path, making sure to include the {{ item }} variable in the path.
Create a AWS::ApiGateway::Resource with a PathPart of {proxy+}, this will use a ParentId of the RootResourceId attr from your Rest API
Create a AWS::ApiGateway::Method for the ResourceId of the resource above. This will use the AWS_PROXY and set the uri to reference the Lambda function.
Hope this helps
Related
I am trying to create a simple infra using cloudformation. I have to create an Rest API Gateway and Lambda function. That function will be called using API Gateway.
Api Gateway > Lambda
The cloudformation code is below ( I am not showing the code related to role creation or managed policy).
medtestFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: ""
Environment:
Variables:
APIID: !Ref medTestRestapi
SLACK_VERIFICATION_TOKEN:
Ref: SlackVerificationToken
SLACK_INCOMING_WEBHOOK_URL:
Ref: SlackIncomingWebhookURL
FunctionName: "med-test2"
Handler: "index.handler"
Architectures:
- "x86_64"
Code:
S3Bucket:
Ref: S3CodeBucket
S3Key:
Ref: MedTestFunctionS3Key
MemorySize: 128
Role: !GetAtt medtestrole.Arn
Runtime: "nodejs14.x"
Timeout: 6
TracingConfig:
Mode: "PassThrough"
medTestRestapi:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "medtest2"
Description: "medtest2"
ApiKeySourceType: "HEADER"
EndpointConfiguration:
Types:
medTestApiStage:
Type: "AWS::ApiGateway::Stage"
Properties:
StageName: "a"
DeploymentId: !Ref medTestApiDeployment
RestApiId: !Ref medTestRestapi
Description: "a"
CacheClusterEnabled: false
TracingEnabled: false
medTestApiMethod:
DependsOn: medtestFunction
Type: "AWS::ApiGateway::Method"
Properties:
RestApiId: !Ref medTestRestapi
ResourceId: !GetAtt medTestRestapi.RootResourceId
HttpMethod: "POST"
AuthorizationType: "NONE"
ApiKeyRequired: false
RequestParameters: {}
MethodResponses:
-
ResponseModels:
"application/json": "Empty"
StatusCode: "200"
Integration:
ContentHandling: "CONVERT_TO_TEXT"
IntegrationHttpMethod: "POST"
IntegrationResponses:
-
ResponseTemplates: {}
StatusCode: "200"
PassthroughBehavior: "WHEN_NO_MATCH"
TimeoutInMillis: 29000
Type: "AWS_PROXY"
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:med-test2/invocations"
medTestApiDeployment:
DependsOn: medTestApiMethod
Type: "AWS::ApiGateway::Deployment"
Properties:
RestApiId: !Ref medTestRestapi
Description: "a"
medTestFunctionPermission:
DependsOn: [medTestApiDeployment, medTestApiMethod]
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt medtestFunction.Arn
Principal: "apigateway.amazonaws.com"
SourceArn: !Join [ ":", ["arn:aws:execute-api", !Ref AWS::Region, !Ref AWS::AccountId, !Ref medTestRestapi, "/*/POST/" ] ]
After stack creation when I am checking the function it says
The API with ID : could not be found.
But when I add the trigger manually using console on top of the created stack then it works. Any idea what I am doing wrong? Thanks
you've got one colon too many in SourceArn of medTestFunctionPermission
The one just after the API Gateway ID
You have:
arn:aws:execute-api:eu-west-1:<accountId>:<apiGWId>:/*/POST/
Should be:
arn:aws:execute-api:eu-west-1:<accountId>:<apiGWId>/*/POST/
You can use !Sub instead of !Join. It's easier to read:
SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${medTestRestapi}/*/POST/
I have a CloudFormation template which I'm trying to deploy. This template is very similar to another template I have which works without issue however when deploying this template it keeps failing stating:
APIGatewayDeployment5332c373d4 CREATE_FAILED The REST API doesn't contain any methods (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 02c0352f-b23b-44d5-a0af-2f3f3ecd7273)
I had this issue previously on my other template and added the DependsOn property on the deployment which fixed the issue however even with this property this template is failing.
See cut down version of my template below:
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Environment:
Variables:
FOO: bah
Api:
EndpointConfiguration: REGIONAL
Resources:
APIGateway:
Type: AWS::Serverless::Api
Properties:
Name: Some API
StageName: !Sub ${EnvironmentTagName}
ApiFooResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt APIGateway.RootResourceId
PathPart: foo
RestApiId: !Ref APIGateway
ApiFooNotificationResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !Ref ApiFooResource
PathPart: notification
RestApiId: !Ref APIGateway
ApiFooNotificationMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
AuthorizationType: NONE
ResourceId: !Ref ApiFooNotificationResource
RestApiId: !Ref APIGateway
MethodResponses:
- StatusCode: '200'
Integration:
Credentials: !GetAtt SQSRole.Arn
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: 200
Type: AWS
Uri: !Sub arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage
RequestParameters:
integration.request.querystring.QueueUrl: !Sub '''${FooNotificationQueue}'''
integration.request.querystring.MessageBody: method.request.body
ApiTestResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt APIGateway.RootResourceId
PathPart: test
RestApiId: !Ref APIGateway
ApiTestMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
AuthorizationType: NONE
ResourceId: !Ref ApiTestResource
RestApiId: !Ref APIGateway
MethodResponses:
- StatusCode: '200'
Integration:
Credentials: !GetAtt SQSRole.Arn
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: 200
Type: AWS
Uri: !Sub arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage
RequestParameters:
integration.request.querystring.QueueUrl: !Sub '''${TestQueue}'''
integration.request.querystring.MessageBody: method.request.body
ApiDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: ApiTestMethod
Properties:
RestApiId: !Ref APIGateway
Description: !Sub Api Deployment for ${EnvironmentTagName}
StageName: !Sub ${EnvironmentTagName}
...**Removed the rest for brevity**...
Parameters:
StackTagName:
Type: String
Description: Stack Name (injected by Stackery at deployment time)
EnvironmentTagName:
Type: String
Description: Environment Name (injected by Stackery at deployment time)
I have a serverless app with a http event. I need to create a child resource to the endpoint created by serverless.
How can I accomplish this with serverless?
The template looks like this so far:
provider:
name: aws
runtime: nodejs8.10
region: <REGION>
functions:
myFunc:
timeout: 30
handler: "index.handler"
name: "myFunc"
events:
- http:
path: /myFunc
method: post
cors: true
resources:
Resources:
saysResource:
Type: "AWS::ApiGateway::Resource"
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ParentId:
Ref: <NEED_/myFunc_RESOURCE_ID_HERE>
PathPart: "{says}"
GetMethod:
Type: "AWS::ApiGateway::Method"
Properties:
HttpMethod: GET
RequestParameters:
'method.request.path.says': true
RestApiId:
Ref: ApiGatewayRestApi
ResourceId:
Ref: saysResource
Integration:
Type: aws
Credentials:
Ref: S3ReadAccessRole
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
RequestParameters:
'integration.request.path.key': 'method.request.path.says'
Uri: 'arn:aws:s3:::my-bucket/users/{key}'
As an attempt to get it working, I re-declaration of the resource for /myFunc was performed(hoping that CFN would not create already created resources):
myFuncResource:
Type: "AWS::ApiGateway::Resource"
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: "/myFunc"
This failed with the error:
An error occurred: myFuncResource - Another resource with the same parent
already has this name: my-func (Service: AmazonApiGateway; Status Code:
409; Error Code: ConflictException; Request ID: <Request_Id>).
We have an API Gateway utilising a custom Token authoriser. We have 2 lambdas - Greetings and GenerateToken.
We only want the Greetings lambda to be behind a authoriser - requires to be called in the following way utilising SAM:
curl -X GET \
https://<apigatewayid>.execute-api.eu-west-1.amazonaws.com/Prod/generateToken \
-H 'X-API-KEY: allow'
How can we achieve that the GenerateToken path does not require a HTTP token for authenticating?
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: API Gateway with Lambda Token Authorizer
Resources:
GreetingsApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
swagger: 2.0
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
paths:
"/hello":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GreetingsLambda.Arn}/invocations
responses: {}
"/generateToken":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GenerateTokenLambda.Arn}/invocations
responses: {}
Auth:
DefaultAuthorizer: CustomAuthorizer
Authorizers:
MyAuthorizer:
FunctionArn: !GetAtt AuthLambda.Arn
Identity:
Header: X-API-KEY
GenerateTokenLambda:
Type: AWS::Serverless::Function
Properties:
Role: !GetAtt LambdaRole.Arn
CodeUri: "s3://<bucket-name>/code.zip"
Handler: src/generateToken.handler
Events:
GetRoot:
Type: Api
Properties:
RestApiId: !Ref GreetingsApiGateway
Path: /generateToken
Method: get
GreetingsLambda:
Type: AWS::Serverless::Function
Properties:
Role: !GetAtt LambdaRole.Arn
CodeUri: "s3://<bucket-name>/code.zip"
Handler: src/greetings.handler
Events:
GetRoot:
Type: Api
Properties:
RestApiId: !Ref GreetingsApiGateway
Path: /hello
Method: get
AuthLambda:
Type: AWS::Serverless::Function
Properties:
Role: !GetAtt LambdaRole.Arn
CodeUri: "s3://<bucket-name>/code.zip"
Handler: src/auth.handler
Globals:
Function:
Runtime: nodejs8.10
Outputs:
ApiURL:
Description: "OUR API URL"
Value: !Sub "https://${GreetingsApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
I am not very sure if I completely understood what you want, but here's a Cloudformation template to create api-gateway resources with & without authorization enabled. I am using Cognito User Pool authorization method but it can be just as easily be a custom authorizer.
RestAPI:
Type: AWS::ApiGateway::RestApi
DeletionPolicy: Delete
Properties:
Name: {"Ref": "AWS::StackName"}
ApiKeySourceType: HEADER
EndpointConfiguration:
Types:
- EDGE
ApiAuthorizer:
Type: AWS::ApiGateway::Authorizer
DeletionPolicy: Retain
DependsOn: UserPoolList
Properties:
Name: !Join ["-", [{"Ref": "AWS::StackName"}, "authorizer"]]
RestApiId: !Ref RestAPI
Type: COGNITO_USER_POOLS
AuthType: cognito_user_pools
IdentitySource: "method.request.header.Authorization"
ProviderARNs: <User Pool ARN>
ResourceSignin:
Type: AWS::ApiGateway::Resource
DeletionPolicy: Delete
Properties:
RestApiId: !Ref RestAPI
ParentId: !GetAtt RestAPI.RootResourceId
PathPart: "signin"
SigninPostMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestAPI
ResourceId: !Ref ResourceSignin
HttpMethod: POST
AuthorizationType: NONE
ApiKeyRequired: <true/false>
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserHandlerFunction.Arn}/invocations
Credentials: !GetAtt GenApiGatewayRole.Arn
ResourceUserCreate:
Type: AWS::ApiGateway::Resource
DeletionPolicy: Delete
Properties:
RestApiId: !Ref RestAPI
ParentId: !GetAtt RestAPI.RootResourceId
PathPart: "create"
CreatePostMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestAPI
ResourceId: !Ref ResourceUserCreate
HttpMethod: POST
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref ApiAuthorizer
ApiKeyRequired: <true/false>
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserHandlerFunction.Arn}/invocations
Credentials: !GetAtt UserApiGatewayRole.Arn
Here resource signin has a POST method with no authorization while create resource has a POST method with authorization enabled.
If you are planning to use API keys this might be the only way possible. I could not get API keys to work with SAM (I believe API keys with SAM is not yet supported - this was about a month back, but you can double check).
We can achieve this via swagger within API Gateway:
The World lambda is a public API and the Hello lambda resides behind the AuthLambda authorizer
OurApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
Authorizers:
MyAuthorizer:
FunctionPayloadType: REQUEST
FunctionArn: !GetAtt AuthLambda.Arn
DefinitionBody:
swagger: 2.0
basePath: /prod
info:
title: AwsSamExample
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
schemes:
- https
paths:
"/hello":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloLambda.Arn}/invocations
responses: {}
security:
- MyAuthorizer: []
"/world":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WorldLambda.Arn}/invocations
responses: {}
security: []
Use the Serverless framework and easily manage this with just an attribute.
https://www.npmjs.com/package/serverless
I have created a cloudformation template to create api gateway/resource/method/function on AWS platform, and also associated a lambda function to my api. Once I create the stack I get a url to hit my aws api. Whenever I hit this url, I get internal server error.
I am not sure what could be the reason, but if I toggle between 2 lambda functions from aws console for integration request and deploy, it starts working as expected.
I don't want to do it manually as deployment should be done at the time of stack creation.
Below is the template I used to create resources
Resources:
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub 'testing'
EndpointConfiguration:
Types:
- 'EDGE'
ApigwResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt RestApi.RootResourceId
# lambda function
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Role: 'lambda_role'
Handler: lambda_s3.lambda_handler
Code:
S3Bucket: { 'Fn::ImportValue': !Sub '${S3Bucket}-S3AppsBucketId' }
S3Key: 'lambda_source_code'
Runtime: python3.7
MemorySize: 128
Timeout: 60
FunctionName: !Sub 'lambda_function'
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: PUT
ResourceId: !Ref ApigwResource
RestApiId: !Ref RestApi
AuthorizationType: AWS_IAM
MethodResponses:
- ResponseModels: { 'application/json' : 'Empty' }
StatusCode: 200
Integration:
Type: AWS
IntegrationHttpMethod: PUT
IntegrationResponses:
- StatusCode: 200
Uri: !Sub
- 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations'
- lambdaArn: !GetAtt LambdaFunction.Arn
ApiGatewayDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- 'ApiGatewayMethod'
Properties:
RestApiId: !Ref RestApi
StageName: !Ref Environment
I found the problem, my api gateway didn't have permission to invoke lambda function. When I was toggling lambda functions manually, it used to provide necessary permissions to my api gateway thus this work around was working. I added below piece to my cloudformation template to make this work.
ConfigLambdaPermission:
Type: "AWS::Lambda::Permission"
DependsOn:
- RestApi
- LambdaFunction
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref LambdaFunction
Principal: apigateway.amazonaws.com