yasutomogのブログ

Software Engineerの雑記

AmplifyプロジェクトでCognito送信者のLambdaトリガーを設定してみた

概要

  • Amplifyプロジェクトで普通にAuthを追加してMFA対応していた
  • SMS認証コードとEMail認証コードはそれぞれCognito標準機能を使っていたが、TwilioやSendGridなどの別サービスで送るための対応が必要になった
  • Cognitoから認証コードを送るときに、カスタム送信できるような機構が用意されているので、それを利用

ドキュメントや参考になるブログ

docs.aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

qiita.com

対応方法

  • AmplifyのCloud Formationに組み込みたかったが、現状、この機能はCloud Formationに対応されていないらしく、別途設定が必要になる
  • KMS(AWSコンソール)から、「カスタマー管理型のキー」を作成する
    • 特に考えずそのまま作成する
  • Lambda Layerを作っておく
    • package.jsonに以下を追加しておく。実際の認証コード送信に必要であれば、「@sendgrid/mail」や「twilio」も追加しておく
  • Lambdaを用意する
    • 公式ドキュメントにあるものを流用すれば良い
    • handler関数では、Cognitoから発火されたイベント名をみて、処理をハンドリングしてあげる(TwilioやSendGridを使って認証コード送信を書く)
  • LambdaにKMSの権限をアタッチしてあげる
    • Lambdaの[設定]→[アクセス権限]→[実行ロール]からKMSの権限をアタッチする
  • Amazon Cognitoサービスプリンシパルに、Lambda関数を呼び出すための、cognito idp.amazonaws.com へのアクセス権を付与

    aws lambda add-permission \
    --function-name <作成したLambdaのARN> \
    --statement-id "CognitoLambdaInvokeAccess" \
    --action lambda:InvokeFunction \
    --principal cognito-idp.amazonaws.com
    
  • Cognitoユーザープールを更新して、Lambdaトリガーを追加する

    aws cognito-idp update-user-pool \
    --user-pool-id <CognitoユーザープールID>  \
    --lambda-config "CustomSMSSender={LambdaVersion=V1_0,LambdaArn=<作成したLambdaのARN>},CustomEmailSender={LambdaVersion=V1_0,LambdaArn=<作成したLambdaのARN>},KMSKeyID=<作成したKMSのARN>" \
    --profile <AWSのプロファイル>
    
  • 間違ってLambdaトリガーを追加したときに解除するコマンド

    aws cognito-idp update-user-pool \
    --userpool-id <userpool_id> \
    --lambda-config "{}"
    

ハマったところ

たまに認証コードが同じユーザーに2度送られる事象が発生

  • SMS認証コードをTwilioのVerify APIを使って送信していたが、時たま、2度送られていた
  • 設定したLambdaトリガーのログを見ていると、以下が出力されており、失敗したLambdaの約1分後に同じイベントが発火されていた。
  • Verify API処理が3秒超える場合がたまにあり、そのときに再度Lambdaが実行され2度送信されていることを確認

    Task timed out after 3.00 seconds
    

対応方法

  • Lambdaのタイムアウトをデフォルトの3秒から10秒に変更し、メモリも256mbに変更
  • 設定値は、利用するサービス仕様に合わせる

www.twilio.com

カスタムLambdaトリガーが実行されない事象発生

  • aws cognito-idp update-user-pool コマンドでLambdaカスタムトリガーを紐付けても、Lambdaが呼ばれない
  • AmplifyでAuth追加した時点では、Cognitoの「どの属性を確認しますか?」の項目では、「Eメールまたは電話番号」を選択していたが、コマンド実行すると、「検証なし」に変更されていた。

対応方法

  • aws cognito-idp update-user-pool コマンドのオプションに auto-verified-attributes を追加
  • 上記オプションには、 sms-configuration が必要になる
    • sms-configuration に指定には、「SMS 送信に使用する IAM ロールの ARN」と「SMS 送信に使用する IAM ロールの、信頼ポリシーに設定されている sts:ExternalId の値」が必要になる。
    • このIAMロールは、Amplifyで適切にAuth追加していると既に作成されているので、それを使う。
    • Cognito(AWSコンソール)の「MFAそして確認」メニューの一番下に「新規ロール名」があるので、IAM(AWSコンソール)から検索して、信頼関係のタブを開くと「sts:ExternalId」の値が確認可能
    aws cognito-idp update-user-pool \
    --user-pool-id <CognitoユーザープールID> \
    --lambda-config "CustomSMSSender={LambdaVersion=V1_0,LambdaArn=<作成したLambdaのARN>},CustomEmailSender={LambdaVersion=V1_0,LambdaArn=<作成したLambdaのARN>},KMSKeyID=<作成したKMSのARN>" \
    --profile <AWSのプロファイル> \
    --auto-verified-attributes {email,phone_number} \
    --sms-configuration SnsCallerArn=<SMS 送信に使用する IAM ロールの ARN>,ExternalId=<SMS 送信に使用する IAM ロールの、信頼ポリシーに設定されている sts:ExternalId の値>
    

Next.js on Amplifyの503エラーの続き

概要

yasutomo.hatenablog.com

  • 上記ブログの続き
  • AmplifyでNext.jsをデプロイしたタイミングだと問題なく動くが、あるタイミングからCloud Frontの503エラーになっていた
  • 上記事象が発生するのは、同じAmplifyプロジェクト上で同じソースコードで構築したhostingでも再現するものとしないものがあった

原因

  • Next.js on Amplifyで構築される、Lambda@Edge に SQS の権限がアタッチされないことがある
  • SQS の権限がアタッチ「されるとき」と「されないとき」の2パターンあるのがポイントで、ケースは以下
    • Amplify hosting作成時、はじめてデプロイするタイミングのソースコードがISR対応している:SQSの権限がアタッチされる
    • Amplify hosting作成時、はじめてデプロイするタイミングのソースコードがISR対応していない:SQSの権限がアタッチされない
  • ISRのキャッシュが切れて裏でページ生成されるタイミングで、Lambda@edge から SQS に SendMessage しようとしたときに権限がないと503エラーとなる

対処方法

  • 解決するには、以下2つがある。(現状はどちらも試していて、どちらのケースでもうまく動いている)
    • Amplify hostingを作り直す
      • 事象が発生しているhostingを一度削除し、改めて作り直す
      • まだサイト公開前とかであれば、こちらの方法がシンプルで良さそう
    • 事象が発生している Lambda@edge にSQSの権限をアタッチする
      • 雑にやるなら、AmazonSQSFullAccess ポリシーを付ける。正しくやるなら、"Resource": "arn:aws:sqs:us-east-1:<アカウント ID >:", の Actionに "sqs:SendMessage" を指定する。
    • Lambda@edge や SQS のIDについては、Amplifyコンソールのデプロイ部分のログに出力されている
    • Lambda@edge に権限アタッチ後、改めてデプロイしても自分で割り当てた権限はついたままとなっている(Amplify側の挙動の保証はない?)

所感

  • プロジェクト構築時は、シンプルな状態から始めることが多いので、途中からISR対応ページなど追加したときに気をつける
  • Amplifyコンソールのデプロイフェーズのログが重要
    • ここに出力されている、CloudFront や Lambda@edge のIDから詳細ログを追う力が必要

調査のときに見ていたGitHubとドキュメント

github.com

github.com

Next.js on Amplifyの503エラー

yasutomo.hatenablog.com

yasutomo.hatenablog.com

また雑多なメモを残しておきます。

概要

  • Amplify上でNext.jsを動かすときに503エラーが発生
  • ネットで調べていると色々と情報はでてくるが、自分がハマったケースは大きく2つ
    • Next.jsでISRをしているときに発生
    • Amplifyのアクセスコントロールを適用しようとして発生

Next.jsでISRをしているときに発生

  • 調べ始めたとき、以下2つの日本語記事が参考になりました。

zenn.dev

dev.classmethod.jp

  • はじめてAmplify hosting するときに、Amplify CLI と Next.js verisonのバージョンをどちらも latest にするのがポイント

f:id:yasug:20211203001942p:plain

  • 上記対応はプロジェクト構築時にも見て知っていたので正しく設定していましたが、それでも503エラーが発生するケースがありました。
  • ちょっとたちが悪いのが、ページ表示したタイミングで常に発生するわけではなく、何かしらの契機で発生していて、調査が難かしかったです。
  • それから調査を続けてて、参考になったissueが以下です。

github.com

github.com

  • 1つ目はwebpackのバージョンで、5をfalseにして4で動かすというものです。自分の場合これでは解決に至らず、結局は5に戻しています。また、このオプションは近い将来に無効になるので、可能であれば使わずに解決したいところでした。
  • 2つ目は、Amplifyの環境変数にある、_LIVE_UPDATES の値でした。このissueにもある通り、verisonに10 と指定されていました。ビルド設定のバージョンはlatestになっているのを確認していたので、こちらはissueを見るまで気づきませんでした。
  • 10になっている部分をlatestに変更し、改めてビルドし直してみましたが解決はしなかったです。ダメ元で、一度hostingを削除して改めて作り直したところ、_LIVE_UPDATES のNext.jsのバージョンも以下のようにすべてlatestになっており、その後は503エラーは発生していないです。

f:id:yasug:20211203003030p:plain

Amplifyのアクセスコントロールを適用しようとして発生

  • Amplifyにはアクセスコントロールのメニューから簡単にBasic認証かけれる便利機能がありますが、Next.jsでSSR対応している場合には正しく動きませんでした。
  • 以下の公式ドキュメントにも、その旨が掲載されていて読んだ記憶があるのですが、いま改めて見ると載ってません。

docs.aws.amazon.com

  • このブログを書く前に試してみたところ、普通に動いているように見えます。その当時は、アクセスコントロールの設定自体は有効になり、画面を開くとID/PASSの入力を求められるのですが、正しい値を入れても認証が通らないという事象でした。
  • そこからAmplifyの機能に頼らず、自分でアクセス制御しようと思い、Amplifyのデプロイ先のCloudFrontに関数を作成しました。Basic認証ではなく、以下のような、IP制限するようなプログラムを設定していました。(CloudFrontの関数ではconstが使えなくて地味にハマりました。)
  function handler(event) {
    var request = event.request;
    var headers = event.request.headers;
    var clientIP = headers['x-forwarded-for']['value']

    var IP_WHITE_LIST = [];
    var isPermittedIp = IP_WHITE_LIST.includes(clientIP);

    if (isPermittedIp) {
        return request;
    } else {
        var response = {
            statusCode: 403,
            statusDescription: 'Forbidden',
        }
        return response;
    }
}
  • これも最初うまく動いていたのですが、何かを契機に503が多発するようになることがあったのと。Amplifyでビルドすると、関数が外れてしまう問題を確認しているところでした。
  • いまはアクセスコントロールがうまく動いていそうなので、一旦このまま経過観察していきたいと思います。

感想

  • 何か困ったら大抵はGitHubでやり取りされているので、そこで解決案やアプローチ方法が見つかることが多い
  • Amplifyのupdateが早いので、その時点では難しくても、最悪の逃げ道だけ用意しておけば、それを使わずとも環境側の変更で解決することが多い

AmplifyでNext.jsをSSRしたときのビルドログ出力先

yasutomo.hatenablog.com

昨日、上の記事を書きましたが、Advent Calendarにまだ空きがあるので、雑多なメモを残しておきます。

概要

  • Amplify環境でNext.jsのアプリケーションを構築
  • Next.jsでは、ページによってSSG、ISR、SSRを使い分けている

課題

  • SSRしているページの描画が遅いことが発覚しログ確認しようと思った
  • SSRやISRしているときに、getStaticPropsgetServerSideProps 関数でconsole.log出力していたが、どこで確認できるか分からなかった

解決方法

f:id:yasug:20211202001705p:plain

  • Amplify Console のFrontend environments から デプロイをクリック
  • 以下のようにCloudFrontやLambda@Edgeの識別子を確認することができる
2021-12-01T07:37:02 [INFO]: Beginning deployment for application aaaaaaa, branch:dev, buildId aaaaaa
2021-12-01T07:37:05 [INFO]: Deploying SSR Resources. Distribution ID: bbbbbbb. This may take a few minutes...
2021-12-01T07:37:05 [INFO]: Deployed the following resources to your account:
2021-12-01T07:37:05 [INFO]: - CloudFront Domain ID: ccccccc
2021-12-01T07:37:05 [INFO]: - SSR Lambda@Edge: dddddd-dddddd
2021-12-01T07:37:05 [INFO]: - Image Optimization Lambda@Edge: eeeeee-eeeeee
2021-12-01T07:37:05 [INFO]: - ISR Lambda: ffffff
2021-12-01T07:37:05 [INFO]: - ISR SQS Queue: gggggg
2021-12-01T07:37:05 [INFO]: - S3 Bucket: hhhhhh
2021-12-01T07:37:05 [INFO]: Deployment complete
  • AWSコンソールで、バージニア北部(us-east-1)リージョン からLambdaの識別子でフィルタし、そこからCloudWatchをたどることで、Next.jsのビルド時のログを確認することが可能

気になった点

  • SSRのログを確認しようとしたときに、「 SSR Lambda@Edge」の値からたどっても見つからなく、ISR Lambda の方を確認すると、SSRページのログも出力されていた。

複数のAmplifyプロジェクトを使った開発方法

トライビートという会社の札幌オフィスで開発全般やってます。
気づいたらもう12月ですね。
AWS Amplify Advent Calendar 2021のカレンダー2は、まだ空きが多いので、今年開発していく中で得た知見をメモとして残しておきます。

qiita.com

概要

  • Amplifyの複数envについては色々な記事で情報が公開されている
  • 複数のAmplifyプロジェクトを接続する記事をあまり見かけなかったので試行錯誤した
  • 実際のプロジェクトでやりたかったこと
    • 管理者サイトと一般ユーザー向けサイトを別々のAmplifyでプロジェクトで管理したい
    • AmplifyのBackend(Auth、API、Function、Storage)は共有したい
  • サブドメインとかで別々にhostingしたいんだけど、Backendはそもそも同じデータ層を扱うので、APIやFunction等で共有できるものは一元管理したい。

f:id:yasug:20211201001010p:plain

どう解決したか

  • 一般ユーザー向けサイトを普通にAmplifyプロジェクト(FrontendとBackend)で構築。
  • 管理者サイトは、Backendを用意せず、一般ユーザー向けサイトのBackendを参照させる流れで構築。

f:id:yasug:20211201002152p:plain

  • 具体的な方法は下記の公式ドキュメントが参考になりました。

docs.aws.amazon.com

  • ざっくりとした流れ
    1. まず最初に、一般ユーザー向けサイト(Backendを含むAmplifyプロジェクト)を通常のAmplifyプロジェクト構築の流れで作成
    2. 次に管理者向けサイトはフロントエンド部分のみ実装したプロジェクトを構築し、amplify init 等はせずに、 amplify pull をしてbackendに一般ユーザー向けサイトを選択する。
    3. Backendを紐付けるときに、「Do you plan on modifying this backend?」と聞かれるのは、「No」を選択し、管理者向けプロジェクトからはBackendの変更をできないようにする。
    4. あとは、ドキュメントに従い、Amplify Console上でhostingの設定をしていく

tips

  • 管理者向けサイトを amplify delete すると、一般ユーザー向けサイトのBackendも削除される。管理者向けサイト側のAmplifyプロジェクト(hostingのみ)削除されることを期待していましたが、そうではなく、接続しているBackendも削除されました。
  • Gitは別リポジトリで管理するようにした。モノレポでディレクトリで分ける構成も検討しましたが、管理者サイトのみ更新など、片一方のみビルド〜デプロイみたいなことができなそうだったので、リポジトリ毎分けるようにしました。
  • 一般ユーザー向けサイトで追加したAPIやFunctiionを、管理者サイトのフロントエンドから利用するとき、一手間必要でした。具体的には下記。
    • aws-export.js を一般ユーザーサイトから管理者サイトへコピーしてくる
    • schema.json を一般ユーザーサイトから管理者サイトへコピーしてくる
      • 以下のコマンドで最新のschema.json が取得できそうであるが、エラーとなったので手作業で頑張ってた
      • $ aws appsync get-introspection-schema --api-id <対象の AppSync の API ID> --format JSON schema.json
    • amplify codegen を叩き、API.tsやmutations.tsやqueries.ts などを生成する

感想と課題

  • APIやFunctionを共有できるのはいいが、管理者サイトでのみ必要なAPIやFunctionを追加するときに、一般ユーザーサイト側のコードを修正するのが結構しんどい。
  • 管理者サイトとプロジェクトを分けるというのは、ユースケースとしてはよくありそうなので、みんながどういう風にやっているか気になる
  • 今回の構成と直接関係があるか調査中だが、Functionを追加したときに、Lambdaに環境変数を付与すると、管理者向けサイトのAmplifyビルドがコケる。管理者サイトは参照のみのため、しっくりこないが、なぜかそうなるので調べているところ。

microCMSでの画面操作とWebhookのパラメータの一覧

概要

  • microCMSからのWebhookを利用して独自でデータ管理するために作成
  • コンテンツの状態とユーザー操作によって、typeとstatusがどう変わるかをまとめる
  • 参考サイトではstatusが配列で定義されているため、実際にどういう値が来るか確認

参考サイト

document.microcms.io

前提

  • コンテンツIDを手で変更することは考慮しない(ユーザーにさせない運用を想定)
  • コンテンツの並び替えは、データ自体の変更には影響ないため考慮しない
  • APIによる操作も考慮しない(利用しない想定)
  • SSGは今回とは別にWebhookを設定(特にtypeやstatusの判断は不要)

一覧

操作 type contents.old.status contents.new.status
コンテンツの公開(新規) new contents.old === null ["PUBLISH"]
コンテンツの公開(下書きデータなしの状態で更新) edit ["PUBLISH"] ["PUBLISH"]
コンテンツの公開(公開データなし && 下書きデータあり で更新) edit ["DRAFT"] ["PUBLISH"]
コンテンツの公開(公開データあり && 下書きデータあり で更新) edit ["PUBLISH","DRAFT"] ["PUBLISH"]
コンテンツの非公開時(下書きに戻す) edit ["PUBLISH"] ["DRAFT"]
コンテンツの下書き保存時(新規) new contents.old === null ["DRAFT"]
コンテンツの下書き保存時(公開データなしの状態で下書き更新) edit ["DRAFT"] ["DRAFT"]
コンテンツの下書き保存時(公開データありの状態で下書き新規) edit ["PUBLISH"] ["PUBLISH","DRAFT"]
コンテンツの下書き保存時(公開データありの状態で下書き更新) edit ["PUBLISH","DRAFT"] ["PUBLISH","DRAFT"]
公開中コンテンツの削除時(下書きなしの状態) delete ["PUBLISH"] contents.new === null
公開中コンテンツの削除時(下書きありの状態) delete ["PUBLISH","DRAFT"] contents.new === null
下書きコンテンツの削除時 delete ["DRAFT"] contents.new === null
下書きコンテンツの破棄 edit ["PUBLISH","DRAFT"] ["PUBLISH"]

結論

  • typeとstatusを使うことで、処理制御は大体できそう!

Amplifyで発生したビルドエラーの原因と解決案(Jamstack&Nuxt.js)

概要

  • Nuxt.jsでSSGしたものをAmplify上でhostingするJamstack構成
  • GitHubにPushされるとAmplify上でSSGされる流れ
  • Amplifyプロジェクトを新規に構築していく中で、3件連続ビルドエラーを経験したのでまとめる

前提

  • node.js:v14.17.0
  • amplify-cli:4.51.1
  • nuxt:2.15.6

結論

  • aws-exports.jsはフロントエンド側でimportが必要になるので、amplify.ymlでfrontendのビルド前にbackendのビルドでamplify pushを実行し、aws-exports.jsを生成する。(aws-exports.jsのgitignoreはデフォルトのままgit管理しない。他のブログとかを見ているとgitignoreから外してしまっている人もいるが複数人での開発を考慮すると含めないで解決した方が良い認識)
  • amplify-cliのバージョンがAmplifyビルドで動作しているものと、ローカルで使用しているものが一致しているか確認する。
  • Amplifyビルド用のロール作成が必要

エラーNo.1

背景

  • AmplifyとGitHub連携して、Nuxt.jsでSSGしたものをhostingするところまでは問題なかった
  • その後、amplify add authして、プロジェクトにaws-amplify@aws-amplify/ui-vueモジュールをインストールし、aws-amplifyを利用するための、pluginクラスを新規作成。pluginのクラスは下記。
import Vue from 'vue'
import Amplify from 'aws-amplify'
import '@aws-amplify/ui-vue'
import awsExports from '../aws-exports'
    
export default ({ isHMR }) => {
  if (isHMR) return
  if (process.client) {
    Amplify.configure(awsExports)
    Vue.use(Amplify)
  }
}
    
  • Amplify上のSSGするタイミングでエラーが発生

エラー内容

[fatal] Nuxt build error
ERROR in ./plugins/amplify.client.js
Module not found: Error: Can't resolve '../aws-exports' in 'plugins'
  • ../aws-exports.jsが見つからないとのこと

原因

  • amplifyコマンドでプロジェクト構築すると、aws-exports.jsはgitignoreに含まれている
  • Amplify上のビルドフローは、ルートディレクトリにamplify.ymlを配置しておくとで設定可能
  • 最初はhostingだけ試していたので、amplify.ymlを下記のようにしていた
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run generate
  artifacts:
    # IMPORTANT - Please verify your build output directory
    baseDirectory: dist
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*
    

解決策

  • amplify.ymlのfrontendのビルド処理の前に、backendでamplifyPush -simpleを差し込む
  • frontendの前にamplify pushが走り、これによりaws-exports.jsがAmplify上で作成され、その後のfrontendのソースからも参照が可能となる
version: 1
backend:
  phases:
    build:
      commands:
        - '# Execute Amplify CLI with the helper script'
        - amplifyPush --simple
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run generate
  artifacts:
    # IMPORTANT - Please verify your build output directory
    baseDirectory: dist
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*
    

エラーNo.2

背景

  • エラーNo.1の解決策をGitHubにPushして再びAmplify上でビルドしたら別のエラーが発生

エラー内容

# Executing command: amplifyPush --simple
# Getting Amplify CLI Cloud-Formation stack info from environment cache
# Start initializing Amplify environment: main
# Initializing new Amplify environment: main (amplify init)
File project: data should NOT have additional properties: 'graphqltransformer'
JSONValidationError: File project: data should NOT have additional properties: 'graphqltransformer'

原因

解決策

  • ローカルでは、その時点の最新版4.51.1を使っていたので、Amplify上も以下のように合わせた。
  • 初期段階はPackageには何も指定されていなかった。
  • latestを指定しているが、ローカルに明示的に合わせたほうが良いかもしれない。暫く経過観察予定。

f:id:yasug:20210521002243p:plain

f:id:yasug:20210521002333p:plain

エラーNo.3

背景

  • エラーNo.2の解決策を保存して、再びAmplify上でビルドしたら別のエラーが発生

エラー内容

  • Amplifyコンソール上で以下のエラーが発生
Your app does not have a role and you're attempting to interact with AWS resources
In order for you to interact with AWS resources you need to attach a role to your app. This can be done in the General Settings page in the console

f:id:yasug:20210521002837p:plain

原因

  • Amplifyビルドするのに必要なロールがあたっていないという感じ

解決案