AWS Amplify でサンドボックスの主要機能を実際に試してみた

## はじめに

チームでの開発では、各メンバーが専用の AWS 環境を用意できればいいのですが、プロジェクト全体で共有の環境を使用するケースも多いのではないでしょうか。そのような場合、個人の作業がチーム全体に影響を与えないように、配慮しながら開発を進める必要があります。
Amplify のサンドボックス機能は、個人用の一時的な開発環境を簡単に構築することができます。このサンドボックス機能を実際に試しながら、その使い方や動きについて見ていきます。

https://docs.amplify.aws/react/deploy-and-host/sandbox-environments/setup/

### 実行環境

プロジェクトの構築は、公式ドキュメントの [Quickstart](https://docs.amplify.aws/react/start/quickstart/) の手順にて作成しています。

- Node.js: `20.15.0`
- ampx: `1.2.9`

## サンドボックス環境を構築

```shell
npx ampx sandbox
```

定義されているリソースに基づいて、サンドボックス環境をデプロイします。デフォルトでは、実行環境のログインユーザー名が識別子として利用されますが、変更したい場合は、`--identifier` オプションを利用して、指定することもできます。
デプロイ完了後は、リソースの変更に応じてサンドボックス環境にも反映されるようになっていて、リソースによっては[ホットスワップデプロイ](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/cli.html#cli-deploy-hotswap)で変更内容が反映されます。Lambda を使って実装している時は、変更の反映が早くていいですよね。

マネコンで Amplify のサンドボックス環境を確認すると、デプロイしたサンドボックス環境が作成されていることを確認できました。

![Monosnap Image 2024-12-26 21-17-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331769/0e78b328-f0bb-d0d0-a0f5-4e11405e96ed.png)

各リソースは CloudFormation によって管理されているので、スタックが作成されています。実際にどのようなリソースが作成されているかを確認するには、スタックを確認するのがわかりやすいかもしれませんね。

![Monosnap Image 2024-12-26 21-32-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331769/b8a98495-714b-d406-341a-2fa3989fa8a0.png)

デプロイ完了後（編集時も含め）、`amplify_outputs.json` が作成または更新されます。このファイルには、作成されたリソースのエンドポイント情報や認証関連の情報などが定義されています。Amplify の library を利用する際は、このファイルの情報が利用されます。

## シークレットの利用

API のキーやデータベースの認証情報などの機密データを管理するために、シークレットを利用することができます。シークレットは Systems Manager のパラメータストアに保存されます。

### シークレットを追加

```shell
npx ampx sandbox secret set hoge
```

```shell
? Enter secret value ["hogehoge" と入力]
Successfully created version 1 of secret hoge
```

指定したキー（今回のサンプルだと `hoge`）の、シークレットを作成します。`secret set`コマンドを実行すると、キーに応じた値を設定することができます。
作成したシークレットは、Systems Manager のパラメータストアに SecureString として保存されます。

![Monosnap Image 2024-12-29 07-40-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331769/0b84ab0e-dbb9-febf-8c76-babf6dfcb105.png)

なお、同じシークレットキーで再度実行すると、バージョンが上がります。

### シークレットを一覧表示

```shell
npx ampx sandbox secret list
```

```shell
 - bar
 - hoge
```

作成済みのシークレットを一覧表示することができます。

## シークレットの値を確認

```shell
npx ampx sandbox secret get hoge
```

```shell
name: hoge
version: 1
value: hogehoge
lastUpdated: Sun Dec 29 2024 07:31:52 GMT+0900 (Japan Standard Time)
```

シークレットのキーに応じた、シークレットの値を確認することができます。合わせて、シークレットのバージョンや更新日時も確認することができます。表示対象のシークレットは、最新バージョンのシークレットが表示されました。`[シークレットキー]:[バージョン番号]` とすることで、過去のバージョンのシークレットを取得することもできるようでした。

### シークレットの値を利用

secret 関数を利用することで、バックエンドリソースの定義で作成したシークレットの値を利用することができます。secret 関数は、Authentication の[External identity providers](https://docs.amplify.aws/react/build-a-backend/auth/concepts/external-identity-providers/) や、Functions の [secrets](https://docs.amplify.aws/react/build-a-backend/functions/environment-variables-and-secrets/#secrets) など、特定の箇所でのみ利用することができます。
設定したシークレットは、シークレットへの直接的な値の埋め込みではなく、参照を設定する仕組みとなっているため、よりセキュアで良さそうですね。

```typescript:amplify/auth/resource.ts
export const auth = defineAuth({
  loginWith: {
    email: true,
    externalProviders: {
      loginWithAmazon: {
        clientId: secret("hoge"),    // シークレットの値を参照
        clientSecret: secret("bar"), // シークレットの値を参照
      },
      callbackUrls: ["http://localhost:3000/"],
      logoutUrls: ["http://localhost:3000/"]
    }
  },
});
```

このように、secret 関数に、シークレットのキーを指定することで、参照することができます。
デプロイ後、Cognito のユーザープールを確認すると、シークレットの値が参照できていることを確認できます。

![Monosnap Image 2024-12-29 09-16-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331769/4d83d876-3f3f-c3c1-3772-ded5a6631839.png)

なお、シークレットの値はデプロイ後に解決されるので、CloudFormation のテンプレートには、値ではなくシークレットへの参照が設定されています。

```json
{
    ...
    "hogeSecretFetcherResource": {
      "Type": "Custom::SecretFetcherResource",
      "Properties": {
        "ServiceToken": {
          "Fn::GetAtt": [
            "SecretFetcherResourceProviderframeworkonEvent960CF056",
            "Arn"
          ]
        },
        "namespace": "amplify-vite-react-template",
        "name": "yu-sakai",
        "type": "sandbox",
        "secretName": "hoge",
        "secretLastUpdated": "1735427725101"
      },
      "UpdateReplacePolicy": "Delete",
      "DeletionPolicy": "Delete",
      "Metadata": {
        "aws:cdk:path": "amplify-amplifyvitereacttemplate-yusakai-sandbox-xxx/auth/hogeSecretFetcherResource/Default"
      }
    },
    ...
}
```

続いて Lambda の環境変数で、secret 関数を利用してみます。

```typescript: amplify/functions/say-hello/resource.ts
export const sayHello = defineFunction({
  name: "say-hello",
  entry: "./handler.ts",
  environment: {
    STATIC_TEXT: "environment static text",
    HOGE: secret("hoge"),  // シークレットの値を参照
    BAR: secret("bar")     // シークレットの値を参照
  }
})
```

デプロイ後、Lambda 関数の環境変数を確認すると、実行時に値を解決する旨、メッセージが記載されており、シークレットをセキュアに扱っていることを確認できました。

![Monosnap Image 2024-12-29 09-35-19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331769/8dc774b4-7dad-0b00-c63e-7992edab6f95.png)

シークレットは関数実行時に解決されるので、以下の Lambda 関数を実行すると、シークレットが解決されていることを確認できます。

```typescript: amplify/functions/say-hello/handler.ts
import type {Handler} from 'aws-lambda';
import {env} from "$amplify/env/say-hello";

export const handler: Handler = async (event, context) => {
  return `HOGE: ${env.HOGE}, BAR: ${env.BAR}`
};
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331769/58305826-8155-0cdd-55cc-a0731b4a0818.png)

### シークレットを削除

```shell
npx ampx sandbox secret remove hoge
```

指定したキーのシークレットを削除することができます。シークレットを削除すると、Systems Manager のパラメータストアから削除されますので、参照している部分でパラメータの解決ができなくなりますので、注意が必要です。

## Lambda のログをストリーミング

サンドボックス環境にデプロイしている Lambda のログをローカル PC のターミナルでストリーミングすることができます。

https://docs.amplify.aws/react/build-a-backend/functions/streaming-logs/

```shell
npx ampx sandbox --stream-function-logs
```

```shell
[say-hello] 9:56:52 AM INIT_START Runtime Version: nodejs:18.v57        Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:8865cfc6a1d3f2dfabf5c509eaa9fbd70aa12fa4bbe614047030158c21978bcc
[say-hello] 9:56:52 AM START RequestId: 633dc187-3af0-4e35-9994-2216d9c82eb4 Version: $LATEST
[say-hello] 9:56:52 AM 2024-12-29T00:56:52.584Z 633dc187-3af0-4e35-9994-2216d9c82eb4    INFO    event:  { key1: 'value1', key2: 'value2', key3: 'value3' }
[say-hello] 9:56:52 AM END RequestId: 633dc187-3af0-4e35-9994-2216d9c82eb4
[say-hello] 9:56:52 AM REPORT RequestId: 633dc187-3af0-4e35-9994-2216d9c82eb4   Duration: 14.95 ms      Billed Duration: 15 ms  Memory Size: 512 MB     Max Memory Used: 92 MB  Init Duration: 543.08 ms
[say-hello-2] 10:02:40 AM INIT_START Runtime Version: nodejs:18.v57     Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:8865cfc6a1d3f2dfabf5c509eaa9fbd70aa12fa4bbe614047030158c21978bcc
[say-hello-2] 10:02:40 AM START RequestId: fd9821b0-991e-4325-9532-5373155274ed Version: $LATEST
[say-hello-2] 10:02:40 AM 2024-12-29T01:02:40.480Z      fd9821b0-991e-4325-9532-5373155274ed    INFO    この Lambda は say-hello-2 です。
[say-hello-2] 10:02:40 AM END RequestId: fd9821b0-991e-4325-9532-5373155274ed
[say-hello-2] 10:02:40 AM REPORT RequestId: fd9821b0-991e-4325-9532-5373155274ed        Duration: 18.97 ms      Billed Duration: 19 ms  Memory Size: 512 MB     Max Memory Used: 68 MB  Init Duration: 199.97 ms
```

このように Functions でサンドボックス環境にデプロイしているすべての Lambda のログがストリーミングされていることを確認できました。
ストリーミング対象の Lambda をフィルタリングしたい場合は、`--logs-filter` オプションを利用することができます。関数名に含まれる文字列でフィルタリングされます。

```shell
npx ampx sandbox --stream-function-logs --logs-filter hello-2
```

`hello-2` をフィルター条件に指定すると、`say-hello-2` のログのみストリーミングされ、`hello` を指定すると、`say-hello`, `say-hello-2` のログがストリーミングされることを確認できました。管理する Lambda が多くなってくると、使いたくなるオプションですね。

## サンドボックス環境を削除

```
npx ampx sandbox delete
```

コマンドを実行することで、作成したサンドボックス環境を削除することができます。マネコンの Amplify のサンドボックスから削除することもできます。料金が発生するリソースもあるため、サンドボックス環境を利用しなくなった場合は、忘れずにクリーンアップしておきましょう。
サンドボックスで作成されたリソースは、`DeletionPolicy` が `Delete` に設定されているリソースが多いので、スタック削除時にほぼすべてのリソースを削除してくれるのも、個人的には嬉しいポイントでした。

:::note warn
すべてのリソースが確実に削除されたことを確認できているわけではありません。
削除後はマネジメントコンソール等でリソースが残っていないか確認してください。
:::

## さいごに

このサンドボックス環境を使いたいためだけに、Amplify を利用したいと思うほど、非常に良い開発者体験を得られる開発ツールだと感じました。
開発者個人が占有できる開発環境を１アカウント内に構築できること、開発時に実稼働環境と同等の環境を利用できること、そして実際には AWS 上で実行されているにもかかわらず、ローカルで実行している感覚で開発できることが、特に良い点だと思いました。
とても便利な機能ですので、触ったことがない人は、まずは [Quickstart](https://docs.amplify.aws/react/start/quickstart/) でサンドボックス環境を実際にさわってみることをおすすめします 😁

## 参考

https://docs.amplify.aws/react/reference/cli-commands/