GitHub App でリポジトリ接続した Amplify アプリを CloudFormation でデプロイしようとして苦戦したのでまとめてみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

GitHub App を利用した GitHub との接続について

Amplify で GitHub リポジトリと接続する際、以前は Oauth App を利用する必要がありました。現在は GitHub App を利用した形で接続することが推奨されています。

GitHub リポジトリに保存されている新しいアプリを接続すると、 GitHub Amplify はデフォルトでアプリを使用してリポジトリにアクセスします。ただし、 GitHub 以前にリポジトリから接続した既存の Amplify アプリは、OAuth を使用してアクセスします。CI/CD はこれらのアプリでも引き続き使用できますが、新しい Amplify GitHub アプリを使用するように移行することを強くお勧めします。
GitHubリポジトリへの Amplify アクセスを設定する

GitHub App と OAuth App の違いについて

一番大きな違いは下記になるかと思います。

OAuth App は GitHub ユーザとして振る舞う一方、GitHub App は Organization または Organization 内のリポジトリにインストールされた場合、自らのアイデンティティを用います。
GitHub アプリと OAuth アプリとの微妙な違い

上記考え方から OAuth App は作成したユーザーがアクセスできる全てのリポジトリへの権限を有するのに対して、GitHub App は選択したリポジトリのみへのアクセスを実現できます。 また、GitHub App は OAuth App より許可できる権限を細かく設定できるようになっています。OAuth App の場合はリポジトリへのアクセスのために repo 権限が必要になることが多いですが、多くの場合広すぎる権限を与えることになります。

マネジメントコンソールで作成するとどうなる?

現在マネジメントコンソール経由で Amplify アプリを作成すると GitHub App を利用した形で作成されます。
この時 GitHub App と OAuth App を特別意識する必要は無いですが、事前に GitHub の Owner 権限がある状態で GitHub App をインストールする必要があります。
これは https://github.com/apps/aws-amplify-REGION/installations/new から実施することができます。REGION は Amplify を利用するリージョンに置き換える必要があり、 ap-northeast-1 なら https://github.com/apps/aws-amplify-ap-northeast-1/installations/new になります。

GitHub App を利用して接続しているかどうかはマネジメントコンソールで確認可能です。OAuth App を利用している場合、マネジメントコンソールに GitHub Appへ移行と記載されます。 作成済みの Amplify App についても移行を開始をクリックすることで GitHub App を利用した形に変更することができます。

※発生条件が不明瞭ですが、移行時に下記現象が発生して失敗することがあります。issue に記載の通り、aws amplify update-app --app-id --repository-id —access-tokenを実行すれば問題無く移行が完了しました。

CloudFormation で Amplify アプリをデプロイするには?

CloudFormation や CLI で Amplify アプリを作成する場合は、GitHub の PAT(Personal Access Tokens) を利用する必要があります。 これらは Amplify アプリ作成時に一時的に必要な権限を与え、作成後のデプロイにおいては利用されません。 Oauth App の場合は Webhook の作成とデプロイキーの作成に利用され、GitHub App の場合は GitHub リポジトリへのアクセスを承認するために使用されます。 CloudFormation には OauthToken と AccessToken というパラメータがあり、それぞれ OAuth App と GitHub App に対応します。 GitHub App を利用したい場合は AccessToken に PAT を指定すれば良いということになります。

OauthToken
The OAuth token for a third-party source control system for an Amplify app. The OAuth token is used to create a webhook and a read-only deploy key using SSH cloning. The OAuth token is not stored. Use OauthToken for repository providers other than GitHub, such as Bitbucket or CodeCommit. To authorize access to GitHub as your repository provider, use AccessToken. You must specify either OauthToken or AccessToken when you create a new app.

AccessToken
The personal access token for a GitHub repository for an Amplify app. The personal access token is used to authorize access to a GitHub repository using the Amplify GitHub App. The token is not stored. Use AccessToken for GitHub repositories only. To authorize access to a repository provider such as Bitbucket or CodeCommit, use OauthToken. You must specify either AccessToken or OauthToken when you create a new app.
AWS::Amplify::App

CLI で Amplify App を作成するのに使用する create-app にも--oauth-tokenオプションと--access-tokenオプションが存在して、CloudFormation と同様の使い分け方になります。

以上を踏まえて、CloudFormation テンプレートを作成してみると下記のようになります。
AccessToken として指定する PAT は SecretManager に保存したものを参照しております。

AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  Domain:
    Type: String
    Description: The name for the AppName.

  Repository:
    Type: String
    Description: The URL for the Repository.

  BranchName:
    Type: String
    Description: The name for the branch.
    Default: main

Resources:
  MyApp:
    Type: AWS::Amplify::App
    Properties:
      BuildSpec: !Sub |-
        version: 1
        frontend:
          phases:
            # IMPORTANT - Please verify your build commands
            build:
              commands: []
          artifacts:
            # IMPORTANT - Please verify your build output directory
            baseDirectory: /
            files:
              - '**/*'
          cache:
            paths: []
      EnableBranchAutoDeletion: true
      Name: !Sub
        - Amplify-${AppName}
        - AppName: !Join ["-", !Split [".", !Ref Domain]]
      AccessToken: "{{resolve:secretsmanager:MyApp:SecretString:GitHubPersonalAccessToken}}"
      Repository: !Ref Repository

  MyBranch:
    Type: AWS::Amplify::Branch
    Properties:
      AppId: !GetAtt MyApp.AppId
      BranchName: !Ref BranchName
      EnableAutoBuild: true

ハマりそうなポイントが 2 点あったので紹介します。

PAT の権限に注意

PAT に repo 権限が付与されている場合には AccessToken パラメータを利用しても OAuth Token を利用した形でアプリが作成されました。(仕様をドキュメントから確認できたわけではないので、あくまで発生する可能性があるとお考え下さい。) OauthToken として PAT を利用する場合は repo 権限と admin:repo_hook 権限が必要ですが、AccessToken として利用する場合は admin: repo_hook 権限だけで問題ありません。

不要な repo 権限を付与することはセキュリティ的にも望ましくないため避けるべきですが、以前利用していた PAT を流用しようとした時にハマる可能性があるかと思います。(以前作成して Secret Manager に保存していた PAT を利用する場合等) admin:repo_hook のみを付与した PAT を利用しつつ、AccessToken パラメータを使用することで GitHub App を利用した形で Amplify アプリを作成できました。

Organization によっては PAT (特に classic)を使えない設定になっているかも

GitHub の Organization では PAT を利用可能かどうかを Organization 側で設定可能になっています。

Fine-grained personal access tokens を利用できるか、Fine-grained personal access tokens を利用する際に承認が必要かどうか、Personal access token(classic)を利用できるかどうかを設定可能です。 Fine-grained personal access tokens は 2022 年に登場した新しいトークンで、リポジトリを制限して権限を付与したり、より細かいスコープで権限を付与可能になっています。Personal access token(classic) は今まであった旧来のアクセストークンです。この 2 つの関係は Oauth App と GitHub App に近いです。

AWS 公式ドキュメントの手順は Personal access token(classic) を利用しているので、この手順通りに CloudFormation で Amplify アプリを作成するためには personal access tokens (classic) の利用を制限していない必要があります。(2023年1月時点)

Fine-grained personal access tokens を使うには?

ドキュメントの通りに作成するのであれば Personal access token(classic) を利用することになるのですが、このために GitHub Organizaion のポリシーを変更するのは微妙な気がします。
GitHub のドキュメントを確認した所、可能な限り Fine-grained personal access tokens の利用が推奨されており、下記のようなメリットがあります。

各トークンは、1 人のユーザーまたは 1 つの Organization が所有するリソースにのみアクセスできます。
各トークンは、特定のリポジトリにのみアクセスできます。
各トークンには特定のアクセス許可が付与されます。これにより、personal access tokens (classic) に付与されるスコープよりも細かく制御できます。
各トークンには、有効期限が必要です。
Organization オーナーは、Organization 内のリソースにアクセスできる fine-grained personal access token の承認を要求できます。
個人用アクセス トークンの作成

なので、Personal access token(classic) が使えない環境を想定して、Fine-grained personal access tokens を使用して Amplify アプリを作成してみました。 必要な権限がドキュメントに明示されていないので、とりあえずデプロイしてみて足りないと言われた権限を足していく方針を取りました。まず権限なしの Fine-grained personal access tokens を利用してデプロイしてみます。

Fine-grained personal access tokens では Resource ownerを選択できるようになっています。
個人アカウントのリポジトリであれば Resource owner を個人アカウントに、Organization 配下のリポジトリであれば Resource owner を Organization に設定する必要があります。

アクセスできるリポジトリも制限可能です。

このトークンを AccessToken で指定してデプロイした所、下記エラーがでました。(当然ですが...)

Resource handler returned message: "Invalid request provided: There was an issue setting up your repository. Please try again later.({"message":"Not Found","documentation_url":"https://docs.github.com/rest/webhooks/repos#create-a-repository-webhook"})

create-repository-webhook の権限が無いと言われたので、付与します。Metadata の読み取り権限は必須項目のようで自動で付与されました。

Webhooks の Read and write の権限があれば Fine-grained personal access tokens を利用して Amplify アプリを作成することができました!

さいごに

Amplify アプリを作成するだけであればマネジメントコンソールからの方が楽だったりするのですが、CloudFormation テンプレート化すると設定のレビュー時に便利だったり設定ミスを減らせる等のメリットがあります。CloudFormation で Amplify アプリを作成する際は参考にしていただけると幸いです。