プレビュー版のAmplify Gen2の、リソースのオーバーライドを試してみました。
Amplify Gen2のオーバーライドの概要
Amplifyが生成するcfnテンプレートを書き換えます。存在しない機能を追加したい時に使用します。
既存のAmplify(Gen1)はツールファーストであり、豊富なメニューから必要な機能を選択することで、望んでいる機能を簡単に追加することができました。 とはいえ、全ての機能に対応は難しいので、amplify overrideというコマンドを用意し、必要な場合にAmplifyが生成するリソースを書き換えるためのescape hatcheを用意していました。
Amprify Gen2は、設計方針がコードファーストになり、豊富なメニューは無いと予想されるので、Gen1よりリソースの書き換えは増えそうです。 backend.tsというファイルを起点に、作成されたAmplifyリソースの呼び出し、書き換えを行います。書き換えにはCDKを使用します。
Constructへのアクセス
Amplify Gen2はL3 Construct(最高レベルの抽象化)として定義されており、escape hatcheより、WrapされているL2、L1のConstructにアクセスできます。 具体的には、defineBackendという関数でAmplifyリソースを定義し、それぞれのサービスからパスを辿ります。 例:Data(Appsync)の場合
- L2 Construct:backend.data.resources.graphqlApi
- L1 Construct:backend.data.resources.cfnResources.cfnGraphqlApi
このConstructへのパスさえ覚えておけば、迷わなさそうです。 ただ、独自の設定項目があったり、一部の項目が隠されていたり、Cfnでは書き換えられるが、Amplifyでは書き換えられない項目などもあり、CDKの知識+αが必要だ、という印象です。
オーバーライドしてみた
Docsにはリソース書き換えのサンプルがあり、非常に参考になります。
リソースのオーバーライドのDocs
https://docs.amplify.aws/gen2/build-a-backend/auth/override-cognito/ https://docs.amplify.aws/gen2/build-a-backend/data/override-resources/ https://docs.amplify.aws/gen2/build-a-backend/add-aws-services/overriding-resources/
他、メニューの各所にサンプルが書かれています。 書かれていないものを含めて、自分で試してみました。
ユーザーグループの追加(2024/4/23 修正あり)
backend.tsで、custom resourceを使って定義する必要はありません。
defineAuthのパラメータとして定義が可能(groupの作成に加え、groupごとののIAM Roleも作成してくれる)なため、auth/resource.tsで設定を行って下さい。、
export const auth = defineAuth({
loginWith: {
....
},
groups: ["worker", "admin"], // groupを定義
});
※記事初稿時、Amplify Dataではユーザーグループを定義する方法はDocsには無さそうだ、としていましたが、defineAuthで設定ができるため、差し替えました。
パスワードポリシー、ユーザ作成禁止、ゲストアクセス無効化
一部Docs内にサンプルがあります。
amplify/backend.ts
// Change Password policie
const { cfnUserPool, cfnIdentityPool } = backend.auth.resources.cfnResources;
cfnUserPool.addPropertyOverride("Policies", {
PasswordPolicy: {
MinimumLength: 6,
RequireLowercase: false,
RequireNumbers: true,
RequireSymbols: false,
RequireUppercase: false,
TemporaryPasswordValidityDays: 20,
},
});
// Prevent user create
cfnUserPool.addPropertyOverride(
"AdminCreateUserConfig.AllowAdminCreateUserOnly",
true,
);
// Do not allow guest access
cfnIdentityPool.addPropertyOverride("AllowUnauthenticatedIdentities", false);
PasswordPolicyのオーバーライドは、Cognitoには反映されるものの、AmplifyConfiguration.jsonに反映されないので、AWS側とクライアントでPasswordPolicyの齟齬が生じてしまう現象を確認しています。この問題はsandboxとデプロイした環境に影響します。報告済なので、すぐ修正されそうですが・・
暫定処置として、生成されたamplifyConfiguration.jsonを書き換えるスクリプトを作成し、amplify.ymlのbuildセクションへ組込んでいます。
amplify-configuration-override.js
const fs = require("fs");
const filePath = "./amplifyconfiguration.json";
try {
const data = fs.readFileSync(filePath, "utf8");
const amplifyconfiguration = JSON.parse(data);
amplifyconfiguration.aws_cognito_password_protection_settings = {
passwordPolicyMinLength: 6,
passwordPolicyCharacters: ["REQUIRES_NUMBERS"],
};
try {
fs.writeFileSync(filePath, JSON.stringify(amplifyconfiguration), "utf8");
} catch (err) {
console.error(err);
}
} catch (err) {
console.error(err);
}
amplify.yml
// 中略
build:
commands:
# force aws_cognito_password_protection_settings override
- 'node amplify-configuration-override.js'
- 'npm run build'
Dynamo Stream
最近まで、正規の手順でDyamoDBのTableNameへのアクセスができなかった為、Amplify内でIaCを完結させることが難しい状態でしたが、改善され、オーバーライドによりDynamo Streamを定義できるようになりました。
ポイントとして、DynamoDBはAmplifyのリソースではなく、AppSyncのリソースの(DataSource)として定義されているので、アクセスがやや遠回りになるところでしょうか。
amplify/backend.ts
export const backend = defineBackend({
auth, //スキーマ定義で、Hogeというモデルが存在している、とします
data,
dynamoStreamFunction, //事前にFunctionを作成しておく
});
// activate dynamoDB stream
// hoge
backend.data.resources.cfnResources.amplifyDynamoDbTables.Hoge.streamSpecification =
{
streamViewType: StreamViewType.NEW_IMAGE,
};
// add stream trigger
backend.dynamoStreamFunction.resources.lambda.addEventSourceMapping(
"EventSourceMappingDynamo",
{
batchSize: 1,
eventSourceArn: backend.data.resources.tables.Hoge.tableStreamArn,
startingPosition: StartingPosition.LATEST,
},
);
// grant stream read
backend.data.resources.tables.Hoge.grantStreamRead(
backend.dynamoStreamFunction.resources.lambda,
);
その他、DyanmoDBのbillingModeや、provisionedThroughput、ttl、Cognitoのカスタム属性など、公式にサンプルがありますので、気になる機能は一度公式をチェックされることをお勧めします。
注意点
Functionを作成した時、AppSyncのDataSourceであるDynamoDBのTableNameやArnを環境変数として渡すケースが存在すると思うのですが 当然ながら、Cfnの作成順序としては、AppSync→Functionとなります。
ここでAppSync内でLambdaによる関数リゾルバ(Function)を作成した場合、AppSyncを作成するためにdefineFunctionを実行する為、Cfnの作成順序がFunction→AppSync→Functionとなってしまい、循環参照が発生します。
amplify/data/resource.ts
import {
type ClientSchema,
a,
defineData,
defineFunction // 1.Import "defineFunction" to create new functions
} from '@aws-amplify/backend';
// 2. define a function
const echoHandler = defineFunction({
entry: './echo-handler/handler.ts'
})
const schema = a.schema({
EchoResponse: a.customType({
content: a.string(),
executionDuration: a.float()
}),
根本的な原因は、defineFunctionを複数実行して、複数の関数を作成したとしても、出力されるCfnテンプレートは1つになってしまうので、関数ごとのStackの作成順序が同じになる為と思われます。
関数毎にCfnテンプレートを分けて出力すると、この問題も解決するかと考えているのですが、defineFunctionにそのようなオプションは見つかりませんでした。
特殊なパターンかもしれませんが、注意する必要があると感じました。
まとめ
開発中のリソースのオーバーライドですが、CDKの理解があれば、既存のAmplifyよりも使いやすい印象があります。