AWS SAMでAPIキーと使用量プランのリソース作成に失敗する場合の対処方法

# エラーの内容
AWS SAM で AWS::Serverless::Api リソースと AWS::ApiGateway::ApiKey および AWS::ApiGateway::UsagePlan
リソースを同じテンプレート内で指定し、デプロイすると以下のエラーで作成に失敗しました。
以下はCloudFormationのコンソールに出力されるエラーメッセージです。

```
Invalid stage identifier specified              # API キーの CREATE_FAILED
API Stage not found: <APIの物理ID>:<ステージ名>   # 使用量プランの CREATE_FAILED
```

原因としては、AWS::Serverless::Api リソースで APIステージのデプロイが行われる前に
AWS::ApiGateway::ApiKey および AWS::ApiGateway::UsagePlan リソースによる使用量プランの
作成がはじまってしまい、ステージの指定に問題があるというエラーになっているようです。

# どう対処したか
ワークアラウンドとして、DependsOn プロパティに デプロイメントステージの論理IPを追記したところ
CloudFormation のデプロイが正常終了しました。
この論理IDは テンプレート内で明示的に作成していませんが、AWS::Serverless::Api リソースによって
作成されるため、

* \<APIの論理ID>Stage 

という形式で指定します。

## エラーがでるテンプレート例
```CFn_sample_NG.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Sample API

Parameters:
  EnvType:
    Description: Type of API Gateway stage.
    Default: dev
    Type: String
    AllowedValues:
      - dev
      - stg
      - prd
    ConstraintDescription: must specify dev or stg or prd.

  ProjectName:
    Description: Project name.
    Default: samplepj
    Type: String
    MinLength: "3"
    MaxLength: "10"
    AllowedPattern: "[a-zA-Z0-9]*"
    ConstraintDescription: must specify Project name.

Resources:
# Create API Gateway from swagger
  Api:
    Type: AWS::Serverless::Api
    Properties:
      DefinitionUri: ./templates/swagger-apigateway.yaml
      StageName: !Ref EnvType

# Create API Key
  ApiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn: 
      - Api
    Properties:
      Enabled: "true"
      StageKeys:
        - RestApiId: !Ref Api
          StageName: !Ref EnvType
      Name: !Join
        - '-'
        - - !Ref EnvType
          - !Ref ProjectName
          - 'apikey'

# Create API Usage Plan
  ApiUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    DependsOn: 
      - Api
    Properties:
      ApiStages:
        - ApiId: !Ref Api
          Stage: !Ref EnvType
      UsagePlanName: !Join
        - '-'
        - - !Ref EnvType
          - !Ref ProjectName
          - 'usageplan'

# Link API Key and Usage Plan
  ApiUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    DependsOn:
      - ApiKey
      - ApiUsagePlan
    Properties : 
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiUsagePlan
```
上記のテンプレートを package 後、deploy すると ApiUsagePlan と ApiKey の作成に失敗します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/444b9e7d-cd33-aae3-11e7-41dcf1877016.png)

## 正常終了するテンプレート例
```CFn_sample_OK.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Sample API

Parameters:
  EnvType:
    Description: Type of API Gateway stage.
    Default: dev
    Type: String
    AllowedValues:
      - dev
      - stg
      - prd
    ConstraintDescription: must specify dev or stg or prd.

  ProjectName:
    Description: Project name.
    Default: samplepj
    Type: String
    MinLength: "3"
    MaxLength: "10"
    AllowedPattern: "[a-zA-Z0-9]*"
    ConstraintDescription: must specify Project name.

Resources:
# Create API Gateway from swagger
  Api:
    Type: AWS::Serverless::Api
    Properties:
      DefinitionUri: ./templates/swagger-apigateway.yaml
      StageName: !Ref EnvType

# Create API Key
  ApiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn: 
      - Api
      - ApiStage # ★追記
    Properties:
      Enabled: "true"
      StageKeys:
        - RestApiId: !Ref Api
          StageName: !Ref EnvType
      Name: !Join
        - '-'
        - - !Ref EnvType
          - !Ref ProjectName
          - 'apikey'

# Create API Usage Plan
  ApiUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    DependsOn: 
      - Api
      - ApiStage # ★追記
    Properties:
      ApiStages:
        - ApiId: !Ref Api
          Stage: !Ref EnvType
      UsagePlanName: !Join
        - '-'
        - - !Ref EnvType
          - !Ref ProjectName
          - 'usageplan'

# Link API Key and Usage Plan
  ApiUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    DependsOn:
      - ApiKey
      - ApiUsagePlan
    Properties : 
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref ApiUsagePlan
```
ApiUsagePlan と ApiKey の DependsOn プロパティに \<APIの論理ID>Stage を追記します。
今回 API の論理ID をそのまま Api としているので、ステージの論理IDは ApiStage です。
ステージのデプロイメントが先に行われ、使用量プランとAPIキーの作成にも成功しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/1a85f3e9-f38f-228b-73c1-ffb974b8390b.png)

## 補足
AWS SAM 1.4.0 の [リリースノート](https://github.com/awslabs/serverless-application-model/releases/tag/1.4.0) によれば 以下のように Refなどの組み込み関数でAPI Gateway Stage
とAPI Gateway Deployment の論理IDを取得できます。

```yaml
!Ref MyApi.Stage #SAMによって生成されたステージを参照
!Ref MyApi.Deployment #SAMによって生成されたデプロイメントリソースを参照
```

しかし DepedsOn プロパティは文字列のみ指定可能であるため、今回の対応では直接記述しました。
特にデプロイメントリソースについては、API のデプロイ毎に論理IDが変化するので組み込み関数による
参照は重要ですね。

以上です。
参考になれば幸いです。