AWS Amplify サーバーレスウェブアプリケーションを構築するチュートリアルをやってみた

# 初めに

以下のチュートリアルをやってみました。Amplify と CodeCommit の連携、CodeCommit を使用した開発、Cognito を使ったオーソライザー、Lambda プロキシ統合など、普段使わないサービスばかりで勉強になりました。

https://aws.amazon.com/jp/getting-started/hands-on/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito/

## 1. CodeCommit にリポジトリを作成する

CodeCommit を使用してローカルで開発を進める場合、GitHub などと同様に認証情報を作成する必要があります。GitHub では 「SSH」 や GitHub アカウントの「ユーザー名・パスワード」を用いますが、CodeCommit では IAM を使用した認証を利用することができます。

> AWS CodeCommit リポジトリへの接続を設定する最も簡単な方法は、IAM コンソールで CodeCommit の Git 認証情報を設定し、HTTPS 接続にこれらの認証情報を使用することです。

IAM のコンソール画面でユーザーを選択し、CodeCommit の認証情報を作成します。この認証情報をダウンロードしておきます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/45cc821b-904a-f732-e1f5-97bf3d814000.png)

CodeCommit でリポジトリを作成します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/58fb5f39-9d16-cab7-7c0d-3b90bb0cadc9.png)

以下の「HTTPS のクローン」をクリックすると、クローンコマンドをコピーできます。

![1.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/221a084e-a36d-6ca4-78f9-b31c34f644d1.png)

コピーしたコマンドをローカルで実行します。ユーザー名・パスワードを聞かれるので、作成した CodeCommit の認証情報を入力します。

```
PS C:\tutorial> git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/wildrydes-site
Cloning into 'wildrydes-site'...
warning: You appear to have cloned an empty repository.
```

クローンしたフォルダに移動し、開発を行います。

```
PS C:\tutorial> cd wildrydes-site
```

このチュートリアルで用意されているアプリケーションのファイルは S3 に置いてあります。ユーザーアクセスキー（アクセスキーID・シークレットアクセスキー）を作成して S3 からローカルにファイルをコピーします。（`--recursive` オプションはフォルダ内を再帰的にダウンロードするためのオプションです。フォルダ構造を保持したままダウンロードが可能です。）

```
PS C:\tutorial\wildrydes-site> aws s3 cp s3://wildrydes-ap-northeast-1/WebApplication/1_StaticWebHosting/website ./ --recursive
```

ダウンロードしたファイルを CodeCommit にプッシュします。

```
PS C:\tutorial\wildrydes-site> git add .
PS C:\tutorial\wildrydes-site> git commit -m "initial commit"
PS C:\tutorial\wildrydes-site> git push
```

以下はプッシュ後のリポジトリです。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/d8981d15-88ba-e2d4-cad8-1ce104976f64.png)


## 2. Amplify で Web アプリケーションをデプロイする

この章ではCodeCommit に置いたソースコードを利用して Amplify でデプロイします。

「Host your web app」を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9b9df26e-623d-017f-b785-9feb6536655d.png)


Amplify に関連付けるリポジトリとして CodeCommit を選択します。

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/1d7d8e57-a144-123e-1721-1ffe45e5e900.png)

以下ではリポジトリ・ブランチを選択します。

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/78c41a8d-329f-f723-3645-7bce6876e1bd.png)

指定したブランチに変更があったとき、Amplify が自動でビルド・デプロイを行うことを許可するために「Allow AWS Amplify ...」にチェックを入れます。

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/d6997240-ccb7-5cb6-ca69-2174048a2812.png)

設定内容を確認して「保存してデプロイ」をクリックします。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/882b934b-eca1-263d-ae49-a0e6b4ceed7f.png)

## 3. Cognito でユーザープールを作成する

Cognito は認証・認可を行うためのサービスです。ユーザープールではユーザー認証を行います。ユーザープールの目的はユーザー情報を保管することです。Cognito が管理する対象としてもう一つ、 ID プールというものも存在します。こちらはユーザーに対して認可を行います。ID プールの目的はユーザーに他の AWS サービスにアクセスすることを許可することです。

> ユーザープールでは認証 (ID の検証) ができます。ユーザープールを使用すると、アプリユーザーはユーザープールからのサインインや、サードパーティーのアイデンティティプロバイダー (IdP) を介した連携ができます。
>
ID プールでは認可 (アクセスコントロール) ができます。ID プールを使用すると、ユーザーに一意の ID を作成して、他の AWS サービスへのアクセスを許可できます。

今回のチュートリアルでは、サインアップ・サインインの実装を行うために「ユーザープールの管理」を選択します。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/ee0a28f4-5337-e15b-f0b8-468c6e822d11.png)

「ユーザープールを作成する」をクリックします。

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/52883338-7e82-d3ec-d651-8ce63e28e0d1.png)

デフォルトで作成します。

![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/67e8cc08-b10f-43fc-1d1e-1fb6ab4a8077.png)

「プールの作成」をクリックします。

![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/7cf81b17-b279-443a-9bba-9b38dc66a27e.png)

## 4. ユーザープールにアプリを追加する

この章ではアプリクライアントを作成します。アプリクライアントを作成することで、認証されていないユーザーがサインアップ・サインインを行うことが可能になります。

左のナビゲーションペインから「アプリクライアント」を選択します。

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/3eb4c339-ed68-6281-042b-f4b0e814b67c.png)

「アプリクライアントの追加」をクリックします。

![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/7f4e3e85-ab88-c033-72e8-f5b248a83f16.png)

シークレットの作成からチェックを外します。チュートリアルではシークレットについて以下の記述があります。

> クライアントシークレットは、現在ブラウザベースのアプリケーションでの使用はサポートされていません。

なおユーザーガイドではシークレットについて上記のようにはっきりとは述べてはいません。

> アプリケーションを作成すると、そのアプリケーションのシークレットを作成することもできます。シークレットをアプリケーション用に作成した場合、アプリケーションを使用するには、シークレットを提供する必要があります。JavaScript で作成されたブラウザベースのアプリケーションには、シークレットを持つアプリが必要ない場合があります。

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9e15a508-b130-4d43-f6b1-3e9bc3c5fec9.png)

他はデフォルトのまま「アプリクライアントの作成」をクリックします。

![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/21443127-b751-e757-8c73-c6b214bd94ac.png)

wild-ryde-site/js/config.js を編集します。`userPoolId`、`userPoolClientId`、`region` の値を設定します。`invokeUrl` はそのままにします。これは API Gateway で REST API をデプロイによって発行される URL を記述します。

```js
window._config = {
    cognito: {
        userPoolId: '', // e.g. us-east-2_uXboG5pAb
        userPoolClientId: '', // e.g. 25ddkmj4v6hfsfvruhpfi7n4hv
        region: '' // e.g. us-east-2
    },
    api: {
        invokeUrl: '' // e.g. https://rc7nyt4tql.execute-api.us-west-2.amazonaws.com/prod',
    }
};
```

この変更を CodeCommit にプッシュします。

```
PS C:\tutorial\wildrydes-site> git add .\js\config.js
PS C:\tutorial\wildrydes-site> git commit -m "setting config.js"
PS C:\tutorial\wildrydes-site> git push
```

Web サイトを開き、「Giddy Up!」をクリックし、メールアドレス・パスワードを入力します。その後、登録したメールアドレスに認証コードが届くので入力します。最後にログイン画面に遷移するのでログインできることを確認します。この操作の後、以下のようにユーザープールにユーザーが登録されます。

![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/eee76112-f66b-49e4-d8ea-344c9fd168fa.png)

## 5. サーバーレスバックエンドを構築する

この章では DynamoDB、Lambda を作成します。

DynamoDB を使用してテーブルを作成します。以下のように設定します。

![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/6f63f59f-0276-4b9a-e1be-28708d8a397a.png)

次に Lambda 関数を作成します。ロールはあらかじめ次のように作成しておきます。

- 信頼されたエンティティには「Lambda」を選択し、ポリシーは「AWSLambdaBasicExecutionRole」という AWS 管理ポリシーを選択
- インラインポリシーには以下を追加

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "dynamodb:PutItem",
            "Resource": "arn:aws:dynamodb:ap-northeast-1:account-id:table/Rides"
        }
    ]
}
```

![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/6d5daaff-c8a6-5661-2d38-67fb3d2ace37.png)


Lambda 関数のコードは [requestUnicorn.js](https://webapp.serverlessworkshops.io/serverlessbackend/lambda/requestUnicorn.js) を使用します。

## 6. API Gateway で RESTful API を作成する

API を呼び出すユーザーを制御するためには以下の方法があります。このチュートリアルでは「Cognito ユーザープールを使用したオーソライザー」を利用します。

- リソースポリシー
- IAM ロール
- Lambda オーソライザー
- Cognito ユーザープールを使用したオーソライザー



「REST API」の「構築」を選択します。

![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/15ee551d-407f-13b7-dd4d-52feba49fab7.png)

以下のように設定します。

![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9dbebbc1-188b-57c1-d544-e7eb1df6751f.png)

オーソライザーを選択します。

![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8d892127-9e43-eca6-b616-ef5adf654d7c.png)

作成したユーザープールを選択します。

![28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/d1089186-e50b-577a-0ef0-ff02ea74b377.png)

オーソライザーのテストを行います。トークンにはログイン後に表示されるトークンを入力します。

![29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/b4e878a0-1ea0-1d2e-60bd-259b930e4c89.png)

レスポンスを確認します。

![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/0d4da287-493b-cc06-6481-5a4c117d7fae.png)

リソースを選択します。

![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/4ad6160b-d829-208f-c153-59a0b2fc3d8f.png)

「リソースの作成」を選択します。

![32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/e671f4f3-a691-42e9-9f6f-68f6045259df.png)

リソースパスを入力し、CORSを有効化するようにチェックを入れます。

![33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/bda43a92-1ac0-6528-77b3-20aa28428a87.png)

上記リソースを選択した状態でメソッドを作成します。

![34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/e847f660-6209-52da-4b5b-09b1bcc8b86c.png)

「POST」を選択し、横のチェックマークをクリックします。

![35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/90c71366-4f06-00b8-6bef-6165117e72ab.png)

Lambda 関数を選択します。

![36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/240d60aa-bbaf-3ed6-1442-8013502fb6a7.png)

オーソライザーをこのメソッドに設定します。まずメソッドリクエストをクリックします。

![37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/7a768837-e045-29d0-cc54-c8a9c40537bc.png)

赤枠内の鉛筆マークをクリックします。

![38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/a770d795-2aaa-cc30-0791-f2e8b4a2ba8e.png)

作成したオーソライザーを設定します。

![39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/a9fcb070-a4b3-f484-38f9-f12fed6baeb4.png)

API のデプロイを行います。

![41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/cfb07a46-cff2-ebd7-f17a-4e4f158acf25.png)

ステージ変数に「prod」と入力します。

![42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9f4d4563-2aeb-55d9-2b3a-674cab5faa2d.png)

URL をコピーし、wild-ryde-site/js/config.js の `invokeUrl` の値として設定します。

![43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/89ff85b1-b77f-5098-e542-16b3fe0f220a.png)


```js
window._config = {
    cognito: {
        userPoolId: 'ap-northeast-1_xxxxxx',
        userPoolClientId: 'yyyyyyyyyyyy',
        region: 'ap-northeast-1'
    },
    api: {
        invokeUrl: 'https://zzzzzz.execute-api.ap-northeast-1.amazonaws.com/prod' 
    }
};
```

プッシュします。

```
PS C:\tutorial\wildrydes-site> git add .\js\config.js
PS C:\tutorial\wildrydes-site> git commit -m "add invoke url in config.js"
PS C:\tutorial\wildrydes-site> git push
```

サインイン後、地図が表示されるので適当にクリックし「Request Unicorn」をクリックするとユニコーンがその場所まで走ってきます。これは ajax で API Gateway にリクエストを送り、オーソライザーが認証に成功するとバックエンドの Lambda がリクエストを処理します。以下は DynamoDB にアイテムを登録する Lambda 関数の該当ソースです。

```js
function recordRide(rideId, username, unicorn) {
    return ddb.put({
        TableName: 'Rides',
        Item: {
            RideId: rideId,
            User: username,
            Unicorn: unicorn,
            UnicornName: unicorn.Name,
            RequestTime: new Date().toISOString(),
        },
    }).promise();
}
```

またイベント内容がログに出力されているので、Lambda 関数がどんなイベントをユーザーから受け取っているのかを CloudWatch Logs で確認できます。

```
2021-07-22T13:13:07.946Z	6aea1cb3-df95-48d2-b27f-1c4864e944f9	INFO	Received event ( FfOiuANvNwpCrolI7iA0Ig ):  {
  resource: '/ride',
  path: '/ride',
  httpMethod: 'POST',
  headers: {
    Accept: '*/*',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8',
    Authorization: 'eyJraWQiOiIwQ3NnN...
```


Dynamo DB でこのユニコーン情報が登録されていることが確認できます。

![44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/b032155a-c3df-7c8e-5ab2-b92c91eb3d61.png)


# ハマったところ

CodeCommit の認証情報を間違えて入力してしまい、クローンができなくなってしまいました。以下の Windows 資格情報からユーザー名・パスワードを編集できます。

リポジトリの URL が表示されている箇所をクリックします。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/ff7020f2-1828-85c6-ab16-13b7d73e503d.png)

以下の「編集」をクリックし、編集します。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/dd0f45e7-4fc0-798a-7eb1-bdc7e4495739.png)

# 参考記事

https://aws.amazon.com/jp/getting-started/hands-on/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito/

https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up-gc.html

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-settings-client-apps.html

https://aws.amazon.com/jp/premiumsupport/knowledge-center/cognito-user-pools-identity-pools/

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-control-access-to-api.html

https://qiita.com/C_HERO/items/6fa0ff4153f0149645cd

https://qiita.com/maominionbsk54/items/de00bbba78074fdd7325