api gateway + samでapiの環境を作る(api key)

やったこと
- ip制限(sam)
- api keyの設定(sam)
- route53でドメインの設定
- api gatewayでlambdaとドメインを紐づける

今回紹介する項目
- api keyの設定(sam)

#前提
AWS::Serverless::Functionでlambdaを作成済みであること。
参考：https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-function.html

作成したlambdaにapi key設定を行なっていきます。

#api keyの設定(sam)
samで以下のコードでlambdaを作成しているものとします。

```template.yml
ApiGatewayRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod

ApiLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ApiLambdaFunction
      VpcConfig: 略
      CodeUri: ./
      Handler: lambda.handler
      Runtime: nodejs12.x
      Events:
        AnyPath:
          Type: Api 
          Properties:
            Path: /{proxy+}
            Method: ANY
```

必要なことは以下の4点です。
- AWS::Serverless::FunctionにRestApiIdを入れる
- AWS::ApiGateway::ApiKeyの追加
- AWS::ApiGateway::UsagePlanの追加
- AWS::ApiGateway::UsagePlanKeyの追加

##AWS::Serverless::FunctionにRestApiIdを入れる

```
RestApiId: !Ref ApiGatewayRestApi
```

結果こうなります。

```template.yml
ApiGatewayRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod

ApiLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ApiLambdaFunction
      VpcConfig: 略
      CodeUri: ./
      Handler: lambda.handler
      Runtime: nodejs12.x
      Events:
        AnyPath:
          Type: Api 
          Properties:
            Path: /{proxy+}
            Method: ANY
            RestApiId: !Ref ApiGatewayRestApi
```

##AWS::ApiGateway::ApiKeyの追加
以下を追加します。

```
ApiKey:
    Type: AWS::ApiGateway::ApiKey
    Properties: 
      Enabled: true
      Name: !Sub 'api-key'
      StageKeys: 
        - RestApiId: !Ref ApiGatewayRestApi
          StageName: Prod
```

参考：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html


##AWS::ApiGateway::UsagePlanの追加
以下を追加します。

```
  ApiUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    DependsOn:
      - ApiKey
    Properties:
      ApiStages:
          - ApiId: !Ref ApiGatewayRestApi
            Stage: Prod
      Throttle:
        BurstLimit: 200
        RateLimit: 100
      UsagePlanName: !Sub 'api-plan'
```

これはapiのリクエスト制限をつけることができます。
設定した閾値を超えると、429 Too Many Requests エラーレスポンスを返します。

参考:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-usageplan.html
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-request-throttling.html


##AWS::ApiGateway::UsagePlanKeyの追加
以下を追加します。

```
ApiUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    DependsOn:
      - ApiUsagePlan
      - ApiLambdaFunction
    Properties : 
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiUsagePlan
```

これでapi key と usage planを紐づけます。
参考：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-usageplankey.html


##結果

```template.yml
ApiGatewayRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod

ApiLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ApiLambdaFunction
      VpcConfig: 略
      CodeUri: ./
      Handler: lambda.handler
      Runtime: nodejs12.x
      Events:
        AnyPath:
          Type: Api 
          Properties:
            Path: /{proxy+}
            Method: ANY

ApiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn:
      - ApiLambdaLogGroupSubscription
    Properties: 
      Enabled: true
      Name: !Sub 'api-key'
      StageKeys: 
        - RestApiId: !Ref ApiGatewayRestApi
          StageName: Prod

ApiUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    DependsOn:
      - ApiKey
    Properties:
      ApiStages:
          - ApiId: !Ref ApiGatewayRestApi
            Stage: Prod
      Throttle:
        BurstLimit: 200
        RateLimit: 100
      UsagePlanName: !Sub 'api-plan'

ApiUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    DependsOn:
      - ApiUsagePlan
      - ApiLambdaFunction
    Properties : 
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiUsagePlan
```

これでapi keyの設定は以上です。
sam deployするとコンソール上で確認できます。

#注意
apiを叩くときのヘッダーは `X-API-Key`になります。
これは変更できない仕様のようです。