I have a problem with AWS SAM and provisioning API Gateway configurations. I am trying to do a few things:
Configure the API gateway to require api-key in the headers
Create my own stage as defined in my config files.
The API gateway model defined in my file is not being created
Currently, the API gateway gets provisioned and linked to my lambda function but it fails in the two requirements above. Below are my files: template.yaml and swagger.yaml.
Template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-nfeed-s3
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 60
Api:
EndpointConfiguration: REGIONAL
Resources:
SAMnfeedS3API:
Type: AWS::Serverless::Api
Properties:
StageName: alpha
DefinitionUri: ./swagger.yaml
Resources:
SAMnfeedS3Lambda:
Type: AWS::Serverless::Function
Properties:
CodeUri: test-function-sam/
Handler: nfeed_vdp_clusters.lambda_handler
Runtime: python3.6
Role: arn:aws:iam::XXXXXXX:role/Lambda
Events:
SAMnfeedS3API:
Type: Api
Properties:
Path: /vdp_clusters
Method: GET
Environment:
Variables:
TEST: test
Outputs:
SAMnfeedS3API:
Description: "API Gateway endpoint URL for Staging env"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Staging/vdp_clusters"
SAMnfeedS3Lambda:
Description: "Lambda Function ARN"
Value: !GetAtt SAMnfeedS3Lambda.Arn
Swagger.yaml
---
swagger: '2.0'
info:
title: !Ref AWS::StackName
basePath: "/alpha"
schemes:
- "https"
x-amazon-apigateway-api-key-source : "HEADER"
paths:
"/vdp_clusters":
get:
consumes:
- application/json
produces:
- application/json
parameters:
- name: x-api-key
in: header
required: true
type: string
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
uri: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:XXXXXXXXX:function:${SAMnfeedS3Lambda.Arn}/invocations
responses:
default:
statusCode: "200"
httpMethod: "POST"
type: aws_proxy
security:
- api_key: []
securityDefinitions:
api_key:
type: "apiKey"
name: "x-api-key"
in: "header"
definitions:
Empty:
type: "object"
title: "Empty Schema"
$schema: "http://json-schema.org/draft-04/schema#"
As defined in my swagger and template files, "alpha" stage should be created for the gateway but nothing appears. The "Empty" model and api-key requirement also do not appear. Any help, would be appreciated.
The problem is you have duplicated the Resources key in the template.
I recommend always using the yamllint utility on your SAM templates, because it detects YAML formatting issues that sam validate can't always detect. Here is what I got:
▶ yamllint sam-app/template.yaml
sam-app/template.yaml
...
18:1 error duplication of key "Resources" in mapping (key-duplicates)
If you then look in the packaged.yml file that is created by the sam build step, you'll notice that the API you defined will be missing. That's because it's impossible for a dict in Python to contain duplicate keys. The second Resources block you specified just overwrites the first one when the Python YAML library reads the file in.
SAM then generates the implicit API SAMnfeedS3API based on the API you specified in Events using its own generated Swagger rather than the one you (thought you) provided.
Note also that, after you fix up the duplicate key issue, you will also need to reference your API from the Events with a line like:
Events:
SAMnfeedS3API:
Type: Api
Properties:
Path: /vdp_clusters
Method: GET
RestApiId: !Ref SAMnfeedS3API ## ADD THIS LINE
See also my earlier answer here.
Related
I am trying to define an authorizer and use it in a method using Open API Specification. For some reason the authorizer does not appear in the API Gateway Console after I deploy the template. Here's my template shortened:
Type: AWS::Serverless::Api
Properties:
Name: !Sub "API Gateway"
EndpointConfiguration:
Type: REGIONAL
DefinitionBody:
openapi: 3.0.3
info:
title: 'APIs'
version: 1.0.0
paths:
/callHistoryAsync:
post:
parameters:
- name: 'xxxxx'
in: 'query'
required: true
schema:
type: 'string'
x-amazon-apigateway-integration:
type: aws
requestParameters:
integration.request.header.X-Amz-Invocation-Type: '''Event'''
integration.request.querystring.store_id: "method.request.querystring.xxxxx"
httpMethod: POST
uri: !Sub 'arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Function.Arn}/invocations'
responses:
'202':
statusCode: '202'
selectionPattern: ""
responses:
'202':
description: successfully saved
security:
- Authorizer: []
components:
securitySchemes:
Authorizer:
type: 'request'
name: 'Authorization'
in: 'header'
x-amazon-apigateway-authorizer:
authorizerUri: !FindInMap [ !Ref StageName, !Ref "AWS::Region", AuthArn ]
identitySource: 'method.request.header.X-Authorization,method.request.header.X-Date'
type: 'request'
I defined the authorizer in components under securitySchemes and I am using it in the POST method specified under security.
But the authorizer does not appear in the console and neither does it appear under the method. What am I doing wrong?
I figured this out using the export property of the stage tab in API Gateway. I used an existing API Gateway that was defined using CloudFormation and exported it using Export as Swagger + API Gateway Extensions. This basically shows you all the syntax you want.
Here's the screenshot where is this option in the console.
From that export I could see that I need to change my securitySchemes to this:
components:
securitySchemes:
Authorizer:
type: "apiKey"
name: "Unused"
in: "header"
x-amazon-apigateway-authtype: "custom"
x-amazon-apigateway-authorizer:
authorizerUri: "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:xxxxxx:function:xxxxxxx/invocations"
authorizerResultTtlInSeconds: 300
identitySource: "method.request.header.Authorization, method.request.header.Date"
type: "request"
I have been going round and round trying to get this working. I want to be able to define the CorsConfiguration in the HttpApi resource definition but everything I try simply doesn't work. I can only get CORS working if I defined it globally, but that only works if I don't define the HttpApi resource.
The following is what i have so far based on the documentation.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
Globals:
Function:
Timeout: 3
Resources:
MainApi:
Type: AWS::Serverless:HttpApi
Properties:
CorsConfiguration:
AllowHeaders:
- "*"
AllowMethods:
- "GET"
AllowOrigins:
- "http://localhost:8000"
ExposeHeaders:
- "*"
DefinitionBody:
openapi: 3.0.1
info:
title: !Ref 'AWS::StackName'
paths: {}
CheckHumanFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Architectures:
- x86_64
Events:
CheckHuman:
Type: HttpApi
Properties:
ApiId: !Ref MainApi
Path: /human-check
Method: post
Metadata:
DockerTag: nodejs16.x-v1
DockerContext: ./api/human-check
Dockerfile: Dockerfile
Outputs:
MainApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
CheckHumanFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt CheckHumanFunction.Arn
CheckHumanFunctionIamRole:
Description: "Implicit IAM Role created for CheckHuman function"
Value: !GetAtt CheckHumanFunctionIamRole.Arn
The result of this is a 403 on the OPTIONS (preflight) request.
Please can someone provide a working example? But I cannot find an actual working example anywhere and the documentation is infuriating!
Help me Stack Overflow, you're my only hope!
You should add POST and OPTIONS to AllowMethods:
CorsConfiguration
AllowMethods:
- GET
- POST
- OPTIONS
This will cover the preflight request needs.
Note a typo error in your HTTP API resource type definition (has to be AWS::Serverless::HttpApi)
This docs works.
Here is what I am using to configure CORS for my HttpApi (note: I'm using this with a Cognito Authorizer):
Resources:
ApiGatewayApi:
Type: AWS::Serverless::HttpApi
Properties:
StageName: Prod
DefaultRouteSettings:
ThrottlingBurstLimit: 5
ThrottlingRateLimit: 20
Auth:
Authorizers:
GeneralAuth:
AuthorizationScopes:
- email
IdentitySource: "$request.header.Authorization"
JwtConfiguration:
issuer: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPoolId}
audience:
- !Ref Audience
CorsConfiguration:
AllowMethods:
- GET
AllowOrigins:
- http://localhost:8080
I'm using this configuration to deploy to the 'Prod' Stage:
"ApiGatewayApi":
{
"Type": "AWS::Serverless::Api",
"Properties": {
"StageName": "Prod",
"Name" : "MainGateway",
...
I want to deploy different code to the 'Stage' stage.
I tried to change 'StageName' to "Stage" but I get this error:
"Stage already exists".
How do I deploy different code to different stages?
This solution is based on YAML format same can used in JSON format also.
There is a bug in SAM whenever you creating StageName its creating default Stage along with stage name which you provided like Prod. First you delete your current one then you can applied this changes.
To solve this issue there is two ways by adding OpenApiVersion: '2.0' in your YAML file :
Approach 1: Under properties following to StageName can add this. This properties can be added for AWS::Serverless::Api or other resources like AWS::Serverless::Lambda.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS SAM template with a simple API definition
Resources:
ApiGatewayApi:
Type: AWS::Serverless::Api
Properties:
StageName: 'V1'
OpenApiVersion: '2.0'
ApiFunction: # Adds a GET api endpoint at "/" to the ApiGatewayApi via an Api event
Type: AWS::Serverless::Function
Properties:
Events:
ApiEvent:
Type: Api
Properties:
Path: /
Method: get
RestApiId:
Ref: ApiGatewayApi
Runtime: python3.7
Handler: index.handler
InlineCode: |
def handler(event, context):
return {'body': 'Hello World!', 'statusCode': 200}
Approach 2: The following to your SAM template at the top level AND be sure you have defined a stage using "StageName" on your AWS::Serverless:Api resource. This will global level if you multiple resource like API or lambda etc.
Globals:
Api:
OpenApiVersion: 3.0.1
Cors: '*'
Resources:
ImplicitApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/member_portal.zip
Handler: index.gethtml
Runtime: nodejs12.x
Events:
GetHtml:
Type: Api
Properties:
Path: /
Method: get
ExplicitApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Note: This solutions works ONLY when one creates API from scratch. If an API was created before, and user adds OpenApiVersion: '2.0' to it, it doesn't remove "Stage" stage. It needs to be added from the beginning.
AWS::Serverless::Api is a very simple implementation and is not capable of managing multi stage under SAM, better use AWS::ApiGateway::RestApi and multiple AWS::ApiGateway::Stage referring to RestApi resource.
Reference :
https://github.com/aws/serverless-application-model/blob/master/tests/translator/input/api_with_open_api_version.yaml#L3
https://github.com/aws/serverless-application-model/issues/191#issuecomment-551051431
I have a working lambda listening to HTTP API gateway with path /medication/{proxy+}. This workflow is working fine and is responding with expected result when {baseurl}/{basepath}/medication/{proxy+} is invoked. But in order to version the APIs, I decided to add a root path to this => /v1/medication/{proxy+}.
But on invoking {baseurl}/{basepath}/v1/medication/{proxy+} I am getting internal server error, with the same code(lambda) which was used before.
I have deployed both lambda and API gateway using sam/cloudformation template.
Below is the stripped-down template and swagger definitions:
swagger.yml
v1/medication/{proxy+}:
x-amazon-apigateway-any-method:
tags:
- "health"
summary: "Get medication details."
description: "Endpoint for fetching medication details"
operationId: "get allergy"
x-amazon-apigateway-integration:
uri: 'arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:{AWS:Region}:function:ucm-dfd-dev-DFDNotification/invocations'
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
security:
- dfd_authorizer: []
template.yml
DFDAllergy:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-DFDAllergy'
Handler: controllers/allergy.handler
Runtime: nodejs12.x
CodeUri: src
Layers:
- !Ref DFDPackageLayer
Role: 'arn:aws:iam::{AWS:Region}:role/Lambda_Access_Role-Dev'
Events:
samapinew:
Type: Api
Properties:
RestApiId: !Ref DFDApi
Path: /v1/medication/{proxy+}
Method: ANY
Here the path in template.yml and endpoint in swagger.yml are only changes made to incorporate v1 as root path.
Cant seem to find out what I'm missing. Any help is appreciated. thanks in advance.
I am fairly new to AWS SAM. I have implemented an API Gateway using AWS web console and specified the body validation in the API method request, I want to achieve the same thing but using SAM template. My search for how to specify Method Request in API Gateway SAM template gave nothing related to this. Any Help please?
Complete guide to add validations on API gateway here.
Below is a code snippet from SAM template.yaml to add body validations on a sample API. Here a PingInput API model is defined onthe /ping path on the API. The Model contains one required and one optional parameter. Calling the API without the required parameter will fail.
AuthApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
swagger: '2.0'
info:
version: '1.0'
title: 'AwsRestServiceTemplate'
paths:
/ping:
x-amazon-apigateway-any-method:
responses: {}
post:
x-amazon-apigateway-request-validator: 'Validate body' # To specify that the Http Post body needs to be validated
parameters:
- in: 'body'
name: 'PingInput'
required: true
schema:
$ref: '#/definitions/PingInput'
x-amazon-apigateway-integration:
httpMethod: post
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PingFunction.Arn}/invocations
x-amazon-apigateway-request-validators:
Validate body:
validateRequestParameters: true
validateRequestBody: true
definitions:
PingInput:
type: 'object'
required:
- 'requiredKey'
properties:
requiredKey:
type: 'string'
optionalKey:
type: 'string'
If you are looking to configure `authorization", here is the SAM template.
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
DefaultAuthorizer: AWS_IAM
MyFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: MyFunction
...
Events:
Post:
Type: Api
Properties:
Path: /compute
Method: POST
RestApiId: !Ref MyApi
Auth:
Authorizer: AWS_IAM
You have to define the authorizer inside AWS::Serverless::Api definition and use it inside the function.
References:
https://github.com/awslabs/serverless-application-model/blob/master/examples/2016-10-31/api_aws_iam_auth/template.yaml
https://github.com/awslabs/serverless-application-model/blob/master/examples/2016-10-31/api_cognito_auth/template.yaml