kakakakakku blog

Weekly Tech Blog: Keep on Learning!

AWS SAM で Amazon API Gateway の Usage Plan を作るときに API Stage not found と出たら

AWS SAM で Amazon API Gateway (REST) の API Key と Usage Plan を構築するとき,実行時に以下のように API Stage not found というエラーが出ることがある.これはよくハマる問題で,AWS SAM 関連の GitHub を見ると issue も出ている.今回は解決策と注意点を紹介しようと思う👌

Resource handler returned message: "API Stage not found: 22efb50tt2:v1 (Service: ApiGateway, Status Code: 404, Request ID: 2fe0c9bf-6d15-4438-b379-52c40bb66a80)" (RequestToken: d4727ca1-4d1c-6c68-0cda-478e6d499bd6, HandlerErrorCode: NotFound)

TL;DR

最初に解決策を書いておく❗️AWS SAM テンプレートの AWS::ApiGateway::UsagePlan に Amazon API Gateway Stage に対する DependsOn を追加して,Amazon API Gateway Stage の構築完了を明示的に待つ必要がある.

論理名は2種類ある!?

以下の例では DependsOn に指定している論理名は ApiGatewayv1Stage になっているけど,これは <api‑LogicalId><stage‑name>Stage という命名規則に従っている📝なお,正確には以下のように Stage 名の指定によって論理名も変わってしまうため注意する🌀

  • <api‑LogicalId><stage‑name>Stage(Stage 名をベタ書きする場合)
  • <api‑LogicalId>Stage(Stage 名を Parameters から取得する場合)
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: sandbox-api-gateway
      StageName: v1
      DefinitionUri: ./openapi/openapi.yaml

  ApiGatewayKey:
    Type: AWS::ApiGateway::ApiKey

  ApiGatewayUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref ApiGateway
          Stage: v1
    DependsOn:
      - ApiGatewayv1Stage

  ApiGatewayUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiGatewayKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiGatewayUsagePlan

検証(Stage 名をベタ書きする場合)

以下のように AWS SAM で Amazon API Gateway (REST) 関連リソースを構築する.今回 Stage 名 v1 は2箇所にベタ書きしてある.このままデプロイすると API Stage not found というエラーが出ることがある.

  • AWS::Serverless::Api
  • AWS::ApiGateway::ApiKey
  • AWS::ApiGateway::UsagePlan
  • AWS::ApiGateway::UsagePlanKey
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: sandbox-api-gateway
      StageName: v1
      DefinitionUri: ./openapi/openapi.yaml

  ApiGatewayKey:
    Type: AWS::ApiGateway::ApiKey

  ApiGatewayUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref ApiGateway
          Stage: v1

  ApiGatewayUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiGatewayKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiGatewayUsagePlan

実は AWS SAM で AWS::Serverless::Api を使って Amazon API Gateway (REST) を構築しようとすると,内部的に AWS::ApiGateway::StageAWS::ApiGateway::Deployment も構築される.ブラックボックスな仕様ではなく,ちゃんと以下のドキュメントに載っている.

docs.aws.amazon.com

そして,AWS::ApiGateway::UsagePlan を構築するときに,依存する AWS::ApiGateway::Stage の構築が間に合わず API Stage not found というエラーが出てしまう.よって,内部的に構築される AWS::ApiGateway::Stage の論理名の命名規則に従って <api‑LogicalId><stage‑name>Stage に対する DependsOn を追加する必要がある.以下の AWS SAM テンプレートは期待した通りに構築できる❗️

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: sandbox-api-gateway
      StageName: v1
      DefinitionUri: ./openapi/openapi.yaml

  ApiGatewayKey:
    Type: AWS::ApiGateway::ApiKey

  ApiGatewayUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref ApiGateway
          Stage: v1
    DependsOn:
      - ApiGatewayv1Stage

  ApiGatewayUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiGatewayKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiGatewayUsagePlan

実際に aws cloudformation describe-stack-resources コマンドで Amazon API Gateway Stage を確認すると,確かに論理名 ApiGatewayv1Stage で構築されている🙂

$ aws cloudformation describe-stack-resources --stack-name kakakakakku-blog-apigw-apikey | jq '.StackResources[] | select(.ResourceType == "AWS::ApiGateway::Stage")'
{
  "StackName": "kakakakakku-blog-apigw-apikey",
  "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/kakakakakku-blog-apigw-apikey/082450d0-de49-11ed-a0dc-0acc4753a47f",
  "LogicalResourceId": "ApiGatewayv1Stage",
  "PhysicalResourceId": "v1",
  "ResourceType": "AWS::ApiGateway::Stage",
  "Timestamp": "2023-04-19T00:00:00.000000+00:00",
  "ResourceStatus": "CREATE_COMPLETE",
  "DriftInformation": {
    "StackResourceDriftStatus": "NOT_CHECKED"
  }
}

検証(Stage 名を Parameters から取得する場合)

しかし Stage 名 v1 をベタ書きするのではなく,AWS CloudFormation の Parameters から取得しようとすると,先ほど紹介した AWS::ApiGateway::Stage の論理名の命名規則が <api‑LogicalId>Stage に変わってしまうという注意点がある(ドキュメントにも書かれていないように思う...💧).よって,以下のように論理名 ApiGatewayStage に対する DependsOn を追加すると AWS SAM テンプレートは期待した通りに構築できる❗️

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Parameters:
  StageName:
    Type: String
    Default: v1

Resources:
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: sandbox-api-gateway
      StageName: !Ref StageName
      DefinitionUri: ./openapi/openapi.yaml

  ApiGatewayKey:
    Type: AWS::ApiGateway::ApiKey

  ApiGatewayUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref ApiGateway
          Stage: !Ref StageName
    DependsOn:
      - ApiGatewayStage

  ApiGatewayUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiGatewayKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiGatewayUsagePlan

もう一度 aws cloudformation describe-stack-resources コマンドで Amazon API Gateway Stage を確認すると,確かに論理名は ApiGatewayStage で構築されている🙂

$ aws cloudformation describe-stack-resources --stack-name kakakakakku-blog-apigw-apikey | jq '.StackResources[] | select(.ResourceType == "AWS::ApiGateway::Stage")'
{
  "StackName": "kakakakakku-blog-apigw-apikey",
  "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/kakakakakku-blog-apigw-apikey/26030900-de4b-11ed-ab51-0e3a83b37d3b",
  "LogicalResourceId": "ApiGatewayStage",
  "PhysicalResourceId": "v1",
  "ResourceType": "AWS::ApiGateway::Stage",
  "Timestamp": "2023-04-19T00:00:00.000000+00:00",
  "ResourceStatus": "CREATE_COMPLETE",
  "DriftInformation": {
    "StackResourceDriftStatus": "NOT_CHECKED"
  }
}

まとめ

AWS SAM で Amazon API Gateway (REST) の API Key と Usage Plan を構築するときに出る可能性のある API Stage not found というエラーに関してまとめてみた.論理名の命名規則が2種類あるところは注意❗️

参考になれば💡

github.com

おまけ

せっかく AWS SAM で AWS::Serverless::Api を使ってるなら Auth プロパティを使って API Key と Usage Plan を構築すればもっと簡単じゃん❓という話もあると思うけど,AWS::Serverless::ApiDefinitionUri で OpenAPI ファイルを指定している場合には Auth プロパティが使えないという仕様になっている💧

$ sam validate -t template.yaml
Error: [InvalidResourceException('ApiGateway', "Auth works only with inline Swagger specified in 'DefinitionBody' property.")] ('ApiGateway', "Auth works only with inline Swagger specified in 'DefinitionBody' property.")

関連 issue

github.com

github.com