Swagger, API Gateway using AWS Cloudformation - yaml

I am deploying a API which is mapped to a loadbalancer. I could test the API successfully on the console but while using the invoke link in the stage, I am getting a 403. The ELB is a http end point and the invoke url is https which is normal I would say.
Also if I use the ELB DNS Name, I could get the desired result. Looks like requests are not going through API Gateway.
I am doing all of this using Cloudformation and swagger. Here is the relevant part
EmployeeApi:
Type: AWS::ApiGateway::RestApi
Properties:
BodyS3Location:
Bucket: !Ref S3Bucket
Key: "swagger.yaml"
EmployeeApiDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref EmployeeApi
EmployeeApiStage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref EmployeeApiDeployment
RestApiId: !Ref EmployeeApi
StageName: dev
Variables:
employeeELB:
Fn::ImportValue:
!Sub ${NetworkStackName}-ELB
EmployeeApiUsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
ApiStages:
- ApiId: !Ref EmployeeApi
Stage: !Ref EmployeeApiStage
UsagePlanName: Basic
EmployeeApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Name: employee-api-key
Enabled: true
StageKeys:
- RestApiId: !Ref EmployeeApi
StageName: !Ref EmployeeApiStage
Relevant part of my swagger file is
swagger: "2.0"
info:
version: 1.0.0
title: employee-service
description: Welcome to API documentation of Employee Service
schemes:
- https
securityDefinitions:
api_key:
type: apiKey
name: x-api-key
in: header
x-amazon-apigateway-request-validators:
RequestValidator:
validateRequestBody: true
validateRequestParameters: true
x-amazon-apigateway-request-validator: RequestValidator
paths:
/employees:
get:
security:
- api_key: []
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
uri: http://${stageVariables.employeeELB}/employees
passthroughBehavior: when_no_match
httpMethod: GET
contentHandling: "CONVERT_TO_TEXT"
type: http_proxy

Related

Debug CloudFormation validation issue

I am using a linter and my template appears valid, yet my deployment is failing with a "AWS::ElasticLoadBalancingV2::ListenerRule Validation exception". There does not appear to be any place to further drill down on this exception in the Cloud Formation console. How do I determine why my deployment is invalid?
Cloud Formation template
Parameters:
Env:
Type: String
Mappings:
EnvMap:
sandbox:
...
Resources:
HttpsListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Certificates:
- CertificateArn: !FindInMap [EnvMap, !Ref Env, CertificateArn]
DefaultActions:
- Type: forward
ForwardConfig:
# TODO: read all this stuff off HTTP listener
TargetGroupStickinessConfig:
Enabled: false
TargetGroups:
- TargetGroupArn:
!FindInMap [EnvMap, !Ref Env, LoadBalancerDefaultTargetArn]
Weight: 1
TargetGroupArn:
!FindInMap [EnvMap, !Ref Env, LoadBalancerDefaultTargetArn]
LoadBalancerArn: !FindInMap [EnvMap, !Ref Env, LoadBalancerArn]
Port: 443
Protocol: HTTPS
HttpsListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- Type: forward
ForwardConfig:
TargetGroupStickinessConfig:
Enabled: false
TargetGroups:
- TargetGroupArn:
!FindInMap [EnvMap, !Ref Env, LoadBalancerRouteTargetArn]
Weight: 1
TargetGroupArn:
!FindInMap [EnvMap, !Ref Env, LoadBalancerRouteTargetArn]
Conditions:
- Field: path-pattern
PathPatternConfig:
Values:
- /*
Values:
- /*
ListenerArn: !Ref HttpsListener
Priority: 50000
Error
"Status Reason" from the event.
Resource handler returned message: "Invalid request provided: AWS::ElasticLoadBalancingV2::ListenerRule Validation exception" (RequestToken: 16bd4239-0d41-b16f-2963-b0a774009dfd, HandlerErrorCode: InvalidRequest)
Try to remove PathConfigPattern from the Conditions key:
HttpsListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- Type: "forward"
ForwardConfig:
TargetGroupStickinessConfig:
Enabled: false
TargetGroups:
- TargetGroupArn: !FindInMap [ EnvMap, !Ref Env, LoadBalancerRouteTargetArn ]
Weight: 1
TargetGroupArn: !FindInMap [ EnvMap, !Ref Env, LoadBalancerRouteTargetArn ]
Order: 1
Conditions:
- Field: path-pattern
Values:
- "/*"
ListenerArn: !Ref HttpsListener
Priority: 50000
Also, make sure your EnvMap map looks like this:
Parameters:
Env:
Type: String
Default: sandbox
Mappings:
EnvMap:
sandbox:
LoadBalancerRouteTargetArn: "arn:aws:elasticloadbalancing:eu-west-1:111111111111:targetgroup/my-tg-1/222222222222"
prod:
LoadBalancerRouteTargetArn: "arn:aws:elasticloadbalancing:eu-west-1:333333333333:targetgroup/my-tg-2/444444444444"

Error creating AWS ElasticSearch Domain (now as AWS OpenSearch service) by CloudFormation

I am trying to create an AWS ElasticSearch Domain (now as AWS OpenSearch service) by cloudformation template but getting error "Resource handler returned message: "null" (RequestToken: 90149a2b-10a1-2609-20e0-9e839731fc2f, HandlerErrorCode: InternalFailure)".
CloudFormation template:
AWSTemplateFormatVersion: "2010-09-09"
Description: ""
Resources:
LogsLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/aws/aes/domains/domainName/application-logs"
LogsLogGroup2:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/aws/aes/domains/domainName/index-logs"
LogsLogGroup3:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: "/aws/aes/domains/domainName/search-logs"
OpenSearchServiceDomain:
Type: "AWS::OpenSearchService::Domain"
DependsOn:
- LogsLogGroup
- LogsLogGroup2
- LogsLogGroup3
Properties:
DomainName: "domainName"
ClusterConfig:
DedicatedMasterCount: 3
DedicatedMasterEnabled: true
DedicatedMasterType: "c4.large.search"
InstanceCount: 2
InstanceType: "c4.large.search"
ZoneAwarenessEnabled: true
ZoneAwarenessConfig:
AvailabilityZoneCount: 2
WarmEnabled: false
AccessPolicies:
Version: '2012-10-17'
Statement:
-
Effect: 'Allow'
Principal:
AWS: '*'
Action: 'es:*'
Resource: 'arn:aws:es:us-east-1:xxxxxxxxx:domain/domainName/*'
VPCOptions:
SecurityGroupIds:
- !ImportValue "sg-xxxxxxxxx"
SubnetIds:
- !ImportValue "subnet-xxxxxxxxx"
- !ImportValue "subnet-xxxxxxxxx"
- !ImportValue "subnet-xxxxxxxxx"
EncryptionAtRestOptions:
Enabled: true
NodeToNodeEncryptionOptions:
Enabled: true
AdvancedOptions:
"rest.action.multi.allow_explicit_index": "true"
EBSOptions:
EBSEnabled: true
VolumeType: "gp2"
VolumeSize: 200
LogPublishingOptions:
ES_APPLICATION_LOGS:
CloudWatchLogsLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/aes/domains/domainName/application-logs"
Enabled: true
INDEX_SLOW_LOGS:
CloudWatchLogsLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/aes/domains/domainName/index-logs"
Enabled: true
SEARCH_SLOW_LOGS:
CloudWatchLogsLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/aes/domains/domainName/search-logs"
Enabled: true
DomainEndpointOptions:
EnforceHTTPS: true
TLSSecurityPolicy: "Policy-Min-TLS-1-0-2019-07"
AdvancedSecurityOptions:
Enabled: true
InternalUserDatabaseEnabled: true
MasterUserOptions:
MasterUserName: "user"
MasterUserPassword: "password"
We had the same vague error. Adding AmazonOpenSearchServiceFullAccess policy to the deploy user got beyond it.
Just an FYI - you don't need AccessPolicies if you're creating an VPC based Domain OR you don't need Security Groups/Subnets if you're creating an Internet based Domain.
AccessPolicies are only required for an Internet based Domains
VPCOptions are only required for VPC based Domains

Cloudformation: Invalid permissions on Lambda function found with API GW Integration

This is my cloudformation template, which deploys successfully:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Deploy Da Blog
Resources:
ArticleTable:
Type: AWS::DynamoDB::Table
Properties:
KeySchema:
- AttributeName: id
KeyType: HASH
AttributeDefinitions:
- AttributeName: id
AttributeType: S
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: !Sub ${AWS::StackName}-Article
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
############################### BLOG API ############################
BlogRestApiLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/apigateway/${AWS::StackName}'
BlogRestApiLoggingRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'BlogRestApiLoggingRole-${AWS::StackName}'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
## specifies the IAM role that Amazon API Gateway uses to write API logs to Amazon CloudWatch Logs
BlogRestApiAccount:
Type: AWS::ApiGateway::Account
Properties:
CloudWatchRoleArn: !GetAtt [ BlogRestApiLoggingRole, Arn ]
BlogRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub '${AWS::StackName}'
Description: Blog API
EndpointConfiguration:
Types:
- EDGE
BlogRestApiDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref BlogRestApi
Description: Automatically created by the RestApi construct
DependsOn:
- BlogRestApi
- BlogApiResource
- BlogApiIdResource
- ListArticlesMethod
- PostArticleMethod
- GetArticleMethod
BlogRestApiStageProd:
Type: AWS::ApiGateway::Stage
DependsOn:
- BlogRestApiDeployment
Properties:
RestApiId: !Ref BlogRestApi
DeploymentId: !Ref BlogRestApiDeployment
StageName: prod
MethodSettings:
- LoggingLevel: INFO
ResourcePath: '/*'
HttpMethod: '*'
MetricsEnabled: true
BlogApiRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'BlogApiRole-${AWS::StackName}'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
BlogApiPolicy:
Type: AWS::IAM::Policy
DependsOn:
- BlogApiRole
- ListArticlesFunction
- PostArticleFunction
- GetArticleFunction
Properties:
PolicyName: !Sub 'BlogApiPolicy-${AWS::StackName}'
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: 'Allow'
Action: 'lambda:InvokeFunction'
Resource:
- !GetAtt ListArticlesFunction.Arn
- !GetAtt PostArticleFunction.Arn
- !GetAtt GetArticleFunction.Arn
Roles:
- !Ref BlogApiRole
############################### END OF Blog API ############################
############################### START OF Blog Functions ####################
BlogApiResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt [ BlogRestApi, RootResourceId ]
PathPart: article
RestApiId: !Ref BlogRestApi
BlogApiIdResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !Ref BlogApiResource
PathPart: "{id}"
RestApiId: !Ref BlogRestApi
BlogApiOptionsMethod:
Type: AWS::ApiGateway::Method
DependsOn:
- BlogApiResource
Properties:
ApiKeyRequired: true
AuthorizationType: NONE
HttpMethod: OPTIONS
Integration:
Type: MOCK
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': '{"statusCode": 200}'
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'GET,POST,PATCH,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
'application/json': '{}'
MethodResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: false
method.response.header.Access-Control-Allow-Methods: false
method.response.header.Access-Control-Allow-Origin: false
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
ListArticlesFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-list-articles-function'
Handler: com.zenithwebfoundry.blog.api.ListArticlesHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:Query
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
ListArticlesMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListArticlesFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: ListArticles
PostArticleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-post-article-function'
Handler: com.zenithwebfoundry.blog.api.SaveArticleHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:PutItem
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
PostArticleMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostArticleFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: PostArticle
GetArticleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-get-article-function'
Handler: com.zenithwebfoundry.blog.api.GetArticleHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
GetArticleMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiIdResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetArticleFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: GetArticle
############################### END OF Blog Functions ######################
BlogAPIDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
DomainName: !Join [ ".", ['blogapi', !Ref DomainName]]
EndpointConfiguration:
Types:
- EDGE
CertificateArn: !Ref DomainCert
SecurityPolicy: TLS_1_0
BlogAPIHostedZone:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref BlogAPIDomainName
BlogAPIBasePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref BlogAPIDomainName
RestApiId: !Ref BlogRestApi
Stage: 'prod'
Route53RecordSetGroup:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref DomainHostedZoneId
RecordSets:
- Name: !Join [ "", ['blog', '.', !Ref DomainName, '.']]
Type: A
TTL: '300'
ResourceRecords:
- 52.64.238.177
- Name: !Join [ ".", ['blogapi', !Ref DomainName]]
Type: A
AliasTarget:
# HostedZoneId: !Ref BlogAPIHostedZone # distributionHostedZoneId - alias target name does not lie in the target zone
HostedZoneId: Z2FDTNDATAQYW2
# https://j97h8bvml9.execute-api.ap-southeast-2.amazonaws.com/prod/articles
DNSName: !GetAtt [BlogAPIDomainName, DistributionDomainName]
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join [ ".", [ !Ref 'AWS::StackName', 'assets' ] ]
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: [GET,PUT,POST,DELETE,HEAD]
AllowedOrigins: ['http://localhost*']
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join [ ".", [ !Ref 'AWS::StackName', 'web' ] ]
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebsiteConfiguration:
IndexDocument: 'index.html'
ErrorDocument: 'index.html'
Parameters:
ParamCodePackage:
Type: String
ParamCodeBucket:
Type: String
DomainName:
Description: "Public DNS Zone Name"
Type: String
DomainHostedZoneId:
Type: String
Description: 'The AWS HostedZoneId of the above domain name'
DomainCert:
Type: String
Description: 'The arn reference to the certificate used with the domain.'
ParamRequestMappingTemplate:
Type: String
Description: 'Read from resources/templates'
SaveArticleHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.SaveArticleHandler'
GetArticleHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.GetArticleHandler'
ListArticlesHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.ListArticlesHandler'
ParamAPIGatewayRequestEventMappingTemplate:
Type: String
Default: '{
"resource" : "$context.resourceId",
"path" : "$context.path",
"httpMethod" : "$context.httpMethod",
"headers": {
#foreach($header in $input.params().header.keySet())
"$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end
#end
},
"method": "$context.httpMethod",
"pathParameters": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
#end
},
"queryStringParameters": {
#foreach($queryParam in $input.params().querystring.keySet())
"$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end
#end
},
"body" : $input.json("$"),
"isBase64Encoded": false
}'
ParamAPIGatewayResponseEventMappingTemplate:
Type: String
Default: '#set($statusCode = $input.path("$.statusCode"))
#set($context.responseOverride.status = $statusCode)
#set($headers = $input.path("$.headers"))
#foreach($key in $headers.keySet())
#set($context.responseOverride.header[$key] = $headers[$key])
#end
#set($context.responseOverride.header.Access-Control-Allow-Headers = "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token")
#set($context.responseOverride.header.Access-Control-Allow-Methods = "*")
#set($context.responseOverride.header.Access-Control-Allow-Origin = "*")
{
"body": $input.json("$.body")
}
'
Outputs:
ArticleEndpoint:
Value: !Join ["", ['https://', !Ref BlogRestApi, '.execute-api.ap-southeast-2.', !Ref 'AWS::URLSuffix', '/', !Ref BlogRestApiStageProd, '/'] ]
The problem is that when I try to call the ListArticlesFunction using the following curl expression:
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET https://blogapi.zenithwebfoundry.com/article
I get the following:
HTTP/2 500
content-type: application/json
content-length: 36
date: Sun, 30 Aug 2020 09:09:50 GMT
x-amzn-requestid: c1948ed5-18c5-4807-8904-3c1c2af06c25
x-amzn-errortype: InternalServerErrorException
x-amz-apigw-id: SE3y1G71ywMF5fw=
x-cache: Error from cloudfront
via: 1.1 647846f53eba457a8e4ba1d1d42a6336.cloudfront.net (CloudFront)
x-amz-cf-pop: SYD1-C1
x-amz-cf-id: 6bnMhNRUdf1znTmD0vQn86UZcMF_j9JCzFb-JvhwwT9j6ch4P8t20g==
{"message": "Internal server error"}
Checking cloudwatch, I see the slightly cryptic error:
Execution failed due to configuration error: Invalid permissions on Lambda function
The same thing happens if I go into the APIGateway service console and go to the article GET resource. So at least its consistent.
I have tested the handler in isolation and there's no compilation problems anywhere, so I'm pretty sure its stack-related.
I gather that the problem must either be in the BlogApiPolicy Policy or the Lambda Policy, but nothing I set seems to work. Does anyone know what cloudformation shenanigans is needed to get this right?
The answer is that I needed the role added as a Credentials: entry in the method of each Lambda, for example:
ListArticlesMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListArticlesFunction.Arn}/invocations'
Credentials: !GetAtt [BlogApiRole, Arn]
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: ListArticles
Why this is suddenly needed, when this was never needed before, is quite beyond me, but I must thank #Marcin for his/her patient assistance and convincing my doubting mind.
Based on the comments.
The issue was that BlogApiRole, although being created, it was not used in any of the API Gateway methods. This is required, because API Gateway needs to have explicit permissions to invoke a lambda functions. To enable this, Credentials in AWS::ApiGateway::Method Integration should be set.
The alternative is to use AWS::Lambda::Permission. The use of the AWS::Lambda::Permission is especially useful when there are many methods in the API gateway which require invoking the lambda. The reason is that you can create one such AWS::Lambda::Permission for the lambda function which can allow API gateway access to the lambda as a whole. This saves us from defining Credentials for each method independently.

SAM template resource cache override

Within a SAM template file I have defined an API as well as two Lambda functions that have events configured for a few routes.
At API level I have enabled the caching for the API and a TTL. I would now want to have the caching settings overridden for one of the API routes but I don't seem to find out how to go about doing that.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Elrond API Facade
Resources:
Api:
Type: AWS::Serverless::Api
Properties:
Name: api
StageName: Prod
CacheClusterEnabled: true
CacheClusterSize: '0.5'
MethodSettings:
- CachingEnabled: true
CacheTtlInSeconds: 30
HttpMethod: '*'
ResourcePath: '/*'
Handler:
Type: AWS::Serverless::Function
Properties:
FunctionName: handler
CodeUri: ./handler
Handler: ./handler/index.handler
Events:
Method:
Type: Api
Properties:
RestApiId: !Ref Api
Path: /method
Method: get
# --> what to add here to override global caching settings?
Lambda functions don't include caching out of the box. Lets try instead to:
Create another "AWS::Serverless::Api" Resource with your new caching needs
Have your intended "AWS::Serverless::Function" resource use it instead.
Here is an example of a new "AWS::Serverless::Api" with more caching added into the mix
Resources:
Api:
Type: AWS::Serverless::Api
Properties:
Name: api
StageName: Prod
CacheClusterEnabled: true
CacheClusterSize: '0.5'
MethodSettings:
- CachingEnabled: true
CacheTtlInSeconds: 30
HttpMethod: '*'
ResourcePath: '/*'
BiggerCacheApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
CacheClusterEnabled: true
CacheClusterSize: '0.5'
MethodSettings:
- CachingEnabled: true
CacheTtlInSeconds: 3000
HttpMethod: '*'
ResourcePath: '/*'
Handler:
Type: AWS::Serverless::Function
Properties:
FunctionName: handler
CodeUri: ./handler
Handler: ./handler/index.handler
Events:
Method:
Type: Api
Properties:
RestApiId: !Ref BiggerCacheApi
Path: /method
Method: get
OtherHandler:
Type: AWS::Serverless::Function
Properties:
...
RestApiId: !Ref Api
...

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