TL;DR;
- GitHub ActionsでCloud Functionsをデプロイしようとした
- 単純に「Cloud Functions開発者」ロールを付与したサービスアカウント(
ci-deploy@PROJECT_ID.iam.gserviceaccount.com
とする )でデプロイしようとしたらエラーになった - 下記コマンドでデフォルトのサービスアカウント
PROJECT_ID@appspot.gserviceaccount.com
をci-deploy@PROJECT_ID.iam.gserviceaccount.com
にバインドして実行したら無事デプロイできた。
gcloud iam service-accounts add-iam-policy-binding PROJECT_ID@appspot.gserviceaccount.com \
--member='serviceAccount:ci-deploy@PROJECT_ID.iam.gserviceaccount.com' \
--role=roles/iam.serviceAccountUser
実現したいこと
devブランチにマージしたときに、GitHub ActionsでCloud Functionsをデプロイすること。
IAMエラー発生
サービスアカウントを作成
- GitHub Actionsでデプロイを実行するサービスアカウントとして
ci-deploy@PROJECT_ID.iam.gserviceaccount.com
を作成 - 「Cloud Functions開発者(roles/cloudfunctions.developer)」ロールを付与
- サービスアカウントのJSONキーを作成
GitHub Secretsの設定
- サービスアカウントのJSONキーの中身を
GCP_SA_KEY_DEV
という名前でセット
GitHub ActionsのWorkflowを定義
下記Workflowを定義します。
Actionsの中身の説明は本旨ではないので割愛します。
gcloudを実行するために使っている GoogleCloudPlatform/github-actions/setup-gcloud については、リポジトリを参照してください。
https://github.com/GoogleCloudPlatform/github-actions/tree/master/setup-gcloud
name: Deploy Cloud Functions(Dev)
on:
push:
branches:
- dev
jobs:
build-deploy:
name: Deploy Functions
runs-on: ubuntu-latest
env:
REGION: asia-northeast1
steps:
- uses: actions/checkout@v2
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
# OSSであれば project_id や service_account_email もsecretsに入れるのが良いでしょう
project_id: PROJECT_ID
service_account_email: ci-deploy@PROJECT_ID.iam.gserviceaccount.com
service_account_key: ${{ secrets.GCP_SA_KEY_DEV }}
- name: Deploy Functions
run: |
gcloud functions deploy FUNCTION1_NAME --source ./src/FUNCTION1_NAME --region=${REGION}
gcloud functions deploy FUNCTION2_NAME --source ./src/FUNCTION2_NAME --region=${REGION}
GitHub Actionsの実行
任意のfeatureブランチからdevにマージしてGitHub Actionsを起動します。
すると、gcloud functions deploy
で下記エラーが発生します。
[Missing necessary permission iam.serviceAccounts.actAs for on the service account PROJECT_ID@appspot.gserviceaccount.com.
Ensure that service account PROJECT_ID@appspot.gserviceaccount.com is a member of the project PROJECT_ID, and then grant the roles/iam.serviceAccountUser role.
You can do that by running 'gcloud iam service-accounts add-iam-policy-binding PROJECT_ID@appspot.gserviceaccount.com --member= --role=roles/iam.serviceAccountUser'
In case the member is a service account please use the prefix 'serviceAccount:' instead of 'user:'.]
IAMエラー解消
IAMアカウントにロールを追加
エラーメッセージを見て、え? PROJECT_ID@appspot.gserviceaccount.com
とかお呼びじゃないんだけど?
いや、実行するのに使ってるのは ci-deploy@PROJECT_ID.iam.gserviceaccount.com
なんだけど?
などと思いながらも、メッセージの指示通り下記コマンドを実行します。
gcloud iam service-accounts add-iam-policy-binding PROJECT_ID@appspot.gserviceaccount.com \
--member='serviceAccount:ci-deploy@PROJECT_ID.iam.gserviceaccount.com' \
--role=roles/iam.serviceAccountUser
これにより、 PROJECT_ID@appspot.gserviceaccount.com
にroles/iam.serviceAccountUserロールが付き、かつ ci-deploy@PROJECT_ID.iam.gserviceaccount.com
がバインドされるようです。
なお、バインド情報は下記コマンドで確認できます。
(GCPコンソールで確認する方法は不明です)
> gcloud iam service-accounts get-iam-policy PROJECT_ID@appspot.gserviceaccount.com
bindings:
- members:
- serviceAccount:ci-deploy@PROJECT_ID.iam.gserviceaccount.com
role: roles/iam.serviceAccountUser
etag: xxxxxxxxxx
version: 1
GitHub Actionsの再実行
上記コマンドを実行した上で、再度、GitHub Actionsを実行(GitHub上でRe-run)すると、無事、Cloud Functionsのデプロイに成功しました。
IAMエラーの原因究明(半分くらい)
しかし、この時点では、結局、何が問題であって、なぜ上手くいったのか理解できていませんでした。
人に聞いたり、調べたりしてみたところ、リファレンスに
デフォルトでは、Cloud Functions はランタイムに App Engine のデフォルトのサービス アカウント(PROJECT_ID@appspot.gserviceaccount.com)を使用します。
という記述を見つけます。
ここで gcloud functions deploy
のエラーコマンドに立ち戻ってみると、
Missing necessary permission iam.serviceAccounts.actAs for on the service account PROJECT_ID@appspot.gserviceaccount.com.
つまり、 PROJECT_ID@appspot.gserviceaccount.com
が、指定したサービスアカウントとして振る舞う(iam.serviceAccounts.actAs)権限が無い、ということを言っています(たぶん)。
この権限を与えるのが先ほどのコマンド(再掲)
gcloud iam service-accounts add-iam-policy-binding PROJECT_ID@appspot.gserviceaccount.com \
--member='serviceAccount:ci-deploy@PROJECT_ID.iam.gserviceaccount.com' \
--role=roles/iam.serviceAccountUser
のようです。
まだ残る疑問
ただ、 PROJECT_ID@appspot.gserviceaccount.com
は、あくまでFunctionsのランタイムのIAMユーザであって、デプロイ時のものではないと、私は認識しています。
むしろ、こちらに記述のある、
Cloud Functions サービスでは、関数の作成、更新または削除中にプロジェクトの管理アクションを実行するため、Google Cloud Functions サービス エージェント アカウント(service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com)を使用します。
service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com
がデプロイに使うIAMユーザなのではないかと。
だとすれば、権限を付けるべきユーザはこちらなのでは……しかし、デプロイは上手く動いてるし……謎。
GCPのIAM周り、難しいですね。
謝辞
エラー原因について相談の乗ってくれて、いろいろな情報をくれた @inductor さん、@0Delta さん、ありがとうございました。
おかげで「何か知らんが動いた」で終わらせずに済みました。