Open31

Cloud Build で firebase functions と hosting を deploy したい

nbstshnbstsh

service account 権限付与

service account 準備する

Add Cloud Build Service Account, Firebase Admin and API Keys Admin roles.

必要な role は以下三つ

  • Cloud Build Service Account (roles/cloudbuild.builds.builder)
  • Firebase Admin (roles/firebase.admin)
  • API Keys Admin (roles/serviceusage.apiKeysAdmin)

https://cloud.google.com/build/docs/deploying-builds/deploy-firebase

nbstshnbstsh

cloud build default service account

cloud build はデフォで Compute Engine default service account を利用する。

PROJECT_NUMBER-compute@developer.gserviceaccount.com

実際に利用される service accoun は GCP console > Cloud Build >Settings > Service Account から確認できる

https://cloud.google.com/build/docs/cloud-build-service-account

nbstshnbstsh

custom service account 作成

Note: We have introduced changes to the default service account used to run builds. As a best practice, we recommend that you specify your own service account to run your builds. For more information see Cloud Build default service account change.

デフォの compute engine の service account は Editor role なのでだいぶ権限が強い。そのため、Cloud Build 用の個別の service account を作成し、必要最低限の権限を付与するのがベターとのこと。

この方針に従い Cloud Buiild 用の service account を作成し、必要な role を付与していく。

service account 作成

gcloud cli で service account 作成する

SERVICE_ACCOUNT_NAME="cloud-build-firebase"
PROJECT_ID="$(gcloud config get-value project)"
gcloud iam service-accounts create $SERVICE_ACCOUNT_NAME \
    --project $PROJECT_ID \
    --description="Cloud Build Service Account for Firebase Deploy" \
    --display-name="Cloud Build Firebase"

確認

gcloud iam service-accounts list 

service account email 控えとく

SERVICE_ACCOUNT_EMAIL="$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com"

role を付与

  • Cloud Build Service Account (roles/cloudbuild.builds.builder)
  • Firebase Admin (roles/firebase.admin)
  • API Keys Admin (roles/serviceusage.apiKeysAdmin)
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
    --role="roles/cloudbuild.builds.builder"
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
    --role="roles/firebase.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
    --role="roles/serviceusage.apiKeysAdmin"
nbstshnbstsh

cloudbuild.yaml で指定

cloudbuild.yaml
#...
serviceAccount: 'projects/$PROJECT_ID/serviceAccounts/cloud-build-firebase@$PROJECT_ID.iam.gserviceaccount.com'
options:
  logging: CLOUD_LOGGING_ONLY
nbstshnbstsh

firebase comminuty builder

Cloud Build で firebase-tools 使えるように下準備をする。

https://cloud.google.com/build/docs/deploying-builds/deploy-firebase#using_the_firebase_community_builder

nbstshnbstsh

github repo を clone して自身の project の Container Registry に build した image を push してあげる必要がある。

git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/firebase
gcloud builds submit --region=$REGION .

ERROR: (gcloud.builds.submit) FAILED_PRECONDITION: failed precondition: due to quota restrictions, cannot run builds in this region, see https://cloud.google.com/build/docs/locations#restricted_regions_for_some_projects

asia-northeast1 だとエラー出た。しょうがないので asia-east1 でやるか。

gcloud builds submit --region=asia-east1 .
firebase community builder の中身
Dockerfile
FROM node:lts-alpine3.18 AS app-env

# Install Python and Java and pre-cache emulator dependencies.
RUN apk add --no-cache python3 py3-pip openjdk11-jre bash && \
    npm install -g firebase-tools && \
    firebase setup:emulators:database && \
    firebase setup:emulators:firestore && \
    firebase setup:emulators:pubsub && \
    firebase setup:emulators:storage && \
    firebase setup:emulators:ui && \
    rm -rf /var/cache/apk/*

ADD firebase.bash /usr/bin
RUN chmod +x /usr/bin/firebase.bash

ENTRYPOINT [ "/usr/bin/firebase.bash" ]
firebase.bash
#!/bin/bash

# run the original firebase
if [ $FIREBASE_TOKEN ]; then
  firebase "$@" --token $FIREBASE_TOKEN
else
  firebase "$@"
fi
cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/firebase', '.']
images:
- 'gcr.io/$PROJECT_ID/firebase'
tags: ['cloud-builders-community']

https://github.com/GoogleCloudPlatform/cloud-builders-community/tree/master/firebase

push したら clone したコードは消してOK

cd ../..
rm -rf cloud-builders-community/
nbstshnbstsh

firebase command を Cloud Build で実行

これで準備完了。Cloud Build step で firebase command が利用できるようになった。

こんな感じで↓先ほど push した gcr.io/$PROJECT_ID/firebase image を使う step を用意して firebase command を実行できる。

cloudbuild.yaml
steps:
  - name: gcr.io/project-id/firebase
    args: ['deploy', '--project=firebase-project-id', '--only=hosting']
gcloud builds submit --region=$REGION --config config-file-path source-directory
nbstshnbstsh

Artifact Registry への移行

Container Registry is deprecated and scheduled for shutdown. Organizations that haven't used Container Registry prior to January 8, 2024 have new gcr.io repositories hosted on Artifact Registry by default. Effective May 15, 2024, Google Cloud projects without previous usage of Container Registry will only support hosting and managing images for the gcr.io domain in Artifact Registry.

Container Registry is scheduled for shutdown on March 18, 2025. For details on the deprecation, see Container Registry deprecation.

公式 Doc の warning にある通り、Container Registry は shut down されるので、Artifact Registry への移行が必要。(January 8, 2024 までに Container Registry 使っていなかったら "gcr.io" は Artifact Registry に host された状態になるので移行不要)

https://zenn.dev/nbstsh/scraps/65a5b7009bb3b3

nbstshnbstsh

package.json script 経由で firebase command 使いたい場合

firebase comminuty builder は entrypoint に firebase を実行する bash が指定されているため、firebase command を実行する前提で args に引数を受け渡す指定の方法が基本となる。

cloudbuild.yaml
steps:
  - name: gcr.io/project-id/firebase
    args: ['deploy', '--project=firebase-project-id', '--only=hosting']

ただこれだと package.json scripts で deploy 用の command を用意してその一部として firebase command を呼び出しているケースに対応できない↓

package.json
  "scripts": {
    "deploy:dev": "npm run build:dev && firebase deploy --only hosting --project dev",
    //...
  }

この場合の対応について考える。

nbstshnbstsh

entrypoint

Use the entrypoint in a build step to specify an entrypoint if you don't want to use the default entrypoint of the builder. If you don't set this field, Cloud Build will use the builder's entrypoint. The following snippet sets the entrypoints for the npm build step:

cluodbuild.yaml entrypoint で builder の default entrypoint を置き換えることができる。
これを使って、firebase commnity builder の entrypoint を置き換えてあげれば良さそう。

https://cloud.google.com/build/docs/build-config-file-schema#entrypoint

nbstshnbstsh

firebase comminity builder の entrypoint 変更

firebase comminity builder は node image をベースに作られているので npm, yarn は利用できる。

firebase community builder Dockerfile
Dockerfile
FROM node:lts-alpine3.18 AS app-env

# Install Python and Java and pre-cache emulator dependencies.
RUN apk add --no-cache python3 py3-pip openjdk11-jre bash && \
    npm install -g firebase-tools && \
    firebase setup:emulators:database && \
    firebase setup:emulators:firestore && \
    firebase setup:emulators:pubsub && \
    firebase setup:emulators:storage && \
    firebase setup:emulators:ui && \
    rm -rf /var/cache/apk/*

ADD firebase.bash /usr/bin
RUN chmod +x /usr/bin/firebase.bash

ENTRYPOINT [ "/usr/bin/firebase.bash" ]

なので、npm を利用している場合なら、以下のように entrypoint に "npm" を設定してあげれば良い

cloudbuild.yaml
steps:
  #...
  - name: gcr.io/project-id/firebase
    entrypoint: 'npm'
    args: ['run', 'deploy']
nbstshnbstsh

pnpm を使っている場合は...?

pnpm を利用している場合は、残念なが firebase comminity builder をそのまま利用する形での対応は難しそう...

対応案として考えられるのは、この辺↓

  • firebase comminity builder の Dockerfile をベースに pnpm を組み込んだものを自作して image を push し、それを Cloud Build で利用する
  • firebase cominity builder を Cloud Build で利用する際に pnpm を install する
  • firebase-tools を project の devDependencies に入れちゃう
nbstshnbstsh

firebase community builder を利用する際に pnpm を install

firebase cominity builder を Cloud Build で利用する際に pnpm を install する

今回は手短に Cloud Build 内で完結できるこの方向で対応してみる。

Using the script field

cloudbuild.yaml の script field を利用することで shell scripts を実行できる。こちらを利用して pnpm を install してから、package.json script command を実行する流れでいく。

https://cloud.google.com/build/docs/configuring-builds/run-bash-scripts#using_the_script_field

cloudbuild.yaml

scrip field で pnpm を install した上で、pnpm install してからの deploy command を実行する例↓

cloudbuild.yaml
steps:
  - name: gcr.io/project-id/firebase
    script: |
      npm install -g pnpm
      pnpm install
      pnpm run deploy
nbstshnbstsh

権限エラー

Error: Missing permissions required for functions deploy. You must have permission iam.serviceAccounts.ActAs on service account xxxxxxxxx@xxxxxxxxxx

service account に対する Service Account User Role が足りないみたい。

hosting の deploy のみにしてみたところ、この権限エラーはでないので functions を deploy する際に必要な権限が足りてないっぽい。

nbstshnbstsh

Cloud Run service account 利用権限付与

"Cloud Build で利用する service account" に "Cloud Run runtime の service account" に対する Service Account User role (roles/iam.serviceAccountUser) 付与する

CLOUD_BUILD_SERVICE_ACCOUNT_EMAIL= Cloud Build で利用する service account
CLOUD_RUN_SERVICE_ACCOUNT_EMAIL= Cloud Run runtime の service account
gcloud iam service-accounts add-iam-policy-binding \
    $CLOUD_RUN_SERVICE_ACCOUNT_EMAIL \
    --member=serviceAccount:$CLOUD_BUILD_SERVICE_ACCOUNT_EMAIL \
    --role=roles/iam.serviceAccountUser

確認

gcloud iam service-accounts get-iam-policy \
    $CLOUD_RUN_SERVICE_ACCOUNT_EMAIL
nbstshnbstsh

App Engine service account 利用権限付与

どうやら App Engine の service account の権限も必要みたい。

"Cloud Build で利用する service account" に "App Engine の service account" ($PROJECT_ID@appspot.gserviceaccount.com)に対する Service Account User role (roles/iam.serviceAccountUser) 付与する。

CLOUD_BUILD_SERVICE_ACCOUNT_EMAIL= Cloud Build で利用する service account
APP_ENGINE_SERVICE_ACCOUNT_EMAIL= App Engine Default Service Account (PROJECT_ID@appspot.gserviceaccount.com)
gcloud iam service-accounts add-iam-policy-binding \
    $APP_ENGINE_SERVICE_ACCOUNT_EMAIL \
    --member=serviceAccount:$CLOUD_BUILD_SERVICE_ACCOUNT_EMAIL \
    --role=roles/iam.serviceAccountUser

確認

gcloud iam service-accounts get-iam-policy \
    $APP_ENGINE_SERVICE_ACCOUNT_EMAIL
nbstshnbstsh

これで問題なく Cloud Build から firebase hosting, funcions に deploy できた

nbstshnbstsh
nbstshnbstsh

Custom Role 作ってみる

公式 Doc に従って以下3つの role を付与することで hosting, functions へ deploy できることは確認できた。

  • Cloud Build Service Account (roles/cloudbuild.builds.builder)
  • Firebase Admin (roles/firebase.admin)
  • API Keys Admin (roles/serviceusage.apiKeysAdmin)

しかし、権限与えすぎな気がするので最低限の権限に絞りたい。特に Firebase Admin の権限が強すぎる。

nbstshnbstsh
roles/cloudbuild.builds.builder
description: Can perform builds
etag: AA==
includedPermissions:
- artifactregistry.aptartifacts.create
- artifactregistry.attachments.create
- artifactregistry.attachments.get
- artifactregistry.attachments.list
- artifactregistry.dockerimages.get
- artifactregistry.dockerimages.list
- artifactregistry.files.download
- artifactregistry.files.get
- artifactregistry.files.list
- artifactregistry.files.update
- artifactregistry.files.upload
- artifactregistry.kfpartifacts.create
- artifactregistry.locations.get
- artifactregistry.locations.list
- artifactregistry.mavenartifacts.get
- artifactregistry.mavenartifacts.list
- artifactregistry.npmpackages.get
- artifactregistry.npmpackages.list
- artifactregistry.packages.get
- artifactregistry.packages.list
- artifactregistry.packages.update
- artifactregistry.projectsettings.get
- artifactregistry.pythonpackages.get
- artifactregistry.pythonpackages.list
- artifactregistry.repositories.createOnPush
- artifactregistry.repositories.deleteArtifacts
- artifactregistry.repositories.downloadArtifacts
- artifactregistry.repositories.get
- artifactregistry.repositories.list
- artifactregistry.repositories.listEffectiveTags
- artifactregistry.repositories.listTagBindings
- artifactregistry.repositories.readViaVirtualRepository
- artifactregistry.repositories.uploadArtifacts
- artifactregistry.rules.get
- artifactregistry.rules.list
- artifactregistry.tags.create
- artifactregistry.tags.get
- artifactregistry.tags.list
- artifactregistry.tags.update
- artifactregistry.versions.get
- artifactregistry.versions.list
- artifactregistry.yumartifacts.create
- cloudbuild.builds.create
- cloudbuild.builds.get
- cloudbuild.builds.list
- cloudbuild.builds.update
- cloudbuild.operations.get
- cloudbuild.operations.list
- cloudbuild.workerpools.use
- containeranalysis.occurrences.create
- containeranalysis.occurrences.delete
- containeranalysis.occurrences.get
- containeranalysis.occurrences.list
- containeranalysis.occurrences.update
- logging.logEntries.create
- logging.logEntries.list
- logging.views.access
- pubsub.topics.create
- pubsub.topics.publish
- remotebuildexecution.blobs.get
- resourcemanager.projects.get
- resourcemanager.projects.list
- source.repos.get
- source.repos.list
- storage.buckets.create
- storage.buckets.get
- storage.buckets.list
- storage.objects.create
- storage.objects.delete
- storage.objects.get
- storage.objects.list
- storage.objects.update
name: roles/cloudbuild.builds.builder
stage: GA
title: Cloud Build Service Account

nbstshnbstsh
roles/firebase.admin
description: Full access to Firebase products.
etag: AA==
includedPermissions:
- apikeys.keys.get
- apikeys.keys.getKeyString
- apikeys.keys.list
- apikeys.keys.lookup
- appengine.applications.get
- automl.annotationSpecs.create
- automl.annotationSpecs.delete
- automl.annotationSpecs.get
- automl.annotationSpecs.list
- automl.annotationSpecs.update
- automl.annotations.approve
- automl.annotations.create
- automl.annotations.list
- automl.annotations.manipulate
- automl.annotations.reject
- automl.columnSpecs.get
- automl.columnSpecs.list
- automl.columnSpecs.update
- automl.datasets.create
- automl.datasets.delete
- automl.datasets.export
- automl.datasets.get
- automl.datasets.getIamPolicy
- automl.datasets.import
- automl.datasets.list
- automl.datasets.setIamPolicy
- automl.datasets.update
- automl.examples.delete
- automl.examples.get
- automl.examples.list
- automl.examples.update
- automl.files.delete
- automl.files.list
- automl.humanAnnotationTasks.create
- automl.humanAnnotationTasks.delete
- automl.humanAnnotationTasks.get
- automl.humanAnnotationTasks.list
- automl.locations.get
- automl.locations.getIamPolicy
- automl.locations.list
- automl.locations.setIamPolicy
- automl.modelEvaluations.create
- automl.modelEvaluations.get
- automl.modelEvaluations.list
- automl.models.create
- automl.models.delete
- automl.models.deploy
- automl.models.export
- automl.models.get
- automl.models.getIamPolicy
- automl.models.list
- automl.models.predict
- automl.models.setIamPolicy
- automl.models.undeploy
- automl.operations.cancel
- automl.operations.delete
- automl.operations.get
- automl.operations.list
- automl.tableSpecs.get
- automl.tableSpecs.list
- automl.tableSpecs.update
- clientauthconfig.brands.get
- clientauthconfig.brands.list
- clientauthconfig.brands.update
- clientauthconfig.clients.create
- clientauthconfig.clients.delete
- clientauthconfig.clients.get
- clientauthconfig.clients.list
- clientauthconfig.clients.update
- cloudbuild.builds.get
- cloudbuild.builds.list
- cloudbuild.operations.get
- cloudbuild.operations.list
- cloudconfig.configs.get
- cloudconfig.configs.update
- cloudfunctions.functions.call
- cloudfunctions.functions.create
- cloudfunctions.functions.delete
- cloudfunctions.functions.get
- cloudfunctions.functions.getIamPolicy
- cloudfunctions.functions.invoke
- cloudfunctions.functions.list
- cloudfunctions.functions.setIamPolicy
- cloudfunctions.functions.sourceCodeGet
- cloudfunctions.functions.sourceCodeSet
- cloudfunctions.functions.update
- cloudfunctions.locations.list
- cloudfunctions.operations.get
- cloudfunctions.operations.list
- cloudmessaging.messages.create
- cloudnotifications.activities.list
- cloudtestservice.environmentcatalog.get
- cloudtestservice.matrices.create
- cloudtestservice.matrices.get
- cloudtestservice.matrices.update
- cloudtoolresults.executions.create
- cloudtoolresults.executions.get
- cloudtoolresults.executions.list
- cloudtoolresults.executions.update
- cloudtoolresults.histories.create
- cloudtoolresults.histories.get
- cloudtoolresults.histories.list
- cloudtoolresults.settings.create
- cloudtoolresults.settings.get
- cloudtoolresults.settings.update
- cloudtoolresults.steps.create
- cloudtoolresults.steps.get
- cloudtoolresults.steps.list
- cloudtoolresults.steps.update
- datastore.backupSchedules.create
- datastore.backupSchedules.delete
- datastore.backupSchedules.get
- datastore.backupSchedules.list
- datastore.backupSchedules.update
- datastore.backups.delete
- datastore.backups.get
- datastore.backups.list
- datastore.backups.restoreDatabase
- datastore.databases.bulkDelete
- datastore.databases.create
- datastore.databases.createTagBinding
- datastore.databases.delete
- datastore.databases.deleteTagBinding
- datastore.databases.export
- datastore.databases.get
- datastore.databases.getMetadata
- datastore.databases.import
- datastore.databases.list
- datastore.databases.listEffectiveTags
- datastore.databases.listTagBindings
- datastore.databases.update
- datastore.entities.allocateIds
- datastore.entities.create
- datastore.entities.delete
- datastore.entities.get
- datastore.entities.list
- datastore.entities.update
- datastore.indexes.create
- datastore.indexes.delete
- datastore.indexes.get
- datastore.indexes.list
- datastore.indexes.update
- datastore.keyVisualizerScans.get
- datastore.keyVisualizerScans.list
- datastore.locations.get
- datastore.locations.list
- datastore.namespaces.get
- datastore.namespaces.list
- datastore.operations.cancel
- datastore.operations.delete
- datastore.operations.get
- datastore.operations.list
- datastore.statistics.get
- datastore.statistics.list
- errorreporting.groups.list
- eventarc.channelConnections.create
- eventarc.channelConnections.delete
- eventarc.channelConnections.get
- eventarc.channelConnections.getIamPolicy
- eventarc.channelConnections.list
- eventarc.channelConnections.publish
- eventarc.channelConnections.setIamPolicy
- eventarc.channels.attach
- eventarc.channels.create
- eventarc.channels.delete
- eventarc.channels.get
- eventarc.channels.getIamPolicy
- eventarc.channels.list
- eventarc.channels.publish
- eventarc.channels.setIamPolicy
- eventarc.channels.undelete
- eventarc.channels.update
- eventarc.enrollments.create
- eventarc.enrollments.delete
- eventarc.enrollments.get
- eventarc.enrollments.getIamPolicy
- eventarc.enrollments.list
- eventarc.enrollments.setIamPolicy
- eventarc.enrollments.update
- eventarc.events.receiveAuditLogWritten
- eventarc.events.receiveEvent
- eventarc.googleApiSources.create
- eventarc.googleApiSources.delete
- eventarc.googleApiSources.get
- eventarc.googleApiSources.getIamPolicy
- eventarc.googleApiSources.list
- eventarc.googleApiSources.setIamPolicy
- eventarc.googleApiSources.update
- eventarc.googleChannelConfigs.get
- eventarc.googleChannelConfigs.update
- eventarc.locations.get
- eventarc.locations.list
- eventarc.messageBuses.create
- eventarc.messageBuses.delete
- eventarc.messageBuses.get
- eventarc.messageBuses.getIamPolicy
- eventarc.messageBuses.list
- eventarc.messageBuses.publish
- eventarc.messageBuses.setIamPolicy
- eventarc.messageBuses.update
- eventarc.messageBuses.use
- eventarc.operations.cancel
- eventarc.operations.delete
- eventarc.operations.get
- eventarc.operations.list
- eventarc.pipelines.create
- eventarc.pipelines.delete
- eventarc.pipelines.get
- eventarc.pipelines.getIamPolicy
- eventarc.pipelines.list
- eventarc.pipelines.setIamPolicy
- eventarc.pipelines.update
- eventarc.providers.get
- eventarc.providers.list
- eventarc.triggers.create
- eventarc.triggers.delete
- eventarc.triggers.get
- eventarc.triggers.getIamPolicy
- eventarc.triggers.list
- eventarc.triggers.setIamPolicy
- eventarc.triggers.undelete
- eventarc.triggers.update
- fcmdata.deliverydata.list
- firebase.billingPlans.get
- firebase.billingPlans.update
- firebase.clients.create
- firebase.clients.delete
- firebase.clients.get
- firebase.clients.list
- firebase.clients.undelete
- firebase.clients.update
- firebase.links.create
- firebase.links.delete
- firebase.links.list
- firebase.links.update
- firebase.playLinks.get
- firebase.playLinks.list
- firebase.playLinks.update
- firebase.projects.delete
- firebase.projects.get
- firebase.projects.update
- firebaseabt.experimentresults.get
- firebaseabt.experiments.create
- firebaseabt.experiments.delete
- firebaseabt.experiments.get
- firebaseabt.experiments.list
- firebaseabt.experiments.update
- firebaseabt.projectmetadata.get
- firebaseanalytics.resources.googleAnalyticsEdit
- firebaseanalytics.resources.googleAnalyticsReadAndAnalyze
- firebaseappcheck.appAttestConfig.get
- firebaseappcheck.appAttestConfig.update
- firebaseappcheck.appCheckTokens.verify
- firebaseappcheck.debugTokens.get
- firebaseappcheck.debugTokens.update
- firebaseappcheck.deviceCheckConfig.get
- firebaseappcheck.deviceCheckConfig.update
- firebaseappcheck.playIntegrityConfig.get
- firebaseappcheck.playIntegrityConfig.update
- firebaseappcheck.recaptchaEnterpriseConfig.get
- firebaseappcheck.recaptchaEnterpriseConfig.update
- firebaseappcheck.recaptchaV3Config.get
- firebaseappcheck.recaptchaV3Config.update
- firebaseappcheck.resourcePolicies.get
- firebaseappcheck.resourcePolicies.update
- firebaseappcheck.safetyNetConfig.get
- firebaseappcheck.safetyNetConfig.update
- firebaseappcheck.services.get
- firebaseappcheck.services.update
- firebaseappdistro.groups.list
- firebaseappdistro.groups.update
- firebaseappdistro.releases.list
- firebaseappdistro.releases.update
- firebaseappdistro.testers.list
- firebaseappdistro.testers.update
- firebaseauth.configs.create
- firebaseauth.configs.get
- firebaseauth.configs.getHashConfig
- firebaseauth.configs.getSecret
- firebaseauth.configs.update
- firebaseauth.users.create
- firebaseauth.users.createSession
- firebaseauth.users.delete
- firebaseauth.users.get
- firebaseauth.users.sendEmail
- firebaseauth.users.update
- firebasecrash.issues.update
- firebasecrash.reports.get
- firebasecrashlytics.config.get
- firebasecrashlytics.config.update
- firebasecrashlytics.data.get
- firebasecrashlytics.issues.get
- firebasecrashlytics.issues.list
- firebasecrashlytics.issues.update
- firebasecrashlytics.sessions.get
- firebasedatabase.instances.create
- firebasedatabase.instances.delete
- firebasedatabase.instances.disable
- firebasedatabase.instances.get
- firebasedatabase.instances.list
- firebasedatabase.instances.reenable
- firebasedatabase.instances.undelete
- firebasedatabase.instances.update
- firebasedataconnect.connectorRevisions.delete
- firebasedataconnect.connectorRevisions.get
- firebasedataconnect.connectorRevisions.list
- firebasedataconnect.connectors.create
- firebasedataconnect.connectors.delete
- firebasedataconnect.connectors.get
- firebasedataconnect.connectors.list
- firebasedataconnect.connectors.update
- firebasedataconnect.locations.get
- firebasedataconnect.locations.list
- firebasedataconnect.operations.cancel
- firebasedataconnect.operations.delete
- firebasedataconnect.operations.get
- firebasedataconnect.operations.list
- firebasedataconnect.schemaRevisions.delete
- firebasedataconnect.schemaRevisions.get
- firebasedataconnect.schemaRevisions.list
- firebasedataconnect.schemas.create
- firebasedataconnect.schemas.delete
- firebasedataconnect.schemas.get
- firebasedataconnect.schemas.list
- firebasedataconnect.schemas.update
- firebasedataconnect.services.create
- firebasedataconnect.services.delete
- firebasedataconnect.services.executeGraphql
- firebasedataconnect.services.executeGraphqlRead
- firebasedataconnect.services.get
- firebasedataconnect.services.list
- firebasedataconnect.services.update
- firebasedynamiclinks.destinations.list
- firebasedynamiclinks.destinations.update
- firebasedynamiclinks.domains.create
- firebasedynamiclinks.domains.delete
- firebasedynamiclinks.domains.get
- firebasedynamiclinks.domains.list
- firebasedynamiclinks.domains.update
- firebasedynamiclinks.links.create
- firebasedynamiclinks.links.get
- firebasedynamiclinks.links.list
- firebasedynamiclinks.links.update
- firebasedynamiclinks.stats.get
- firebaseextensions.configs.create
- firebaseextensions.configs.delete
- firebaseextensions.configs.list
- firebaseextensions.configs.update
- firebaseextensionspublisher.extensions.create
- firebaseextensionspublisher.extensions.delete
- firebaseextensionspublisher.extensions.get
- firebaseextensionspublisher.extensions.list
- firebasehosting.sites.create
- firebasehosting.sites.delete
- firebasehosting.sites.get
- firebasehosting.sites.list
- firebasehosting.sites.update
- firebaseinappmessaging.campaigns.create
- firebaseinappmessaging.campaigns.delete
- firebaseinappmessaging.campaigns.get
- firebaseinappmessaging.campaigns.list
- firebaseinappmessaging.campaigns.update
- firebasemessagingcampaigns.campaigns.create
- firebasemessagingcampaigns.campaigns.delete
- firebasemessagingcampaigns.campaigns.get
- firebasemessagingcampaigns.campaigns.list
- firebasemessagingcampaigns.campaigns.start
- firebasemessagingcampaigns.campaigns.stop
- firebasemessagingcampaigns.campaigns.update
- firebaseml.models.create
- firebaseml.models.delete
- firebaseml.models.get
- firebaseml.models.list
- firebaseml.models.update
- firebaseml.modelversions.create
- firebaseml.modelversions.get
- firebaseml.modelversions.list
- firebaseml.modelversions.update
- firebasenotifications.messages.create
- firebasenotifications.messages.delete
- firebasenotifications.messages.get
- firebasenotifications.messages.list
- firebasenotifications.messages.update
- firebaseperformance.config.update
- firebaseperformance.data.get
- firebaserules.releases.create
- firebaserules.releases.delete
- firebaserules.releases.get
- firebaserules.releases.getExecutable
- firebaserules.releases.list
- firebaserules.releases.update
- firebaserules.rulesets.create
- firebaserules.rulesets.delete
- firebaserules.rulesets.get
- firebaserules.rulesets.list
- firebaserules.rulesets.test
- firebasestorage.buckets.addFirebase
- firebasestorage.buckets.get
- firebasestorage.buckets.list
- firebasestorage.buckets.removeFirebase
- firebasestorage.defaultBucket.create
- firebasestorage.defaultBucket.delete
- firebasestorage.defaultBucket.get
- logging.logEntries.list
- monitoring.timeSeries.list
- oauthconfig.verification.get
- orgpolicy.policy.get
- recommender.cloudFunctionsPerformanceInsights.get
- recommender.cloudFunctionsPerformanceInsights.list
- recommender.cloudFunctionsPerformanceInsights.update
- recommender.cloudFunctionsPerformanceRecommendations.get
- recommender.cloudFunctionsPerformanceRecommendations.list
- recommender.cloudFunctionsPerformanceRecommendations.update
- recommender.iamPolicyInsights.get
- recommender.iamPolicyInsights.list
- recommender.iamPolicyInsights.update
- recommender.iamPolicyRecommendations.get
- recommender.iamPolicyRecommendations.list
- recommender.iamPolicyRecommendations.update
- recommender.locations.get
- recommender.locations.list
- recommender.runServiceCostInsights.get
- recommender.runServiceCostInsights.list
- recommender.runServiceCostInsights.update
- recommender.runServiceCostRecommendations.get
- recommender.runServiceCostRecommendations.list
- recommender.runServiceCostRecommendations.update
- recommender.runServiceIdentityInsights.get
- recommender.runServiceIdentityInsights.list
- recommender.runServiceIdentityInsights.update
- recommender.runServiceIdentityRecommendations.get
- recommender.runServiceIdentityRecommendations.list
- recommender.runServiceIdentityRecommendations.update
- recommender.runServicePerformanceInsights.get
- recommender.runServicePerformanceInsights.list
- recommender.runServicePerformanceInsights.update
- recommender.runServicePerformanceRecommendations.get
- recommender.runServicePerformanceRecommendations.list
- recommender.runServicePerformanceRecommendations.update
- recommender.runServiceSecurityInsights.get
- recommender.runServiceSecurityInsights.list
- recommender.runServiceSecurityInsights.update
- recommender.runServiceSecurityRecommendations.get
- recommender.runServiceSecurityRecommendations.list
- recommender.runServiceSecurityRecommendations.update
- recommender.storageBucketSoftDeleteInsights.get
- recommender.storageBucketSoftDeleteInsights.list
- recommender.storageBucketSoftDeleteInsights.update
- recommender.storageBucketSoftDeleteRecommendations.get
- recommender.storageBucketSoftDeleteRecommendations.list
- recommender.storageBucketSoftDeleteRecommendations.update
- remotebuildexecution.blobs.get
- resourcemanager.hierarchyNodes.listEffectiveTags
- resourcemanager.projects.get
- resourcemanager.projects.getIamPolicy
- resourcemanager.projects.list
- run.configurations.get
- run.configurations.list
- run.executions.cancel
- run.executions.delete
- run.executions.get
- run.executions.list
- run.jobs.create
- run.jobs.createTagBinding
- run.jobs.delete
- run.jobs.deleteTagBinding
- run.jobs.get
- run.jobs.getIamPolicy
- run.jobs.list
- run.jobs.listEffectiveTags
- run.jobs.listTagBindings
- run.jobs.run
- run.jobs.runWithOverrides
- run.jobs.setIamPolicy
- run.jobs.update
- run.locations.list
- run.operations.delete
- run.operations.get
- run.operations.list
- run.revisions.delete
- run.revisions.get
- run.revisions.list
- run.routes.get
- run.routes.invoke
- run.routes.list
- run.services.create
- run.services.createTagBinding
- run.services.delete
- run.services.deleteTagBinding
- run.services.get
- run.services.getIamPolicy
- run.services.list
- run.services.listEffectiveTags
- run.services.listTagBindings
- run.services.setIamPolicy
- run.services.update
- run.tasks.get
- run.tasks.list
- runtimeconfig.configs.create
- runtimeconfig.configs.delete
- runtimeconfig.configs.get
- runtimeconfig.configs.list
- runtimeconfig.configs.update
- runtimeconfig.operations.get
- runtimeconfig.operations.list
- runtimeconfig.variables.create
- runtimeconfig.variables.delete
- runtimeconfig.variables.get
- runtimeconfig.variables.list
- runtimeconfig.variables.update
- runtimeconfig.variables.watch
- runtimeconfig.waiters.create
- runtimeconfig.waiters.delete
- runtimeconfig.waiters.get
- runtimeconfig.waiters.list
- runtimeconfig.waiters.update
- serviceusage.quotas.get
- serviceusage.services.get
- serviceusage.services.list
- storage.anywhereCaches.create
- storage.anywhereCaches.disable
- storage.anywhereCaches.get
- storage.anywhereCaches.list
- storage.anywhereCaches.pause
- storage.anywhereCaches.resume
- storage.anywhereCaches.update
- storage.bucketOperations.cancel
- storage.bucketOperations.get
- storage.bucketOperations.list
- storage.buckets.create
- storage.buckets.createTagBinding
- storage.buckets.delete
- storage.buckets.deleteTagBinding
- storage.buckets.enableObjectRetention
- storage.buckets.get
- storage.buckets.getIamPolicy
- storage.buckets.getObjectInsights
- storage.buckets.list
- storage.buckets.listEffectiveTags
- storage.buckets.listTagBindings
- storage.buckets.restore
- storage.buckets.setIamPolicy
- storage.buckets.update
- storage.folders.create
- storage.folders.delete
- storage.folders.get
- storage.folders.list
- storage.folders.rename
- storage.managedFolders.create
- storage.managedFolders.delete
- storage.managedFolders.get
- storage.managedFolders.getIamPolicy
- storage.managedFolders.list
- storage.managedFolders.setIamPolicy
- storage.managementHubs.get
- storage.managementHubs.update
- storage.multipartUploads.abort
- storage.multipartUploads.create
- storage.multipartUploads.list
- storage.multipartUploads.listParts
- storage.objects.create
- storage.objects.delete
- storage.objects.get
- storage.objects.getIamPolicy
- storage.objects.list
- storage.objects.overrideUnlockedRetention
- storage.objects.restore
- storage.objects.setIamPolicy
- storage.objects.setRetention
- storage.objects.update
name: roles/firebase.admin
stage: GA
title: Firebase Admin
nbstshnbstsh
roles/serviceusage.apiKeysAdmin
description: Ability to create, delete, update, get and list API keys for a project.
etag: AA==
includedPermissions:
- apikeys.keys.create
- apikeys.keys.delete
- apikeys.keys.get
- apikeys.keys.getKeyString
- apikeys.keys.list
- apikeys.keys.lookup
- apikeys.keys.undelete
- apikeys.keys.update
- orgpolicy.policy.get
- serviceusage.apiKeys.regenerate
- serviceusage.apiKeys.revert
name: roles/serviceusage.apiKeysAdmin
stage: GA
title: API Keys Admin
nbstshnbstsh

最低限必要そうなところから trial and error でしらみ潰しに必要な権限付け足していく

nbstshnbstsh

ようやく deploy できるところまでいけた...

こちらが hosting, functions を deploy するために必要だった権限↓

role.yaml
#...
includedPermissions:
  # Cloud Build Operations
  - storage.objects.get
  - cloudbuild.builds.create
  - cloudbuild.builds.get
  - cloudbuild.builds.update
  - cloudbuild.builds.list
  - artifactregistry.repositories.downloadArtifacts
  # Firebase tools
  - firebase.projects.get
  # Firebase Hosting Deployment
  - firebasehosting.sites.update
  # Cloud Functions Deployment
  - cloudfunctions.functions.create
  - cloudfunctions.functions.update
  - cloudfunctions.functions.delete
  - cloudfunctions.functions.list
  - runtimeconfig.configs.get
  - serviceusage.services.get
  - firebaseextensions.instances.list
  - resourcemanager.projects.get
  - cloudfunctions.functions.generateUploadUrl
  - cloudfunctions.operations.get

この他に、以下 resource 対して iam policy binding は別途行っている↓

  • Logging 用 Storage bucket への storage admin 権限
  • Cloud Run service account user
  • App Engine service account user
nbstshnbstsh

ただ、現状 functions は https trigger しかないからこの程度で収まっている感もある。

おとなしく Firebase Functions Admin role を付与した方がいいかもしれない...

nbstshnbstsh

scheduler を追加してみる

scheduler functions を追加して不要な role が存在しないか確かめる

import { onSchedule } from 'firebase-functions/v2/scheduler';

//...

export const scheduler = onSchedule('every day 00:00', async (context) => {
  console.log('This will run every day 00:00!');
  // Add your scheduled task logic here
});

権限エラー出た

Permissions denied enabling cloudscheduler.googleapis.com.

serviceusage.services.enable が足りなかったみたい

scheduler の deploy 時に permission エラー↓

HTTP Error: 403, Permission 'run.services.setIamPolicy' denied on resource

run.services.setIamPolicy 追加する

deploy 完了!

nbstshnbstsh

Permission 一覧

cloudbuild.yaml
# ...

includedPermissions:
  # Cloud Build Operations
  - storage.objects.get
  - cloudbuild.builds.create
  - cloudbuild.builds.get
  - cloudbuild.builds.update
  - cloudbuild.builds.list
  - artifactregistry.repositories.downloadArtifacts
  # Firebase tools
  - firebase.projects.get
  # Firebase Hosting Deployment
  - firebasehosting.sites.update
  # Cloud Functions Deployment
  - cloudfunctions.functions.create
  - cloudfunctions.functions.update
  - cloudfunctions.functions.delete
  - cloudfunctions.functions.list
  - runtimeconfig.configs.get
  - serviceusage.services.get
+  - serviceusage.services.enable
  - firebaseextensions.instances.list
  - resourcemanager.projects.get
  - cloudfunctions.functions.generateUploadUrl
  - cloudfunctions.operations.get
+  - run.services.setIamPolicy
nbstshnbstsh

https trigger を public にするとエラー

export const example = onRequest(
  {
+    invoker: 'public',
  },
  //...
);

Permission 'run.services.getIamPolicy' denied on resource ...

run.services.getIamPolicy が必要

修正後の permissions↓

cloudbuild.yaml
includedPermissions:
  # Cloud Build Operations
  - storage.objects.get
  - cloudbuild.builds.create
  - cloudbuild.builds.get
  - cloudbuild.builds.update
  - cloudbuild.builds.list
  - artifactregistry.repositories.downloadArtifacts
  # Firebase tools
  - firebase.projects.get
  # Firebase Hosting Deployment
  - firebasehosting.sites.update
  # Cloud Functions Deployment
  - cloudfunctions.functions.create
  - cloudfunctions.functions.update
  - cloudfunctions.functions.delete
  - cloudfunctions.functions.list
  - runtimeconfig.configs.get
  - serviceusage.services.get
  - serviceusage.services.enable 
  - firebaseextensions.instances.list
  - resourcemanager.projects.get
  - cloudfunctions.functions.generateUploadUrl
  - cloudfunctions.operations.get
  - run.services.setIamPolicy
+  - run.services.getIamPolicy 
nbstshnbstsh

hosting で firebase functions への rewrite するとエラー

run.services.get が必要