Search code examples
amazon-web-servicesaws-api-gatewayaws-cloudformationapi-key

AWS Cloudformation Link API Key to API Gateway


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

Solution

  • 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.