一からAWS API Developer Portalを構築する

AWS API Gateway Developer PortalをAWSコンソールからデプロイし、ポータルでAPIを管理できる状態までセットアップする手順をまとめました。

#### 補足

- デプロイの方法は下記の2種類ありますが、今回は1の方法について補足を残します

1. AWS Serverless Application Repositoryを利用してAWSコンソールからデプロイ
2. GitHubからアプリのリポジトリをCloneし、SAM経由でデプロイ


- 公式ドキュメント：[サーバーレス開発者ポータルを使用して API Gateway API を分類する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-developer-portal.html#apigateway-developer-portal-best-practices)

- なお、2の方法を試されたい場合は、上記公式ドキュメントの他、こちらのクラスメソッド様の記事を参考にしていただくと良いかと思います
    - [API Gateway のポータルサイトを簡単に構築できるサーバーレスアプリを試す ー Developers.IO](https://dev.classmethod.jp/articles/api-gateway-portal/)


##  手順

※この手順を実行したAWS バージョン: `4.0.3`

### 1. Applicationページを開く
   
https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:563878140293:applications~api-gateway-dev-portal

※クリックすると AWSコンソールのAWS Lambdaサービスページに飛ぶので、AWSコンソールにログインする必要があります

##### 補足

以下の方法でもアプリケーションページにたどり着けます。

1. [Lambda] > [関数] > [関数の作成] と選択する
2.  [Serverless Application Repository の参照]を選択する
3.  「パブリックアプリケーション」に `api-gateway-dev-portal` と入力する
4.  検索バー下の [カスタム IAM ロールまたはリソースポリシーを作成するアプリを表示する] にチェックを入れる
5.  同名のアプリが検索結果に現れるので、選択する

### 2. [Deploy]をクリックする

### 3. 「アプリケーションの設定」項目に以下を記載する

ここで設定した内容・名前を元にスタック・リソースが作られていきます。
経験上、命名規則違反でスタックのデプロイがこけることが多々あります~~（命名規則を入力欄の横に説明して欲しい...もっと言うとフォームごとにバリデーションチェックがあると嬉しい。。。）~~。そのため、できるだけ下記「名前例」に寄せた形かつ、長くならないようにするのがコツです。
また、全世界やリージョン内でユニークな名前でなければならないリソースもありますので、お気をつけください。

なお、各設定項目の説明は[こちら](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-developer-portal.html#apigateway-developer-portal-settings)にもあります。

#### 3-1. 以下の設定を入力する


| 設定名 | 説明 | 名前例 |
|:--|:--|:--|
| アプリケーション名 | CloudFormationに作成されるスタック名。14文字以下にする必要があります。デフォルトの名前は使用しないこと**※1** | api-dev-portal |
| AccountRegistrationMode | ポータルでアカウント作成する際の手順。openなら個人が自由に作成でき、inviteならAdminユーザの招待が必要になる。**この記事では、 `invite` にしたい場合でも一度 `open` でデプロイします ※1**|　open / invite |
| ArtifactS3BucketName | APIのドキュメントが保存されるS3バケットの名前。**S3バケットの命名規則に従う必要あり** | api-dev-portal-docs-xxxxx |
| CognitoDomainNameOrPrefix | Cognitoでホストされる、ユーザープールのウェブUIのドメイン名。**小文字英数字・ハイフンのみ利用可能です**。**リージョン内でユニークである必要あり**| dev-portal-userpool-xxxxx |
| CognitoIdentityPoolName | CognitoのIDプール名。Cognitoユーザープール名にも利用される | DevPortalIdentityPool |
| CustomDomainName | ポータルのカスタムドメイン名。**オプション項目なので、設定しない場合は空にする**| foo.bar.net |
| DevPortalAdminEmail | ポータルのユーザがポータル管理者にフィードバックを送った際の通知の送信先メール。**設定しない場合は空にする** | `USI@xxx.com` |
| DevPortalCustomersTableName | APIポータルのユーザを管理するDynamoDBテーブルの名前 | DevPortalCustomers |
| DevPortalFeedbackTableName | ポータルのユーザが送ったフィードバックを管理するDynamoDBテーブルの名前 | DevPortalFeedback |
| DevPortalPreLoginAccountsTableName | ポータルにプレログインできるアカウントを管理するテーブルの名前 | DevPortalPreLoginAccounts |
| DevPortalSiteS3BucketName | ポータルサイトの静的コンテンツが保存されるS3バケットの名前。**S3バケットの命名規則に従う必要あり** | api-dev-portal-website-xxxxx |
| DevelopmentMode | 開発を容易にするために、OAI, SSL, 静的サイト配置バケットアクセス、Cognitoのコールバック、CORSに関する権限を緩める**※2** | true / false |
| EdgeLambdaRebuildToken | Lambdaをデプロイする際に利用するトークンをデプロイごとに最新にする（タイムスタンプやGUIDを与えることで更新できる） | defaultRebuildToken |
| LocalDevelopmentMode | ローカル環境での開発を容易にするために、CORS設定をなくす**※3**| true / false |
| UseRoute53Nameservers | `CustomDomainName` で指定したカスタムドメインについて、Route53でネームホスティングを行うかどうかを指定する。**カスタムドメインを利用しない場合は `false` にする** | true / false |

##### ※の補足

*※1 理由は、 `invite` でデプロイした後、最初のユーザーの作成方法がわからなかったからです...。なので、 `open` で1ユーザー作成後、今度は `invite` でアプリケーションを更新する方法をとっています*

*※2 スタック名は他のリソースの作成にも利用されますが、 `CognitoPostAuthenticationTriggerFn` というリソースを作成する際に最大64文字までしか認められていない上、スタック名には自動的に `serverlessrepo-` がつく・このリソース名は自動的に `${スタック名}-CognitoPostAuthenticationTriggerFn` になることから、`64-15-35 = 14` という計算です。*
*※3 セキュリティレベルが落ちるため、プロダクションモードでは絶対に `false` に（開発モードを拒否）する必要があるそうです！
また、一度開発モードで作成したポータルは、スタックの更新で `true` （本番モード）にせず、別の新たなポータル（スタック）として作り直す必要があるようです。*


- CustomDomainCloudFrontDistribution

| 設定名 | 説明 |
|:--|:--|
|　CustomDomainNameAcmCertArn | ACM証明書のARN。`CustomDomainName` で指定したカスタムドメインがACM証明書を利用する場合、そのARNを入力する。**カスタムドメインを設定しない場合は空にする** |

- StaticAssetUploader

| 設定名 | 説明 | 名前例 |
|:--|:--|:--|
|　StaticAssetRebuildMode | デフォルトでは上書きされない静的サイト内のカスタム内容を自身のローカルにあるバージョンで上書きする（何を言っているかわからない場合は `overwrite-content` にしない方が良いとのこと） | overwrite-content |
|　StaticAssetRebuildToken | 静的コンテンツをデプロイする際に利用するトークンをデプロイごとに最新にする（タイムスタンプやGUIDを与えることで更新できる） | defaultRebuildToken |


#### 3-2. 「このアプリがカスタム IAM ロールとリソースポリシーを作成することを承認します。」にチェックを入れる

#### 3-3. 最後に「デプロイ」をクリックし、デプロイを開始する

### 4. デプロイ状況を確認する

先ほど設定したポータル「`serverlessrepo-${アプリケーション名}`」のページが表示されます。
デプロイにはある程度時間がかかります。

#### 4-1. 「デプロイ」 > 「SAM テンプレート」 > [CloudFormation スタック]をクリックする

#### 4-2. スタックの情報 > ステータス を確認する

- `CREATE_COMPLETE` → スタックデプロイに成功
- `ROLLBACK_COMPLETE` → スタックデプロイに失敗し、ロールバックがなされた（成功）
- `ROLLBACK_FAILED` → スタックデプロイに失敗した上、ロールバックにも失敗した

ステータスが**`CREATE_COMPLETE` の場合、[5. ポータルページにアクセスできることを確認する
](###5.-ポータルページにアクセスできることを確認する
)に進んで下さい**。

**それ以外のステータスの場合は、デプロイに失敗しているので、原因箇所を確認します**。以下は失敗時の方法です。

 「イベント」タブ以下の「ステータス」項目で、最も古い時刻で `CREATE_FAILED` が起きている箇所を確認します。（最も古いもの以外の失敗は、最初期の失敗の影響を受けて失敗している・失敗の直接原因ではない可能性があります。）

以下がエラーの例です。「状況の理由」に、なぜデプロイに失敗したか書いてあります。
この場合は、ドメイン名には小文字・数字・ハイフン以外入れないでください、とあります。

|タイムスタンプ|論理ID|ステータス|状況の理由|
|:--|:--|:--|:--|
| 2020-04-28 11:40:28 UTC+0900 | CognitoUserPoolDomain | CREATE_FAILED | Failed to create resource. The domain name contains an invalid character. Domain names can only contain lower-case letters, numbers, and hyphens. Please enter a different name that follows this format: ^[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?$ For more details, see CloudWatch group （以下略） |


作成に失敗したスタックは一度[削除]で削除する必要があります。
削除する前に、全てのリソースがロールバックに成功し、リソースの削除に失敗していないことを確認してください。

スタックの状態が `ROLLBACK_FAILED` だった場合は、リソースが削除しきれていない可能性があります。スタックを消しても、ロールバックで削除できなかったリソースが残ってしまう原因になりますので、手動で削除してください。

以下、 `ROLLBACK_FAILED` エラーの例です。
ポータルのS3バケット・顧客DynamoDBテーブル・APIドキュメント管理S3バケットの3つのリソースの削除に失敗しています。


| タイムスタンプ |論理 ID | ステータス | 状況の理由 |
|:--|:--|:--|:--|
| 2020-04-28 11:45:23 UTC+0900 | serverlessrepo-TestDevPortalStack | ROLLBACK_FAILED | The following resource(s) failed to delete: [DevPortalSiteS3Bucket, CustomersTable, ArtifactsS3Bucket]. |

エラーに応じて、適宜手動で対応してください。

上記対応が終わったら、スタックを削除します。

[削除]をクリックし、スタックを削除してください。
スタック自体に対する操作も同じページ内でログ出力されますので、スタックの `DELETE_COMPLETE` を確認してください。

確認できたら、もう一度[1. Applicationページを開く](###1.-Applicationページを開く)からやりします。

### 5. ポータルページにアクセスできることを確認する

ポータルのデプロイが成功していると、ポータルへのリンクが出力されます。
先ほど確認したスタックのページ内の「出力」をクリックし、出力内容を確認します。
`WebsiteURL` というキーの値として出力されているリンクをクリックし、ページ遷移できることを確認してください。

### 6. Adminユーザを作成する

ポータルページを管理するAdminユーザを作成します。

##### 前提の補足
この記事では前述のように、[3. 「アプリケーションの設定」項目に以下を記載する](###3.-「アプリケーションの設定」項目に以下を記載する)の `AccountRegistrationMode` を `open` にしている前提で話します。
理由は、 `invite` でアプリを作成した場合、ポータルページから一切アカウント作成ができないのですが、現在どこからユーザーが新規作成できるのかわかりません（Amazon Cognitoコンソール・CLI経由のみ試しましたがうまくいきませんでした）。

#### 6-1. [5. ポータルページにアクセスできることを確認する](5.-ポータルページにアクセスできることを確認する)でアクセスしたページの右上 [Register] をクリックする
#### 6-2. ID（メールアドレス）・パスワードを設定する
#### 6-3.  IDに入力したメールアドレス宛に認証コードが届くので、入力する

ここまでの手順により、ユーザーが作成されました。 
ここから、ユーザにAdmin権限を付与します。

#### 6-4. [3. 「アプリケーションの設定」項目に以下を記載する](3.-「アプリケーションの設定」項目に以下を記載する)で作成したCognitoユーザープールのページを開く

CognitoID設定項目 `CognitoIdentityPoolName` で指定したプール名がわかる場合は、 Amazon Cognitoのサービスを開き、 [ユーザープールの管理] をクリックした後、その名前のユーザープールを選択してください。

##### ユーザープール名がわからない場合

以下の2通りの方法のうちいずれかの方法で調べてください。

1. AWS Lambdaのサービスページ内「アプリケーション」で今回作成したポータルを選択し、 [概要]　タブ内の「リソース」内の`CognitoIdentityPoolName` を検索し対象リソースを選択する：「論理ID」をクリックするとページ遷移します

2. CloudFormationのサービスページ内で、作成したスタックを選択し「リソース」内から `CognitoUserPool` を検索し対象リソースを選択する：「物理ID」をクリックするとページ遷移します

#### 6-5. [全般設定] > [ユーザーとグループ] で表示したユーザーリストから先ほど登録したユーザを選択する

#### 6-6. [グループに追加]をクリックし、 `${スタック名}AdminsGroup` をリストから選んだ後、[グループに追加]をクリックする

- スタック名は `serverlessrepo-${アプリケーション名}`です

これで、ユーザにAdmin権限が付与されました。APIポータルにログインし直し、タブ「Admin Panel」があることを確認してください。
このパネルから、APIやユーザーの管理ができます。ユーザーの招待もここから可能です。

これで、ポータルのセットアップが完了しました。あとは、ポータルで公開するAPIを管理しましょう。

ポータルの利用方法については、こちらの記事が参考になります。
[AWS API Gateway Developer Portal を使ってみる ー Qiita](https://qiita.com/Junpei_Takagi/items/205aedb6d8c752802ae1)

### 7. [おまけ]APIポータルを更新する

APIポータルは公開リポジトリという性質上、度々バージョンが更新されます。過去には脆弱性のあるバージョンからのアップデートを促す通知などもありました。

こういった際や、ポータルの設定を変更したい時、ポータルを更新する必要が出てきます。ここに、その方法を軽くまとめておきます。

今回は、 [3. 「アプリケーションの設定」項目に以下を記載する](###3.-「アプリケーションの設定」項目に以下を記載する)で `open` に設定した `AccountRegistrationMode` を `invite` に変更し、新たなユーザーは管理者の招待でしか追加できないようにしてみます。

#### 7-1. CloudFormation > スタック に移動し、今回作成したスタックを選択する

- スタック名は `serverlessrepo-${アプリケーション名}`です

#### 7-2. 右上[更新する] > [現在のテンプレートの使用] > [次へ]

#### 7-3. `AccountRegistrationMode` 項目で `invite` を選択し [次へ]

#### 7-4. 「スタックオプションの設定」ページで何も変更せず [次へ]

#### 7-5. 「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にチェックを入れ、[更新]をクリックする

スタックの更新後、スタックの状態が `UPDATE_COMPLETE` になれば完了です。
ポータルにアクセスし、[Register] をクリックしても新規ユーザーが作成できないようになっていることを確認しましょう。