如何授权 Cloud Build 部署到不同项目中的 Firebase 托管?

How can I authorize Cloud Build to deploy to Firebase Hosting in a different project?

我有 GCP 项目 A 和 Firebase 项目 B(在单独的 GCP 项目中)。我正在尝试在 A 中使用 Cloud Build 构建 Web 应用程序并将其部署到 B 中的 Firebase 托管。

B 的 IAM 页面中,我已授予 A<id>@cloudbuild.gserviceaccount.com 服务帐户 API Keys AdminFirebase AdminService Account User 中描述的角色.

A 使用的 Cloud Build 配置的最后一步如下:

  - id: firebase_deploy
    name: gcr.io/$PROJECT_ID/firebase
    entrypoint: sh
    args:
      - '-c'
      - |
        firebase use $_FIREBASE_PROJECT_ID
        firebase target:apply hosting prod $_FIREBASE_HOSTING_TARGET
        firebase deploy --project=$_FIREBASE_PROJECT_ID --only=hosting,firestore:rules

我将 _FIREBASE_PROJECT_ID 替换变量设置为 B,将 _FIREBASE_HOSTING_TARGET 变量设置为我用于站点的主机别名。

当我触发构建时,它失败并出现以下错误:

...
Step #3 - "firebase_deploy": Error: Invalid project selection, please verify project B exists and you have access.
Step #3 - "firebase_deploy":
Step #3 - "firebase_deploy": Error: Must have an active project to set deploy targets. Try firebase use --add
Step #3 - "firebase_deploy":
Step #3 - "firebase_deploy": Error: Failed to get Firebase project B. Please make sure the project exists and your account has permission to access it.
Finished Step #3 - "firebase_deploy"

我怀疑问题可能出在我没有先 运行 Firebase CLI 的额外登录步骤。为此,我似乎需要在本地 运行 firebase login:ci 生成一个令牌,然后通过 FIREBASE_TOKEN 环境变量 as described in the docs 传递它,但是与令牌似乎比需要的更广泛:

构建过程应该只能访问 Firebase 项目 B,而不是“我所有的 Firebase 数据和设置”和“我的 Google 云数据”。

  1. 这里有什么办法可以避免运行 firebase login吗?似乎服务帐户应该已经有足够的访问权限来部署到 Firebase 托管。
  2. 如果我需要 运行 firebase login,有什么方法可以创建一个有限范围的令牌(假设我对默认范围的理解是正确的)?

(我还在 A 中为 B 的服务帐户赋予了 Cloud Functions Developer 角色,并且能够成功 运行 gcloud --project=$_FIREBASE_PROJECT_ID functions deploy ...不同的构建配置。我还使用类似于上述配置的 Cloud Build 配置部署到同一 GCP 项目中的 Firebase 托管,因此我怀疑 firebase login 在所有情况下都不是必需的。)

我找到了一种方法,可以让项目 A 的 Cloud Build 服务帐户部署到 B,而无需过多的权限。

首先,我在 B 下创建了一个名为 deploy 的服务帐户,然后 g运行 将其命名为 Firebase Hosting AdminFirebase Rules AdminCloud Datastore Index Admin 角色。 (我不确定是否需要 Datastore 角色,但控制台显示它是最近使用的,所以我保留了它。)

接下来,我为新服务帐户生成了一个 JSON 密钥,将其粘贴(包括换行符和 double-quotes)作为名为 _DEPLOY_CREDENTIALS 的替换变量,并更新了构建步骤将其复制到环境中:

 - id: firebase_deploy
    name: gcr.io/$PROJECT_ID/firebase
    entrypoint: bash
    args: ['-e', '--', 'build/deploy_hosting.sh']
    env:
      - DEPLOY_CREDENTIALS=$_DEPLOY_CREDENTIALS
      - FIREBASE_PROJECT_ID=$_FIREBASE_PROJECT_ID
      - FIREBASE_HOSTING_TARGET=$_FIREBASE_HOSTING_TARGET

deploy_hosting.sh 中,我将凭据写入一个临时文件,然后通过 GOOGLE_APPLICATION_CREDENTIALS 环境变量将它们传递给 firebase 命令:

#!/bin/bash

set -e
    
CREDS=$(mktemp -t creds.json.XXXXXXXXXX)
printenv DEPLOY_CREDENTIALS >"$CREDS"
export GOOGLE_APPLICATION_CREDENTIALS=$CREDS
      
firebase --debug use "$FIREBASE_PROJECT_ID"
firebase target:apply hosting prod "$FIREBASE_HOSTING_TARGET"
firebase deploy --project="$FIREBASE_PROJECT_ID" --only=hosting,firestore:rules

我为该步骤创建了一个单独的 shell 脚本,因为我 运行 遇到了在直接从构建步骤编写凭据时从凭据中删除引号的问题。有可能将凭据存储在 Secret Manager 中,但这对我的用例来说有点过分了。

我仍然很好奇是否有办法让 A 的服务帐户部署到 B 而无需在 B 中使用服务帐户而 运行 firebase 可执行文件。