Create methods under / using CF for API Gateway - aws-cloudformation

How do I create methods under API Gateway's root / folder using CF? So for example I have a Gateway that looks like the following:
/
OPTIONS
POST
However when trying to do that with CF I get:
Resource's path part only allow a-zA-Z0-9._- and curly braces at the beginning and the end. So my PathPart is the offending line.
ApiGate:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt
- ApiGateApi
- RootResourceId
PathPart: '{/}'
RestApiId: !Ref ApiGateApi
I can change the PathPart to something else but then it creates it as a child object under / which is what I don't want.

Turns out after adding the following to my AWS::ApiGateway::Method it works now.
MyMethodOPTIONS:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !GetAtt MyRestApi.RootResourceId
Here is more context into my Template:
ApiGatewayMethodOPTIONS:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !GetAtt ApiGatewayRestApi.RootResourceId
RestApiId: !Ref ApiGatewayRestApi
AuthorizationType: NONE
HttpMethod: OPTIONS
Integration:
Type: MOCK
IntegrationResponses:
- 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: "'POST,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/json: ''
StatusCode: '200'
PassthroughBehavior: NEVER
RequestTemplates:
application/json: '{"statusCode": 200}'
MethodResponses:
- ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
StatusCode: '200'
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
ApiKeySourceType: HEADER
EndpointConfiguration:
Types:
- REGIONAL
Name: SearchAPI

This fixes my problem, in my case I needed to have 2 methods:
1. will respond to requests to root e.g. https://<api-url>/prod or https://<api-url>/prod/. This will use the RootResourceId of the API Gateway:
ResourceId: !GetAtt myApiGateway.RootResourceId
this will respond to requests to whatever has been set under https://<api-url>/prod/. Could be petstore or if using {proxy+} then the backend workload will try to resolve the request. It will refer to the Resource type defined in the template:
ResourceId: !Ref myResource

Related

How to get API Gateway to validate the pattern of HTTP query string parameters?

I'm trying to get API Gateway to do the above. See template at the bottom - there is a single message parameter, which I've tried to restrict to either foo or bar values, via specification of an AWS::ApiGateway::Model resource, bound to Content-Type application/x-www-form-urlencoded.
Now APIGW will validate the presence of the message parameter -
curl -H "Content-Type: application/x-www-form-urlencoded" "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/1-0-0/hello?messag=foo"
{"message": "Missing required request parameters: [message]"}
But it doesn't seem to validate or restrict the value sent -
curl -H "Content-Type: application/x-www-form-urlencoded" "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/1-0-0/hello?message=whatever"
you sent 'whatever'
Any idea what I am doing wrong here?
AWSTemplateFormatVersion: '2010-09-09'
Outputs:
PublicApiEndpoint:
Value:
Fn::Sub: https://${PublicApiRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${PublicApiStage}
Parameters:
MemorySizeDefault:
Default: '512'
Type: String
RuntimeVersion:
Default: '3.8'
Type: String
TimeoutDefault:
Default: '5'
Type: String
Resources:
HelloFunction:
Properties:
Code:
ZipFile: |
def handler(event, context):
message=event["queryStringParameters"]["message"]
response="you sent '%s'" % message
return {'statusCode': 200,
'headers': {"Content-Type": "text/plain"},
'body': response}
Handler: index.handler
MemorySize:
Ref: MemorySizeDefault
Role:
Fn::GetAtt:
- HelloFunctionRole
- Arn
Runtime:
Fn::Sub: python${RuntimeVersion}
Timeout:
Ref: TimeoutDefault
Type: AWS::Lambda::Function
HelloFunctionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: hello-function-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
HelloEndpointMethod:
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri:
Fn::Sub:
- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${arn}/invocations
- arn:
Fn::GetAtt:
- HelloFunction
- Arn
RequestParameters:
"method.request.querystring.message": true
RequestValidatorId:
Ref: HelloEndpointValidator
RequestModels:
"application/x-www-form-urlencoded": HelloEndpointModel
ResourceId:
Ref: HelloEndpointResource
RestApiId:
Ref: PublicApiRestApi
Type: AWS::ApiGateway::Method
HelloEndpointPermission:
Properties:
Action: lambda:InvokeFunction
FunctionName:
Ref: HelloFunction
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Sub: arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PublicApiRestApi}/${PublicApiStage}/GET/hello
Type: AWS::Lambda::Permission
HelloEndpointResource:
Properties:
ParentId:
Fn::GetAtt:
- PublicApiRestApi
- RootResourceId
PathPart: hello
RestApiId:
Ref: PublicApiRestApi
Type: AWS::ApiGateway::Resource
HelloEndpointValidator:
Properties:
RestApiId:
Ref: PublicApiRestApi
ValidateRequestParameters: true
Type: AWS::ApiGateway::RequestValidator
HelloEndpointModel:
Properties:
RestApiId:
Ref: PublicApiRestApi
ContentType: 'application/x-www-form-urlencoded'
Name: HelloEndpointModel
Schema:
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
properties:
message:
type: string
pattern: "^((foo)|(bar))$"
required:
- message
Type: AWS::ApiGateway::Model
PublicApiDeployment:
DependsOn:
- HelloEndpointMethod
Properties:
RestApiId:
Ref: PublicApiRestApi
Type: AWS::ApiGateway::Deployment
PublicApiRestApi:
Properties:
Name:
Fn::Sub: public-api-rest-api-${AWS::StackName}
Type: AWS::ApiGateway::RestApi
PublicApiStage:
Properties:
DeploymentId:
Ref: PublicApiDeployment
RestApiId:
Ref: PublicApiRestApi
StageName: 1-0-0
Type: AWS::ApiGateway::Stage
Apparently this is not possible - https://twitter.com/alexbdebrie/status/1603059764489252864?s=20&t=LI7OUEw9b5qxCIvkeP4AYQ

AWS API Gateway Method Response in CloudFormation - Output passthrough: No

I am trying to configure the Integration Response - Output Passthrough as No. I have configure the parameter PassthroughBehavior as NEVER, however it doesn´t change the output as desired.
Here is my method resource (in YAML):
apiresourcemsactualizartrxtransaccionesv1actualizarTransaccionesoptions:
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref apigateway
ResourceId: !Ref apiresourcemsactualizartrxtransaccionesv1actualizarTransacciones
HttpMethod: OPTIONS
AuthorizationType: NONE
RequestParameters:
method.request.path.proxy: true
Integration:
PassthroughBehavior: NEVER
Type: MOCK
IntegrationHttpMethod: OPTIONS
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: "'OPTIONS,PATCH'"
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
-
StatusCode: 200
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
DependsOn: apiresourcemsactualizartrxtransaccionesv1actualizarTransacciones
Thank you very much for the help in advance.
My understanding is PassthroughBehavior refers to the request, not the output.
I think your output passthrough is setting to "Yes" because your response model is empty. If you set up a model/transform, that should change output passthrough to No.

Cloudformation - Type: "AWS::ApiGateway::Method" returns {"message": "Internal server error"}

I get {"message": "Internal server error"} return from AWS API gateway when I write a Cloudformation template. Everything works well if I configure them on AWS console.
curl https://abcdefgh.execute-api.ap-southeast-1.amazonaws.com/demo/getitems
{"message": "Internal server error"}
I check all parts in the CF template and I think the problem is in "AWS::ApiGateway::Method". All configuration/parameters are the same between CF template and AWS console ( I checked them on console). When I delete the method created by CF, re-create and re-deploy it manually, it works.
I believe that "AWS::Lambda::Permission" works because I see it in Resource-based policy.
Does anyone have a solution to this problem? Thank you in advance.
The CF template:
apigwGETMethod:
DependsOn:
- apiResourceGET
- lambdaFunction3
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
ResourceId: !Ref apiResourceGET
RestApiId: !Ref apiGateway
Integration:
IntegrationHttpMethod: "GET"
Type: "AWS"
Credentials: !GetAtt ServerlessIAMRole.Arn
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt lambdaFunction3.Arn
IntegrationResponses:
- ResponseTemplates:
"application/json": ""
StatusCode: 200
MethodResponses:
- ResponseModels:
"application/json": "Empty"
StatusCode: 200
apiGatewayDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn:
- apiGatewayGETMethod
Properties:
RestApiId: !Ref apiGateway
StageName: !Ref apiGatewayStageName
lambdaFunction3:
DependsOn:
- ServerlessIAMRole
Type: "AWS::Serverless::Function"
Properties:
FunctionName: !Ref lambdaFunction3Name
Handler: lambda_function3.lambda_handler
Description: "get all items from DynamoDB table"
Runtime: python3.6
CodeUri: .
Role: !GetAtt ServerlessIAMRole.Arn
lambda3ApiGatewayInvoke:
DependsOn:
- lambdaFunction3
- apiGatewayGETMethod
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt lambdaFunction3.Arn
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*"

Getting an error trying to create an AWS API Gateway via Cloudformation

I'm trying to make a simple Cloudformation to create a website hosted on S3 with an API Gateway backend. Everything seems OK as far as I can tell but I get errors when trying to create the API Gateway:
Errors found during import: Unable to put integration on 'ANY' for resource at path '/{proxy+}': AWS ARN for integration must contain path or action (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: b28983d9-687c-11e8-8692-27df1db97456)
The gateway should just be a single route that sends everything to a single lambda. Should be super simple.
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Website S3 Hosted, API Gateway Backend
Parameters:
DomainName:
Type: String
Description: The DNS name of an Amazon Route 53 hosted zone e.g. server.com
AllowedPattern: '(?!-)[a-zA-Z0-9-.]{1,63}(?<!-)'
ConstraintDescription: must be a valid DNS zone name.
Mappings:
S3RegionMap:
us-east-1:
S3HostedZoneId: Z3AQBSTGFYJSTF
S3WebsiteEndpoint: s3-website-us-east-1.amazonaws.com
us-west-1:
S3HostedZoneId: Z2F56UZL2M1ACD
S3WebsiteEndpoint: s3-website-us-west-1.amazonaws.com
us-west-2:
S3HostedZoneId: Z3BJ6K6RIION7M
S3WebsiteEndpoint: s3-website-us-west-2.amazonaws.com
eu-west-1:
S3HostedZoneId: Z1BKCTXD74EZPE
S3WebsiteEndpoint: s3-website-eu-west-1.amazonaws.com
ap-southeast-1:
S3HostedZoneId: Z3O0J2DXBE1FTB
S3WebsiteEndpoint: s3-website-ap-southeast-1.amazonaws.com
ap-southeast-2:
S3HostedZoneId: Z1WCIGYICN2BYD
S3WebsiteEndpoint: s3-website-ap-southeast-2.amazonaws.com
ap-northeast-1:
S3HostedZoneId: Z2M4EHUR26P7ZW
S3WebsiteEndpoint: s3-website-ap-northeast-1.amazonaws.com
sa-east-1:
S3HostedZoneId: Z31GFT0UA1I2HV
S3WebsiteEndpoint: s3-website-sa-east-1.amazonaws.com
Resources:
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: '/'
Policies:
- PolicyName: execution
PolicyDocument:
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:ListBucket
Resource: '*'
- Effect: Allow
Action:
- ec2:DescribeNetworkInterfaces
- ec2:CreateNetworkInterface
- ec2:DeleteNetworkInterface
Resource: '*'
- Effect: Allow
Action:
- cognito-idp:AdminGetUser
- cognito-idp:AdminUpdateUserAttributes
Resource: '*'
APIGatewayExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
Path: '/'
Policies:
- PolicyName: execution
PolicyDocument:
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: '*'
LambdaFunctionAPI:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: exports.handler = function (event, context, callback) { callback(null, event); };
Handler: index.handler
MemorySize: 128
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: nodejs4.3
Timeout: 30
APIGateway:
Type: AWS::ApiGateway::RestApi
Properties:
FailOnWarnings: true
Name: !Join ['-', !Split ['.', !Join ['.', ['api', !Ref DomainName]]]]
Body:
swagger: '2.0'
info:
version: 0.0.1
title: !Join [' ', ['API route for', !Ref DomainName]]
basePath: '/api'
paths:
'/{proxy+}':
options:
summary: CORS support
description: |
Enable CORS by returning correct headers
consumes:
- application/json
produces:
- application/json
tags:
- CORS
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: |
{}
responses:
'200':
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-any-method:
produces:
- "application/json"
responses:
'200':
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-swagger-router-controller: main
x-amazon-apigateway-integration:
type: aws_proxy
httpMethod: POST
uri: !GetAtt LambdaFunctionAPI.Arn
credentials: !Ref APIGatewayExecutionRole
definitions:
Empty:
type: "object"
title: "Empty Schema"
APIDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref APIGateway
Description: Deploy for live
StageName: Live
WebsiteBucket:
Type: AWS::S3::Bucket
Properties:
BucketName:
Ref: DomainName
AccessControl: PublicRead
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: 404.html
Tags:
- Key: Name
Value: !Join ['_', ['WebsiteBucket', !Ref 'AWS::StackName']]
- Key: Domain
Value: !Ref DomainName
DeletionPolicy: Retain
WWWBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join ['.', ['www', !Ref DomainName]]
AccessControl: PublicRead
WebsiteConfiguration:
RedirectAllRequestsTo:
HostName: !Ref WebsiteBucket
Tags:
- Key: Name
Value: !Join ['_', ['WWWBucket', !Ref 'AWS::StackName']]
- Key: Domain
Value: !Ref DomainName
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Statement:
- Action:
- s3:GetObject
Effect: Allow
Resource: !Join ['', ['arn:aws:s3:::', !Ref WebsiteBucket, '/*']]
Principal: '*'
WWWBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WWWBucket
PolicyDocument:
Statement:
- Action:
- s3:GetObject
Effect: Allow
Resource: !Join ['', ['arn:aws:s3:::', !Ref WWWBucket, '/*']]
Principal: '*'
DNS:
Type: AWS::Route53::HostedZone
Properties:
HostedZoneConfig:
Comment: !Join [' ', ['Hosted zone for', !Ref DomainName]]
Name: !Ref DomainName
HostedZoneTags:
- Key: Application
Value: Blog
DNSRecord:
Type: AWS::Route53::RecordSetGroup
DependsOn: DNS
Properties:
HostedZoneName:
Fn::Join: ['', [!Ref DomainName, '.']]
Comment: Zone records.
RecordSets:
- Name: !Ref DomainName
Type: A
AliasTarget:
HostedZoneId: !FindInMap [S3RegionMap, !Ref 'AWS::Region', S3HostedZoneId]
DNSName: !FindInMap [S3RegionMap, !Ref 'AWS::Region', S3WebsiteEndpoint]
- Name: !Join ['.', ['www', !Ref DomainName]]
Type: A
AliasTarget:
HostedZoneId: !FindInMap [S3RegionMap, !Ref 'AWS::Region', S3HostedZoneId]
DNSName: !FindInMap [S3RegionMap, !Ref 'AWS::Region', S3WebsiteEndpoint]
Outputs:
S3WebsiteURL:
Value: !GetAtt WebsiteBucket.WebsiteURL
Description: URL for website hosted on S3
The URI you should use to connect to the Lambda is not the Arn of the Lambda, but an API gateway invocation URI. Additionally, you need to change the credential line from a ref to the Arn of the execution role.
Here a short excerpt of the changed section:
x-amazon-apigateway-integration:
type: aws_proxy
httpMethod: POST
uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunctionAPI.Arn}/invocations"
credentials: !GetAtt APIGatewayExecutionRole.Arn
For those who are using terraform and getting this error, the following works (note that the uri parameter should not be the lambda arn, but the invoke arn which is different):
resource "aws_api_gateway_integration" "ordersIntegration" {
rest_api_id = aws_api_gateway_rest_api.msdApi.id
resource_id = aws_api_gateway_resource.orders.id
http_method = aws_api_gateway_method.ordersget.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "arn:aws:apigateway:${var.AWS_REGION}:lambda:path/2015-03-31/functions/${var.LAMBDA_FN_ARN}/invocations"
}
Where the LAMBDA_FN_ARN is simply "arn:aws:lambda:${var.AWS_REGION}:${var.AWS_ACCOUNT_ID}:function:${var.LAMBDA_NAME}"
Also, 2015-03-31 is a constant value that needs to be used.

Not able to map VPC_LINK with Method Integration on Api Gateway using cloud formation

I am using Cloudformation to configure API Gateway Method to use VPC Link.
Its not working.
Sample code is as below:
ProxyResourceANY:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref RestApiResource
HttpMethod: ANY
AuthorizationType: NONE
Integration:
Type: HTTP_PROXY
IntegrationHttpMethod: ANY
#ConnectionType: VPC_LINK
#ConnectionId: !Ref VpcLink
Uri: !Sub http://${LoadBalancerUrl}:8098
CloudFormation error:
Encountered unsupported property ConnectionId
You can't use !Ref there but you have 2 solution for this :
Simply use !Sub instead of !Ref like this :
#ConnectionId: !Sub ${VpcLink}
Or more complex you can use stage variable :
Use a stage variable "${stageVariables.variable}" in your method instead of the reference to your VpcLink
ProxyResourceANY:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref RestApiResource
HttpMethod: ANY
AuthorizationType: NONE
Integration:
Type: HTTP_PROXY
IntegrationHttpMethod: ANY
ConnectionType: VPC_LINK
ConnectionId: '${stageVariables.vpcLink}'
Uri: !Sub http://${LoadBalancerUrl}:8098
And then set your stage variable referencing your VpcLink when you create your stage :
Stage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: 'Stage'
Variables:
VpcLink: !Ref VpcLink