AWS AmplifyのHostingに待望のIAM Compute Rolesが実装されたので試す

# TL;DR

- Amplify HostingでSSRする際に不足していた「実行ロール」機能が実装された。これでSSR環境下で安全にAWSリソースを利用できる
- 今までは安全にAWSリソースを制御するには、Amplify Backendか、IAMユーザーのクレデンシャルを持たせるかしか方法がなかったが、これが解決する（AWSのほかのコンピューティングと同じように使える）

## Amplify Hostingの実行環境にIAMロールを設定出来るようになった

https://aws.amazon.com/jp/blogs/mobile/iam-compute-roles-for-server-side-rendering-with-aws-amplify-hosting/

Amplify Hostingでは最近はServer-side Rendering（SSR）がサポートされていて、Node.js上でフロントエンドアプリケーションを実行してくれるようになっています。ただしこれまで、そのNode.js実行環境にIAMロールを設定することが出来ませんでした。このアップデートでIAMロールを設定出来るようになり、Node.jsから安全にAWSリソースを操作出来るようになります。

本記事では、SvelteKitを利用して、IAMロールに基づいたAWSリソースの制御をテストします。

# 検証

## S3バケットの作成

バケットを作成してファイルを置いておく。ここでは`amplify-role-test`というバケットに`mock.json`というファイルを配置。

![Screenshot 2025-02-19 at 9.10.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/7188e422-4779-4511-9de6-c1ad079e50b8.png)

```json:mock.json
{
	"message": "hello amplify"
}
```

## IAMポリシーの作成

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::amplify-role-test/*"
        }
    ]
}
```

## IAMロールの作成

「カスタム信頼ポリシー」を選択して以下のポリシーを記述

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "amplify.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

![Screenshot 2025-02-19 at 9.14.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/2967caa7-3741-462e-9f03-9721a2ef47f3.png)

## SvelteKitアプリケーションの作成

SvelteKitはAmplifyで動作します。詳しくは以下の記事を参照してください。

https://qiita.com/Kanahiro/items/dd4afc0d0871b97d4f33

```sh
npx sv create svelte-amplify-ssr-role
cd svelte-amplify-ssr-role
pnpm add -D @aws-sdk/client-s3 amplify-adapter
```

```typescript:src/routes/api/mock/+server.ts
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { json } from '@sveltejs/kit';

const s3 = new S3Client({
	region: 'ap-northeast-1',
});

export async function GET() {
	// getobject
	const params = {
		Bucket: 'amplify-role-test',
		Key: 'mock.json',
	};
	const command = new GetObjectCommand(params);
	const response = await s3.send(command);
	const jsonStr = await response.Body.transformToString();
	const _json = JSON.parse(jsonStr);
	return json(_json);
}

```

```typescript:src/routes/+page.server.ts
import type { PageServerLoad } from './$types';
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
const s3 = new S3Client({
	region: 'ap-northeast-1',
});

export const load: PageServerLoad = async ({}) => {
	// getobject
	const params = {
		Bucket: 'amplify-role-test',
		Key: 'mock.json',
	};
	const command = new GetObjectCommand(params);
	const response = await s3.send(command);
	const jsonStr = await response.Body.transformToString();
	const _json = JSON.parse(jsonStr) as { message: string };
	return {
		mock: _json,
	};
};
```

```svelte:src/routes/+page.svelte
<script lang="ts">
	let { data } = $props();

	async function fetchS3() {
		const response = await fetch('/api/mock');
		const json = await response.json();
		alert(JSON.stringify(json, null, 2));
	}
</script>

<!-- SSR -->
<p>mock.json:</p>
<pre>{JSON.stringify(data, null, 2)}</pre>

<!-- Fetch API via API Routes -->
<button onclick={fetchS3}>Fetch S3</button>
```

```javascript:svelte.config.js
import adapter from 'amplify-adapter';
// 以下略
```

コードはGitHubにあげておきましょう。上記のコードは：

1. `/api/mock`に対するGETリクエストの実装
2. `/`で返却するページの実装

がそれぞれ含まれています。1はREST-APIのことで、Fetch APIで動作を検証しています。2はSSRのことで、`/`にアクセスした際に返却されるHTMLの内容にS3の内容が含まれています。

https://github.com/MIERUNE/svelte-amplify-ssr-role


## Amplifyアプリケーションの作成

```yaml:amplify.yml
version: 1
frontend:
    phases:
        preBuild:
            commands:
                - 'npm install -g pnpm'
                - 'pnpm install'
        build:
            commands:
                - 'pnpm build'
                - 'cd build/compute/default/'
                - 'npm i --production'
    artifacts:
        baseDirectory: build
        files:
            - '**/*'
    cache:
        paths:
            - 'node_modules/**/*'
```

### コンピューティングロールの設定

![Screenshot 2025-02-19 at 9.43.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/d6e236cf-00bb-4762-ac8b-64ec51fd6793.png)


「アプリケーションの設定」に「IAMロール」が追加されているので、「コンピューティングロール」に先ほど作成したIAMロールを設定します。ブランチごとに区別することも出来るようですね。

## 動作検証

デプロイされたアプリケーションにアクセスします。

![Screenshot 2025-02-19 at 9.45.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/8aeb0a55-98fa-42cb-8b73-dae200399cb5.png)*S3から取得したJSONでSSRされている*

![Screenshot 2025-02-19 at 9.45.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/1f47b3ba-1941-4fe0-a453-ef99ae28f04d.png)*API Routesに対するFetch APIの結果。S3にあるファイルを取得できている。*

### コンピューティングロールの設定を解除してみる

![Screenshot 2025-02-19 at 9.49.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/bd45c4ea-9d54-4740-8b6e-7ff99bc92062.png)

再度デプロイして（重要、デプロイ時にIAMロールが決定されるもよう）、アプリケーションにアクセスする

![Screenshot 2025-02-19 at 9.52.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/848b3cc0-5cdc-4aea-863a-017941aac81b.png)

エラーが発生するようになる、ログを確認する

![Screenshot 2025-02-19 at 9.54.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/ddd136a2-e2a3-471c-845e-dd284e6e0983.png)

クレデンシャルが読めてなさそうなので、期待どおりの挙動

## 終わりに

全俺が待ってたアップデートゆえ取り急ぎ検証回しました
Amplify + SSRについては以下のようなトークを持ったこともありました

https://docs.google.com/presentation/d/15fUcmbF02-K_crddDpyMFiBpVrFbpH22_jKUaOcJJs8/edit?usp=sharing

そこでも「実行ロール」が利用できないことには言及していました
今回のアップデートにより、Amplify Hostingの活用の幅がより広がるものと期待しています