I have created an Api usage plan and linked it to an api gateway. is it possible to create multiple usage plans and attach it to same api. if yes, will i need to create different api key for each usage plan?
UsagePlan:
Type: 'AWS::ApiGateway::UsagePlan'
Properties:
ApiStages:
- ApiId: !Ref MyRestApi
Stage: !Ref Prod
Description: Customer ABCs usage plan
Quota:
Limit: 5000
Period: MONTH
Throttle:
BurstLimit: 200
RateLimit: 100
UsagePlanName: Plan_ABC
ApiKey:
Type: 'AWS::ApiGateway::ApiKey'
Properties:
Name: TestApiKey
Description: CloudFormation API Key V1
Enabled: 'true'
UsagePlanKey:
Type: 'AWS::ApiGateway::UsagePlanKey'
Properties:
KeyId: !Ref ApiKey
KeyType: API_KEY
UsagePlanId: !Ref UsagePlan
Related
I'm deploying a serverless application using the AWS SAM Cli and template, but the API Gateway resource is returning a 403 ForbiddenException error when trying to curl / postman it. Tried looking online, but haven't been able to find any answers that solved my issue and wondering if anyone here has experienced this before.
template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs10.x
MemorySize: 256
Api:
Cors:
AllowMethods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers'"
AllowOrigin: "'*'"
Parameters:
ApiKey:
Type: String
Default: none
Conditions:
CreateApiKey: !Not [!Equals [!Ref ApiKey, 'none']]
Resources:
# DynamoDB table setup
DyanmoDBStoryTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Stories
AttributeDefinitions:
- AttributeName: short_id
AttributeType: S
KeySchema:
- AttributeName: short_id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 0
WriteCapacityUnits: 0
BillingMode: PAY_PER_REQUEST
# Log group
DynamoSaveStoryLogGroup:
Type: AWS::Logs::LogGroup
DependsOn: [DynamoSaveStoryLambda]
Properties:
RetentionInDays: 30
LogGroupName: !Sub '/aws/lambda/${DynamoSaveStoryLambda}'
DynamoGetStoryLogGroup:
Type: AWS::Logs::LogGroup
DependsOn: [DynamoGetStoryLambda]
Properties:
RetentionInDays: 30
LogGroupName: !Sub '/aws/lambda/${DynamoGetStoryLambda}'
DynamoUpdateStoryLogGroup:
Type: AWS::Logs::LogGroup
DependsOn: [DynamoUpdateStoryLambda]
Properties:
RetentionInDays: 30
LogGroupName: !Sub '/aws/lambda/${DynamoUpdateStoryLambda}'
# Lambda Fn
DynamoSaveStoryLambda:
Type: AWS::Serverless::Function
Properties:
Policies:
- AmazonDynamoDBFullAccess
Handler: src/lambdas/save-story.handler
Timeout: 10
Events:
SaveStory:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /story
Method: post
DynamoGetStoryLambda:
Type: AWS::Serverless::Function
Properties:
Policies:
- AmazonDynamoDBFullAccess
Handler: src/lambdas/get-story.handler
Timeout: 10
Events:
SaveStory:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /story/{shortId}
Method: get
DynamoUpdateStoryLambda:
Type: AWS::Serverless::Function
Properties:
Policies:
- AmazonDynamoDBFullAccess
Handler: src/lambdas/update-story.handler
Timeout: 10
Events:
SaveStory:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /story/{shortId}
Method: post
# Custom API gateway setup API Keys & usage plans
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
ApiKeyRequired: true
UsagePlan:
Type: AWS::ApiGateway::UsagePlan
DependsOn: [ApiGatewayProdStage]
Condition: CreateApiKey
Properties:
ApiStages:
- ApiId: !Ref ApiGateway
Stage: Prod
DynamoLambdasApiKey:
Type: AWS::ApiGateway::ApiKey
DependsOn: [UsagePlan]
Condition: CreateApiKey
Properties:
Value: !Ref ApiKey
Enabled: true
StageKeys:
- RestApiId: !Ref ApiGateway
StageName: Prod
UsagePlanKey:
Type: AWS::ApiGateway::UsagePlanKey
Condition: CreateApiKey
Properties:
KeyId: !Ref DynamoLambdasApiKey
KeyType: API_KEY
UsagePlanId: !Ref UsagePlan
Outputs:
StoryApi:
Description: Serverless api url generated by AWS Cloudformation upon stack deployment
Value: !Sub 'https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/prod'
ApiKey:
Description: Api key to authorize access in API Gateway
Value: !Ref ApiKey
SAM CLI Version: 0.47.0
Error:
Date →Sun, 26 Apr 2020 19:22:02 GMT
Content-Type →application/json
Content-Length →23
Connection →keep-alive
x-amzn-RequestId →01d6b9ec-dcf0-484c-be07-6b629437b305
x-amzn-ErrorType →ForbiddenException
x-amz-apigw-id →Lm_WOF9ZvHcF7nQ=
Testing it directly from the AWS Lambda console works correctly and cloudwatch logs are generated, but not when I curl/postman the request with the API url that is generated during deployment. I have tried the following:
Ensuring the x-api-key header is set correctly and verifying that the API Gateway in AWS console is set with the correct API Key
Configuring CORS in the API in globals of template. Confirming It creates the options endpoints in API Gateway console
Double checking the endpoints are correct
The error states that it is a cloudfront issue so I've confirmed that the S3 bucket has public access. There are no other cloudfront resources in the AWS console. I'm at a loss as to what is blocking the request.
Answer was simpler than I thought, but for anyone else that comes across this issue, the query parameter is case sensitive. The output url from the serverless application model deployment returns https://${serverlessAppId}.execute-api.${region}.amazonaws.com/${StageName}.
In my case, the StageName was Prod and I was making requests as prod
What is working:
Using the serverless framework:
I have configured an AWS VPC
I have an Amazon Aurora database configured
for my VPC
I have an AWS API Gateway lambda that is configured for my
VPC
When I deploy my lambda, I am able to access it publicly via the AWS
generated URL: XXX.execute-api.us-east-1.amazonaws.com/prod/outages
In my Lambda,I run a very simple query that proves I can connect to
my database.
This all works fine.
What is NOT working:
I have registered a domain with AWS/Route 53 and added a cert (e.g. *.foo.com)
I use the serverless-domain-manager plugin to make my lambda available via my domain (e.g. api.foo.com/outages resolves to XXX.execute-api.us-east-1.amazonaws.com/prod/outages)
This works fine if my lambda is NOT configured for my VPC
But when my lambda IS configured for my VPC, the custom domain api.foo.com/outages does NOT resolve to XXX.execute-api.us-east-1.amazonaws.com/prod/outages
I other words: I can NOT access api.foo.com/outages publicly.
What I need is:
1 - XXX.execute-api.us-east-1.amazonaws.com/prod/outages is available publicly (this works)
2 - My custom domain, api.foo.com/outages points to the SAME lambda as XXX.execute-api.us-east-1.amazonaws.com/prod/outages (in my VPC) and is available publicly (not working. I get: {"message":"Forbidden"})
virtual-private-cloud.yml
service: virtual-private-cloud
provider:
name: aws
region: us-east-1
stage: ${opt:stage, dev}
custom:
appVersion: 0.0.0
VPC_CIDR: 10
resources:
Resources:
ServerlessVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: ${self:custom.VPC_CIDR}.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
ServerlessSubnetA:
DependsOn: ServerlessVPC
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: ServerlessVPC
AvailabilityZone: ${self:provider.region}a
CidrBlock: ${self:custom.VPC_CIDR}.0.0.0/24
ServerlessSubnetB:
DependsOn: ServerlessVPC
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: ServerlessVPC
AvailabilityZone: ${self:provider.region}b
CidrBlock: ${self:custom.VPC_CIDR}.0.1.0/24
ServerlessSubnetC:
DependsOn: ServerlessVPC
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: ServerlessVPC
AvailabilityZone: ${self:provider.region}c
CidrBlock: ${self:custom.VPC_CIDR}.0.2.0/24
Outputs:
VPCDefaultSecurityGroup:
Value:
Fn::GetAtt:
- ServerlessVPC
- DefaultSecurityGroup
Export:
Name: VPCDefaultSecurityGroup-${self:provider.stage}
SubnetA:
Description: 'Subnet A.'
Value: !Ref ServerlessSubnetA
Export:
Name: vpc-subnet-A-${self:provider.stage}
SubnetB:
Description: 'Subnet B.'
Value: !Ref ServerlessSubnetB
Export:
Name: vpc-subnet-B-${self:provider.stage}
SubnetC:
Description: 'Subnet C.'
Value: !Ref ServerlessSubnetC
Export:
Name: vpc-subnet-C-${self:provider.stage}
database-service.yml
service: database-service
provider:
name: aws
region: us-east-1
stage: ${opt:stage, dev}
environment:
stage: ${opt:stage, dev}
plugins:
- serverless-plugin-ifelse
custom:
appVersion: 0.0.1
AURORA:
DB_NAME: database${self:provider.stage}
USERNAME: ${ssm:/my-db-username~true}
PASSWORD: ${ssm:/my-db-password~true}
HOST:
Fn::GetAtt: [AuroraRDSCluster, Endpoint.Address]
PORT:
Fn::GetAtt: [AuroraRDSCluster, Endpoint.Port]
serverlessIfElse:
- If: '"${opt:stage}" == "prod"'
Set:
resources.Resources.AuroraRDSCluster.Properties.EngineMode: provisioned
ElseSet:
resources.Resources.AuroraRDSCluster.Properties.EngineMode: serverless
resources.Resources.AuroraRDSCluster.Properties.ScalingConfiguration.MinCapacity: 1
resources.Resources.AuroraRDSCluster.Properties.ScalingConfiguration.MaxCapacity: 4
ElseExclude:
- resources.Resources.AuroraRDSInstanceParameter
- resources.Resources.AuroraRDSInstance
resources:
Resources:
AuroraSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "Aurora Subnet Group"
SubnetIds:
- 'Fn::ImportValue': vpc-subnet-A-${self:provider.stage}
- 'Fn::ImportValue': vpc-subnet-B-${self:provider.stage}
- 'Fn::ImportValue': vpc-subnet-C-${self:provider.stage}
AuroraRDSClusterParameter:
Type: AWS::RDS::DBClusterParameterGroup
Properties:
Description: Parameter group for the Serverless Aurora RDS DB.
Family: aurora5.6
Parameters:
character_set_database: "utf32"
AuroraRDSCluster:
Type: "AWS::RDS::DBCluster"
Properties:
MasterUsername: ${self:custom.AURORA.USERNAME}
MasterUserPassword: ${self:custom.AURORA.PASSWORD}
DBSubnetGroupName:
Ref: AuroraSubnetGroup
Engine: aurora
EngineVersion: "5.6.10a"
DatabaseName: ${self:custom.AURORA.DB_NAME}
BackupRetentionPeriod: 3
DBClusterParameterGroupName:
Ref: AuroraRDSClusterParameter
VpcSecurityGroupIds:
- 'Fn::ImportValue': VPCDefaultSecurityGroup-${self:provider.stage}
# this is needed for non-serverless mode
AuroraRDSInstanceParameter:
Type: AWS::RDS::DBParameterGroup
Properties:
Description: Parameter group for the Serverless Aurora RDS DB.
Family: aurora5.6
Parameters:
sql_mode: IGNORE_SPACE
max_connections: 100
wait_timeout: 900
interactive_timeout: 900
# this is needed for non-serverless mode
AuroraRDSInstance:
Type: "AWS::RDS::DBInstance"
Properties:
DBInstanceClass: db.t2.small
DBSubnetGroupName:
Ref: AuroraSubnetGroup
Engine: aurora
EngineVersion: "5.6.10a"
PubliclyAccessible: false
DBParameterGroupName:
Ref: AuroraRDSInstanceParameter
DBClusterIdentifier:
Ref: AuroraRDSCluster
Outputs:
DatabaseName:
Description: 'Database name.'
Value: ${self:custom.AURORA.DB_NAME}
Export:
Name: DatabaseName-${self:provider.stage}
DatabaseHost:
Description: 'Database host.'
Value: ${self:custom.AURORA.HOST}
Export:
Name: DatabaseHost-${self:provider.stage}
DatabasePort:
Description: 'Database port.'
Value: ${self:custom.AURORA.PORT}
Export:
Name: DatabasePort-${self:provider.stage}
outage-service.yml
service: outage-service
package:
individually: true
plugins:
- serverless-bundle
- serverless-plugin-ifelse
- serverless-domain-manager
custom:
appVersion: 0.0.12
stage: ${opt:stage}
domains:
prod: api.foo.com
test: test-api.foo.com
dev: dev-api.foo.com
customDomain:
domainName: ${self:custom.domains.${opt:stage}}
stage: ${opt:stage}
basePath: outages
custom.customDomain.certificateName: "*.foo.com"
custom.customDomain.certificateArn: 'arn:aws:acm:us-east-1:395671985612:certificate/XXXX'
createRoute53Record: true
serverlessIfElse:
- If: '"${opt:stage}" == "prod"'
Set:
custom.customDomain.enabled: true
ElseSet:
custom.customDomain.enabled: false
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage}
region: us-east-1
environment:
databaseName: !ImportValue DatabaseName-${self:provider.stage}
databaseUsername: ${ssm:/my-db-username~true}
databasePassword: ${ssm:/my-db-password~true}
databaseHost: !ImportValue DatabaseHost-${self:provider.stage}
databasePort: !ImportValue DatabasePort-${self:provider.stage}
functions:
hello:
memorySize: 2048
timeout: 30
handler: functions/handler.hello
vpc:
securityGroupIds:
- 'Fn::ImportValue': VPCDefaultSecurityGroup-${self:provider.stage}
subnetIds:
- 'Fn::ImportValue': vpc-subnet-A-${self:provider.stage}
- 'Fn::ImportValue': vpc-subnet-B-${self:provider.stage}
- 'Fn::ImportValue': vpc-subnet-C-${self:provider.stage}
environment:
functionName: getTowns
events:
- http:
path: outage
method: get
cors:
origin: '*'
headers:
- Content-Type
- authorization
resources:
- Outputs:
ApiGatewayRestApiId:
Value:
Ref: ApiGatewayRestApi
Export:
Name: ${self:custom.stage}-ApiGatewayRestApiId
ApiGatewayRestApiRootResourceId:
Value:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
Export:
Name: ${self:custom.stage}-ApiGatewayRestApiRootResourceId
I believe you might have missed to add VPC endpoints for API Gateway.
Ensure you create a VPC interface endpoint for API GW and use it in the API you create. This will allow API requests to lambda running within VPC
References:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-apis.html#apigateway-private-api-create-interface-vpc-endpoint
https://aws.amazon.com/blogs/compute/introducing-amazon-api-gateway-private-endpoints/
Hope this helps !!
SOLUTION: We found a solution, in our case the solution is that you can't use wildcard certificates. So if you make a certificate for the exact API URL it does work. Obviously this is not a nice solution for everyone and everything but when you do have this problem and must deploy you can make it work this way.
Also maybe good to mention we did not have any additional problems with the below mentioned case:
I also wanted to add a post I found that might also have lead to this setup not working even if we do get the network to play nice. But again if you or anyone has a working setup like the above mentioned one let me know how you did it.
TLDR
If anyone wants to understand what was going on with API Gateway, take
a look at this thread.
It basically says that API Gateway processes regular URLs (like
aaaaaaaaaaaa.execute-api.us-east-1.amazonaws.com) differently than how
it processes Custom Domain Name URLs (like api.myservice.com). So when
API Gateway forwards your API request to your Lambda Function, your
Lambda Function will receive different path values, depending on which
type of your URL you used to invoke your API.
source:
Understanding AWS API Gateway Custom Domain Names
I am learning AWS SAM and having trouble finding information on how to create an API Key and usage plan through the SAM template.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
GetFamilyByIdFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs8.10
Handler: get-family-by-id.handler
CodeUri: get-family-by-id/
Timeout: 300
Events:
GetFamilyByIdApi:
Type: Api
Properties:
Path: "/family/{id}"
Method: get
I would like to create an API key and associate it with a usage plan for the 'GetFamilyByIdApi' specified above. Any help would be appreciated.
After a bit of digging I figured it out. This solution is when you want to define the value of the api key yourself as opposed to letting API Gateway generate a value. I assume a variation exists for that. Note that 'HvgnApi' refers to my Swagger definition (Type: AWS::Serverless::Api). Enjoy!
ApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Name: !Join ["", [{"Ref": "AWS::StackName"}, "-apikey"]]
Description: "CloudFormation API Key V1"
Enabled: true
GenerateDistinctId: false
Value: abcdefg123456
StageKeys:
- RestApiId: !Ref HvgnApi
StageName: prod
ApiUsagePlan:
Type: "AWS::ApiGateway::UsagePlan"
Properties:
ApiStages:
- ApiId: !Ref HvgnApi
Stage: prod
Description: !Join [" ", [{"Ref": "AWS::StackName"}, "usage plan"]]
Quota:
Limit: 1000
Period: MONTH
UsagePlanName: !Join ["", [{"Ref": "AWS::StackName"}, "-usage-plan"]]
ApiUsagePlanKey:
Type: "AWS::ApiGateway::UsagePlanKey"
DependsOn:
- HvgnApi
Properties:
KeyId: !Ref ApiKey
KeyType: API_KEY
UsagePlanId: !Ref ApiUsagePlan
This is much easier to do with the latest version of SAM:
MyAPI:
Type: AWS::Serverless::Api
Properties:
StageName: dev
Auth:
ApiKeyRequired: true # for all methods
UsagePlan:
CreateUsagePlan: PER_API
Description: Usage plan for this API
I have the following Cloudformation template I am trying to deploy via SAM. This template correctly creates the DynamoDB table, an API Key, a Lambda function and the API Gateway, but I cannot figure out what I need to specify in the template to associate the API KEY with the API Gateway.
I have found plenty of snippets showing partial examples, but I am struggling to piece it all together.
Thank you in advance,
Denny
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Parameters:
TableName:
Type: String
Default: 'influencetabletest'
Description: (Required) The name of the new DynamoDB table Minimum 3 characters
MinLength: 3
MaxLength: 50
AllowedPattern: ^[A-Za-z-]+$
ConstraintDescription: 'Required parameter. Must be characters only. No numbers allowed.'
CorsOrigin:
Type: String
Default: '*'
Description: (Optional) Cross-origin resource sharing (CORS) Origin. You can specify a single origin, all "*" or leave empty and no CORS will be applied.
MaxLength: 250
Conditions:
IsCorsDefined: !Not [!Equals [!Ref CorsOrigin, '']]
Resources:
ApiKey:
Type: AWS::ApiGateway::ApiKey
DependsOn:
- ApiGetter
Properties:
Name: "TestApiKey"
Description: "CloudFormation API Key V1"
Enabled: "true"
ApiGetter:
Type: AWS::Serverless::Api
Properties:
StageName: prd
DefinitionBody:
swagger: 2.0
info:
title:
Ref: AWS::StackName
paths:
/getdynamicprice:
post:
responses: {}
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaGetter.Arn}/invocations
LambdaGetter:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./index.js
Handler: index.handler
Runtime: nodejs8.10
Environment:
Variables:
TABLE_NAME: !Ref TableName
IS_CORS: IsCorsDefined
CORS_ORIGIN: !Ref CorsOrigin
PRIMARY_KEY: !Sub ${TableName}Id
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref TableName
Events:
Api:
Type: Api
Properties:
Path: /getdynamicprice
Method: POST
RestApiId: !Ref ApiGetter
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref TableName
AttributeDefinitions:
-
AttributeName: !Sub "${TableName}Id"
AttributeType: "S"
KeySchema:
-
AttributeName: !Sub "${TableName}Id"
KeyType: "HASH"
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
Outputs:
ApiKeyID:
Value: !Ref ApiKey
ApiUrl:
Value: !Sub https://${ApiGetter}.execute-api.${AWS::Region}.amazonaws.com/prod/getdynamicprice
Description: The URL of the API Gateway you invoke to get your dynamic pricing result.
DynamoDBTableArn:
Value: !GetAtt DynamoDBTable.Arn
Description: The ARN of your DynamoDB Table
DynamoDBTableStreamArn:
Value: !GetAtt DynamoDBTable.StreamArn
Description: The ARN of your DynamoDB Table Stream
Edit (04/22/2020): there now seems to do all this using AWS SAM. Please see answer below
Here's a sample template where I have connected my API to a API key. But that's only been possible because I am using usage plans. I believe that is the primary purpose of an API key. API gateway usage plan
ApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Name: !Join ["", [{"Ref": "AWS::StackName"}, "-apikey"]]
Description: "CloudFormation API Key V1"
Enabled: true
GenerateDistinctId: false
ApiUsagePlan:
Type: "AWS::ApiGateway::UsagePlan"
Properties:
ApiStages:
- ApiId: !Ref <API resource name>
Stage: !Ref <stage resource name>
Description: !Join [" ", [{"Ref": "AWS::StackName"}, "usage plan"]]
Quota:
Limit: 2000
Period: MONTH
Throttle:
BurstLimit: 10
RateLimit: 10
UsagePlanName: !Join ["", [{"Ref": "AWS::StackName"}, "-usage-plan"]]
ApiUsagePlanKey:
Type: "AWS::ApiGateway::UsagePlanKey"
Properties:
KeyId: !Ref <API key>
KeyType: API_KEY
UsagePlanId: !Ref ApiUsagePlan
There does not seem to be a way to do this without a usage plan.
I did try the suggestion from ASR but ended up with a simpler approach.
The AWS SAM (Serverless Application Model) contains prepackaged handling that doesn't necessitate the use of resources of the ApiGateway type.
To create an API Gateway with a stage that requires an authorization token in the header the following simplified code should do it for you :
Resources:
ApiGatewayEndpoint:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
ApiKeyRequired: true
UsagePlan:
CreateUsagePlan: PER_API
UsagePlanName: GatewayAuthorization [any name you see fit]
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda.handler
Runtime: python3.7
Timeout: 30
CodeUri: .
Events:
PostEvent:
Type: Api
Properties:
Path: /content
Method: POST
RequestParameters:
- method.request.header.Authorization:
Required: true
Caching: true
RestApiId:
Ref: ApiGatewayEndpoint [The logical name of your gateway endpoint above]
The elements:
Auth:
ApiKeyRequired: true
UsagePlan:
CreateUsagePlan: PER_API
is what does the trick.
Cloudformation handles the plumbing for you, ie. the Api Key, UsagePlan and UsagePlanKey is automatically created and binded.
Although the docs are definitely not best in class they do provide some additional information: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-resources-and-properties.html
just like in the title. I can deploy stuff on AWS using only Cloud Formation. Now I try to secure my API Gateway with API Keys and looks like I need a Usage Plan for it. It doesn't seem to be covered by the documentation right here: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html
Have any of you had a similar problem and if yes, how did you solved it?
AWS has today released the ability to create AWS::ApiGateway::UsagePlan using cloud formation templates
Now AWS::ApiGateway::UsagePlanKey can be created using CloudFormation.
This snippet demonstrates how you might use a UsagePlan and UsagePlanKey, along with an APIKey in a CloudFormation Template
UsagePlan:
Type: 'AWS::ApiGateway::UsagePlan'
Properties:
ApiStages:
- ApiId: !Ref MyRestApi
Stage: !Ref Prod
Description: Customer ABCs usage plan
Quota:
Limit: 5000
Period: MONTH
Throttle:
BurstLimit: 200
RateLimit: 100
UsagePlanName: Plan_ABC
ApiKey:
Type: 'AWS::ApiGateway::ApiKey'
Properties:
Name: TestApiKey
Description: CloudFormation API Key V1
Enabled: 'true'
UsagePlanKey:
Type: 'AWS::ApiGateway::UsagePlanKey'
Properties:
KeyId: !Ref ApiKey
KeyType: API_KEY
UsagePlanId: !Ref UsagePlan
For anyone reading, this is now supported via the AWS::ApiGateway::UsagePlanKey (docs) resource type in CloudFormation. From that page:
The AWS::ApiGateway::UsagePlanKey resource associates an Amazon API Gateway API key with an API Gateway usage plan. This association determines which users the usage plan is applied to.
AWS has provided the CloudFormation template for the API Keys creation with the UsagePlan and the UsagePlan Keys as well, so probably the CFT for the same would be defined as:
ApiKey:
Type: 'AWS::ApiGateway::ApiKey'
DependsOn:
- ApiGatewayDeployment
Properties:
Name: !Sub "you keyName-Apikeys"
Description: Api Keys Description
Enabled: 'true'
StageKeys:
- RestApiId: !Ref ApiGatewayRestApi
StageName: !Sub "your stageName"
usagePlan:
Type: 'AWS::ApiGateway::UsagePlan'
DependsOn:
- ApiGatewayDeployment
Properties:
ApiStages:
- ApiId: !Ref ApiGatewayRestApi
Stage: !Sub "your stageName"
Description: your description of usage plan
Quota:
Limit: 50000
Period: DAY
Throttle:
BurstLimit: 200
RateLimit: 100
UsagePlanName: !Sub "define your name of UsagePlan"
usagePlanKey:
Type: 'AWS::ApiGateway::UsagePlanKey'
DependsOn:
- ApiGatewayDeployment
Properties:
KeyId: !Ref ApiKey
KeyType: API_KEY
UsagePlanId: !Ref usagePlan
I hope this will probably help you.