今まで、Amplifyが用意する以外のリソースを使いたい場合は、 <project_root>/amplify/backend/api/<API name>/stacks
ディレクトリの中に、CloudFormation(CFn)ファイルを作成して対応してきました。
AWS AppSync + Amplify JavaScript + CustomResourcesで、既存のDynamoDBなどをDatasourceとしたリゾルバーを作成する - メモ的な思考的な
それ以外の方法がないかを探したところ、以下のissueコメントに、カスタムカテゴリを作っても追加できる旨が記載されていました。
Support for custom Resources/CloudFormation templates · Issue #80 · aws-amplify/amplify-cli
そこで、issueに書かれた方法で試してみたため、メモを残します。
ちなみに、「カスタムカテゴリ」と表現するのが正しいのかは微妙ですが、
$ amplify --help | Category | | ------------- | | analytics | ...
と、元々あるAnalyticsやAPIはカテゴリ(Category)と呼ぶようです。
そのため、この記事ではそれにならって、今回自作するカテゴリのことをカスタムカテゴリと呼ぶことにします。
目次
- 環境
- カスタムカテゴリを追加
- amplify env checkout
- amplify pushして状況確認
- IAMポリシーの作成と未承認ロールへの割り当て
- カスタムカテゴリを使わず、APIのカスタムリソースとして作成
- まとめ
- ソースコード
環境
今回は amplify init & amplify api add で、APIモジュールを追加した後の環境で、
- CognitoのIDプールを作成し、amplify init で作成した承認済/未認証ロールを紐付ける
- AppSync GraphQLへのアクセスを許可するIAMポリシーを作成し、未承認ロールに割り当てる
というカスタムリソースを書いてみます。
amplify init & amplify api add は以下のような感じです。
# 初期化 $ amplify init Note: It is recommended to run this command from the root of your app directory ? Enter a name for the project CustomCategory ? Enter a name for the environment dev ? Choose your default editor: Visual Studio Code ? Choose the type of app that you're building javascript Please tell us about your project ? What javascript framework are you using none ? Source Directory Path: src ? Distribution Directory Path: dist ? Build Command: npm run-script build ? Start Command: npm run-script start Using default provider awscloudformation For more information on AWS Profiles, see: https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html ? Do you want to use an AWS profile? Yes ? Please choose the profile you want to use default # APIの追加 $ amplify api add ? Please select from one of the below mentioned services GraphQL ? Provide API name: CustomCategoryAPI ? Choose an authorization type for the API API key ? Do you have an annotated GraphQL schema? No ? Do you want a guided schema creation? No ? Provide a custom type name MyType
カスタムカテゴリを追加
ディレクトリの作成
<project_root>/amplify/backend/
ディレクトリの下に、 identity
ディレクトリを作成します。
CFnテンプレートの作成
今回はIDプールとRoleのマッピングを行うため、
の2つのResourceが必要です。
そのため、このResourceが含まれるファイルを template.json
として、上記の identity
ディレクトリに作成します。
IDプールの作成
まずは AWS::Cognito::IdentityPool
のリソースを作成します。
amplifyのenvとIDプールの名称をパラメータとして受け取り、CoginitoのIDプールを作成します。
AllowUnauthenticatedIdentities: true
としてありますが、IDプールの 認証されていない ID に対してアクセスを有効にする
にチェックを入れてみたかっただけで、特に深い意味はありません。
{ "AWSTemplateFormatVersion": "2010-09-09", "Parameters": { "env": { "Type": "String", }, "IdentityPoolName": { "Type": "String" }, ... }, "Resources": { "IdentityPool": { "Type": "AWS::Cognito::IdentityPool", "Properties": { "IdentityPoolName": { "Fn::Join": [ "__", [ { "Ref": "IdentityPoolName" }, { "Ref": "env" } ] ] }, "AllowUnauthenticatedIdentities": "true" } },
amplify initで作成されたものをnested-cloudformation-stack.ymlで確認し、ロールを割り当て
AWS::Cognito::IdentityPoolRoleAttachment
を設定するには、
- 認証されていないロール
- 認証されたロール
が必要です。
今回は、Amplifyの動きと同じく、amplify init で作成される認証されていないロールと認証されたロールを、自作のIDプールに割り当ててみます。
CFn内でamplify initで作成されるロールを参照する方法を探したところ、 <project_root>/amplify/backend/awscloudformation/nested-cloudformation-stack.yml
に記載されているリソースを使えばよさそうでした。
今回は、
- AuthRole
- UnauthRole
のArnを Fn::GetAtt
で参照します。
ただ、CFnのテンプレート内では参照できなかったため、
- CFnテンプレート (template.json) は
Parameter
で外部から値を受け取る - CFnテンプレートに値を渡すためのファイル
parameters.json
を作成し、その中でFn::GetAtt
を使ってロールのArnを取得・設定する
としました。
関係する部分を抜粋したのは以下です。
template.json
{ "AWSTemplateFormatVersion": "2010-09-09", ... "Parameters": { ... "AuthRoleArn": { "Type": "String" }, "UnauthRoleArn": { "Type": "String" } }, "Resources": { ... "IdentityPoolRoleMap": { "Type": "AWS::Cognito::IdentityPoolRoleAttachment", "Properties": { "IdentityPoolId": { "Ref": "IdentityPool" }, "Roles": { "unauthenticated": { "Ref": "UnauthRoleArn" }, "authenticated": { "Ref": "AuthRoleArn" } } }, "DependsOn": "IdentityPool" } }, ... }
parameters.json
{ "IdentityPoolName": "CustomIdentityPool", "AuthRoleArn": { "Fn::GetAtt": [ "AuthRole", "Arn" ] }, "UnauthRoleArn": { "Fn::GetAtt": [ "UnauthRole", "Arn" ] } }
amplify env checkout
このままの状態で amplify push しようとしても、まだAmplify CLIにはAPIしか認識されていません。
$ amplify status Current Environment: develop | Category | Resource name | Operation | Provider plugin | | -------- | ----------------- | --------- | ----------------- | | Api | CustomCategoryAPI | Create | awscloudformation |
そのため、amplify env checkout を行い、Amplify CLIに認識してもらいます。
# 環境の確認 $ amplify env list | Environments | | ------------ | | *develop | # チェックアウト $ amplify env checkout develop # 認識された $ amplify status Current Environment: develop | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Api | CustomCategoryAPI | Create | awscloudformation | | Identity | CustomIdentityPool | Create | awscloudformation |
また、amplify push などの情報を持っている <project_root>/amplify/backend/amplify-meta.json
も更新されました。
{ "providers": { "awscloudformation": { ... } }, "api": { "CustomCategoryAPI": { ... } } }, // 追加された "identity": { "CustomIdentityPool": { "providerPlugin": "awscloudformation" } } }
amplify pushして状況確認
カスタムカテゴリが認識されたため、amplify push すると、成功しました。
$ amplify push Current Environment: develop | Category | Resource name | Operation | Provider plugin | | -------- | ------------------ | --------- | ----------------- | | Api | CustomCategoryAPI | Create | awscloudformation | | Identity | CustomIdentityPool | Create | awscloudformation | ? Are you sure you want to continue? Yes GraphQL schema compiled successfully. ? Do you want to generate code for your newly created GraphQL API Yes ? Choose the code generation language target javascript ? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] 2 ... ✔ All resources are updated in the cloud GraphQL endpoint: https://host.appsync-api.region.amazonaws.com/graphql GraphQL API KEY: xxx
CognitoのIDプールを確認したところ、たしかに作成されていました。
IAMポリシーの作成と未承認ロールへの割り当て
まずは、AppSync GraphQLへのアクセスを許可するIAMポリシーを作成します。
IAMポリシーでGraphQLへのアクセスを許可するには "arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiID}/types/Mutation/*
のようなResourceに対して設定する必要があります。
ただ、この中の apiID
については、カスタムカテゴリの中では取得できませんでした。一方、APIカテゴリの中では取得できました。
そのため、 <project_root>/amplify/backend/api/<API name>/stacks/
ディレクトリの中に IAMRole.json
を作成します。
この中で
- パラメータ
AppSyncApiId
を参照し、AppSyncのAPI IDを取得 - パラメータ
UnauthRoleName
を参照し、未承認ロールに対しポリシーを割り当て
を行います。
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "AppSync GraphQL Policy", "Metadata": {}, "Parameters": { "AppSyncApiId": { "Type": "String", "Description": "The id of the AppSync API associated with this project." }, "env": { "Type": "String", "Description": "The environment name. e.g. Dev, Test, or Production", "Default": "NONE" }, "UnauthRoleName": { "Type": "String" } }, "Resources": { "AppSyncGraphQLPolicy": { "Type": "AWS::IAM::ManagedPolicy", "Properties": { "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ { "Fn::Sub": [ "arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiID}/types/Query/*", { "apiID": { "Ref": "AppSyncApiId" } } ] }, { "Fn::Sub": [ "arn:aws:appsync:${AWS::Region}:${AWS::AccountId}:apis/${apiID}/types/Mutation/*", { "apiID": { "Ref": "AppSyncApiId" } } ] } ] } ] }, "ManagedPolicyName": { "Fn::Join": [ "-", [ "AppSyncGraphQLPolicy", { "Ref": "env" } ] ] }, "Roles": [ { "Ref": "UnauthRoleName" } ] } } }, "Outputs": { "EmptyOutput": { "Description": "An empty output. You may delete this if you have at least one resource above.", "Value": "" } } }
結果確認
未認証のロールに、AppSyncのGraphQLアクセスを許可するIAMポリシー AppSyncGraphQLPolicy-develop
が割り当てられていました。
Cognito IDプールでの未認証ロール
ロールに割り当てられたポリシー
カスタムカテゴリを使わず、APIのカスタムリソースとして作成
ちなみに、今回作成したCognito IDプールのCFnファイルですが、カスタムカテゴリを使わなくても、APIカテゴリのカスタムリソースとすることも可能です。
<project_root>/amplify/backend/api/<API name>/build/cloud-formation-template.json
を見ても、APIのカスタムリソースのパラメータが各リソースを参照できています。
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "An auto-generated nested stack.", "Metadata": {}, "Parameters": { ... "AuthRoleArn": { "Type": "String" }, "UnauthRoleArn": { "Type": "String" } }, "Resources": { "GraphQLAPI": { "Type": "AWS::AppSync::GraphQLApi", ... }, "GraphQLAPIKey": { "Type": "AWS::AppSync::ApiKey", ... }, "GraphQLSchema": { "Type": "AWS::AppSync::GraphQLSchema", ... }, "MyType": { "Type": "AWS::CloudFormation::Stack", ... }, "CustomResourcesjson": { "Type": "AWS::CloudFormation::Stack", ... }, "IAMRolejson": { "Type": "AWS::CloudFormation::Stack", ... }, "templatejson": { "Type": "AWS::CloudFormation::Stack", "Properties": { "Parameters": { "env": { "Ref": "env" }, "StackIdentityPoolName": { "Ref": "StackIdentityPoolName" }, "AuthRoleArn": { "Ref": "AuthRoleArn" }, "UnauthRoleArn": { "Ref": "UnauthRoleArn" } },
まとめ
以上より、AWS Amplifyでカスタムリソースを作る場合は
<project_root>/amplify/backend/awscloudformation/nested-cloudformation-stack.yml
<project_root>/amplify/backend/api/<API name>/build/cloud-formation-template.json
などを見て使えるリソースを探しつつ、
- APIの中のstacksの中にCFnファイルを作成
- カスタムカテゴリを作成し、その中にCFnファイルを作成
のどちらを行い、必要なパラメータは parameters.json
に記載すれば良いことが分かりました。
ソースコード
Githubに上げました。ディレクトリ custom_category
以下が今回のファイルです。
https://github.com/thinkAmi-sandbox/AWS_AppSync_Amplify-sample