i’m trying to add proxy to my lambda function, but i’m getting error:
“CREATE_FAILED: ApiGatewayResourceJokes (AWS::ApiGateway::Resource)
Resource handler returned message: “Another resource with the same
parent already has this name: example (Service: ApiGateway, Status
Code: 409, Request ID: 3fb2a85c-6fd2-4414-ad3f-72ee095da48b)”
(RequestToken: f33a6da3-be94-9696-3fd6-35a7bc52ef0a, HandlerErrorCode:
AlreadyExists)”
example:
handler: lambdas/endpoints/proxy.handler
events:
- http: GET /example
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi # our default Rest API logical ID
- RootResourceId
PathPart: jokes # the endpoint in your API that is set as proxy
RestApiId:
Ref: ApiGatewayRestApi
ProxyMethod:
Type: AWS::ApiGateway::Method
Properties:
ResourceId:
Ref: ProxyResource
RestApiId:
Ref: ApiGatewayRestApi
HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ?
MethodResponses:
- StatusCode: 200
Integration:
IntegrationHttpMethod: POST
Type: HTTP
Uri: http://api.icndb.com/jokes/random # the URL you want to set a proxy to
IntegrationResponses:
- StatusCode: 200
Related
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
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}/*"
I want to use serverless-offline to authenticate with a custom authorizer.
The file serverless.yml is shown below.
functions:
auth:
handler: src/authorizer.handler
...
custom:
serverless-offline:
httpPort: 4000
resourceRoutes: true
...
resources:
Resources:
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: protected
RestApiId:
Ref: ApiGatewayRestApi
ProxyPathResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Ref: ProxyResource
PathPart: "{proxy+}"
RestApiId:
Ref: ApiGatewayRestApi
ProxyPathMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: "CUSTOM"
AuthorizerId:
Ref: AuthApiGatewayAuthorizer
ResourceId:
Ref: ProxyPathResource
RestApiId:
Ref: ApiGatewayRestApi
HttpMethod: ANY
RequestParameters:
method.request.path.proxy: false
MethodResponses:
- StatusCode: 200
- StatusCode: 401
Integration:
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: https://my-json-server.typicode.com/{proxy}
RequestParameters:
integration.request.path.proxy: method.request.path.proxy
IntegrationResponses:
- StatusCode: 200
- StatusCode: 401
Run it.
$ sls offline
...
offline: ANY /development/auth-test/{proxy*} -> https://my-json-server.typicode.com/{proxy}
offline: [HTTP] server ready: http://localhost:4000 🚀
After that, accessing the following uri will return a normal response.
The custom authorizer does not seem to be working.
$ curl 'http://localhost:4000/development/protected/typicode/demo/profile'
{"name": "typicode"}
Running curl through the api-gateway will result in 401
$ sls deploy
$ curl 'https://xxxx.execute-api.{region}.amazonaws.com/development/protected/typicode/demo/profile' // This is a 401
$ curl 'https://xxxx.execute-api.{region}.amazonaws.com/development/protected/typicode/demo/profile' --header 'Authorization: Bearer {valid_token}' // This is 200
Does serverless-offline go through authentication with http_proxy?
Pardon my broken English!
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
I want to use the standard API Keys feature of API Gateway. If I use standard cloudformation this is possible by setting the property ApiKeyRequired to true for a method. How can I do this with SAM?
I tried using swagger but that does not seem to work:
swagger: "2.0"
info:
title: !Ref AWS::StackName
paths:
"/machines/{resourceid}":
get:
parameters:
- name: resourceid
in: path
type: string
required: true
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambda.Arn}/invocations
responses: {}
security:
- authorizer: []
securityDefinitions:
authorizer:
type: apiKey
name: Authorization
in: header
Any suggestions?
The following swagger definition works:
DefinitionBody:
swagger: "2.0"
info:
title: !Ref AWS::StackName
x-amazon-apigateway-api-key-source : "HEADER"
paths:
"/machines/{resourceId}":
get:
parameters:
- name: resourceId
in: path
type: string
required: true
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MessagingServiceTestHandler.Arn}/invocations
responses: {}
security:
- api_key: []
securityDefinitions:
api_key:
type: "apiKey"
name: "x-api-key"
in: "header"
The name of the api key header must be x-api-key rather than the standard Authorization header.