4

I used sam cli , to create a project. when I package this and deploy , it creates the lambda and also the api gateway with stage and prod stages, policy, roles e.t.c by default, without having to explicitly define in the cloudformation template ( see code below) . as it generates the api gateway automatically, how do i add/attach say if i wanted to add a api key or some kind of authorization for my api generated by template below?

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  simple-node-api
  Sample SAM Template for simple-node-api

Globals:
  Function:
    Timeout: 3

Resources:
 ServerlessHttpApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ApiKeyRequired: true # sets for all methods
      DefinitionBody:
        swagger:2.0
        paths:
          "/myresource":
              post:
                 x-amazon-apigateway-integration
                    httpMethod: post
                    type: aws_proxy
                    uri: ...

 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 ServerlessHttpApi
          StageName: Prod

  ApiUsagePlan:
    Type: "AWS::ApiGateway::UsagePlan"
    Properties:
      ApiStages: 
        - ApiId: !Ref ServerlessHttpApi
          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: 
      - ServerlessHttpApi
    Properties:
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiUsagePlan

  HelloWorldfunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: python3.7
      Events:
        HelloWorld:
          Type: Api
          Properties:
            RestApiId: !Ref ServerlessHttpApi
            Path: /hello
            Method: get

Outputs:
  ServerlessHttpApi:
    Description: API Gateway endpoint URL for Prod stage for Hello World function
    Value:
      Fn::Sub: https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldfunction:
    Description: Express Backend Lambda Function ARN
    Value: !Sub HelloWorldfunction.Arn
  HelloWorldFunctionIamRole:
    Description: Implicit IAM Role created for Hello World function
    Value: !Sub HelloWorldFunctionRole.Arn
ozil
  • 599
  • 7
  • 31
  • 1
    Does this answer your question? [Create an API Key and Usage Plan with SAM](https://stackoverflow.com/questions/54973513/create-an-api-key-and-usage-plan-with-sam) – petey Aug 25 '20 at 15:39

2 Answers2

7

I modified your code to use the API keys as shown here.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  simple-node-api
  Sample SAM Template for simple-node-api

Globals:
  Function:
    Timeout: 3

Resources:

  ServerlessHttpApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ApiKeyRequired: true # sets for all methods

  ApiKey: 
    Type: AWS::ApiGateway::ApiKey
    DependsOn: [ApiUsagePlan]
    Properties: 
      Name: !Join ["", [{"Ref": "AWS::StackName"}, "-apikey"]]
      Description: "CloudFormation API Key V1"
      Enabled: true
      GenerateDistinctId: false
      Value: abcdefg123456665ffghsdghfgdhfgdh4565
      StageKeys:
        - RestApiId: !Ref ServerlessHttpApi
          StageName: Prod

  ApiUsagePlan:
    Type: "AWS::ApiGateway::UsagePlan"
    DependsOn:
      - ServerlessHttpApiProdStage
    Properties:
      ApiStages: 
        - ApiId: !Ref ServerlessHttpApi
          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: 
      - ServerlessHttpApi
    Properties:
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiUsagePlan

  HelloWorldfunction:
    Type: AWS::Serverless::Function
    Properties:
      #CodeUri: hello-world/
      CodeUri: ./
      Handler: app.lambdaHandler
      Runtime: python3.7
      Events:
        HelloWorld:
          Type: Api
          Properties:
            RestApiId: !Ref ServerlessHttpApi
            Path: /hello
            Method: get

Outputs:
  ServerlessHttpApi:
    Description: API Gateway endpoint URL for Prod stage for Hello World function
    Value:
      Fn::Sub: https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldfunction:
    Description: Express Backend Lambda Function ARN
    Value: !Sub HelloWorldfunction.Arn
  HelloWorldFunctionIamRole:
    Description: Implicit IAM Role created for Hello World function
    Value: !Sub HelloWorldFunctionRole.Arn

I commented out some parts so that I can run the code, and I can confirm that it deploys and the API auth is set and API key present:

enter image description here

enter image description here

Marcin
  • 215,873
  • 14
  • 235
  • 294
  • thanks. I updated my code. I added the three resources, Api key, Usageplan and ApiUsagePlanKey, to tie things together. after adding those , i got an error from cloudformation, when creating ApiUsagePlan and Apikey - "invalid stage identifier specified ( service AmazonApiGateway: status : 404: error code : not found exception .... I'm not sure how do i reference the stage name correctly. – ozil Sep 03 '20 at 02:54
  • 1
    @ozil I fixed it. Two DependsOn were missing and apikey was too short. Like before, I modified `CodeUri: hello-world/` as otherwise I can't run the deployment. – Marcin Sep 03 '20 at 03:33
  • you rock. this works. I do have one follow up question if you don't mind. I'm trying to create on post method on the api, with cors enabled and also with the proxy integration. I have updated my question by adding a "DefinitionBody" properties under ServerlessHttpApi resource. could you check to see if i'm in a right direction. – ozil Sep 03 '20 at 13:48
  • 1
    @ozil I'm not too familiar with swagger, thus its difficult to help with it. You could consider making new question specific to the swagger. This way others could try to help as well. – Marcin Sep 03 '20 at 22:14
  • @Marcin- thanks again. I did manage to get it working with some hit and trial methods. and also opened up a new question. the only thing now, i'm trying to figure out is , if there is way to require api key on the method level. it doesn't have to be swagger definitions. all I want to find out is , in the work we have done so far , if we can simply define a post method with api required and another options method to allow CORS. I appreciate all your help. – ozil Sep 04 '20 at 01:20
  • 1
    @ozil I think so it should be possible. I see in the [docs](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-setup-api-key-with-console.html) that you can set api key requirement on the method level. Not sure how exactly to modify the existing answer, but I think this would be good issue for a new question. – Marcin Sep 04 '20 at 01:44
  • 1
    I will create another question – ozil Sep 04 '20 at 01:49
1

You have to mention it in your AWS SAM template. Below is an example:

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ApiKeyRequired: true # sets for all methods

  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Handler: index.handler
      Runtime: nodejs12.x
      Events:
        ApiKey:
          Type: Api
          Properties:
            RestApiId: !Ref MyApi
            Path: /
            Method: get
            Auth:
              ApiKeyRequired: true

You can read more about it here

CK__
  • 1,252
  • 1
  • 11
  • 25