You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GitHub Actions与工作负载身份联盟创建GCP项目的权限问题

问题:GitHub Actions通过工作负载身份联盟创建GCP项目时权限被拒(403)

环境与配置

已完成的配置:

  1. GitHub Actions中使用google-github-actions/auth@v2完成工作负载身份联盟认证,认证步骤执行成功
  2. 在组织级别为GitHub仓库对应的工作负载身份主体分配了roles/resourcemanager.projectCreator角色
  3. 通过Node.js脚本调用Google Cloud Resource Manager v3 API检查并创建项目,但创建环节返回403权限错误

GitHub Actions工作流片段

- name: Google Cloud Auth
  uses: google-github-actions/auth@v2
  with:
    project_id: ${{ vars.GCP_ADMIN_PROJECT_ID }}
    workload_identity_provider: ${{ secrets.GCP_ADMIN_PROJECT_WORKLOAD_IDENTITY_PROVIDER }}

- name: Check and Maybe Create Project
  run: node ./ci-cd/scripts/create-project.js
  env:
    PROJECT_ID: ${{ env.PROJECT_ID }}
    PROJECT_DISPLAY_NAME: ${{ inputs.environment }}

项目创建脚本片段(create-project.js)

import { cleanEnv, makeValidator } from "envalid";
import { google } from "googleapis";

const nonEmptyString = makeValidator((x) => {
  if (typeof x === "string" && x.trim().length > 0) return x;
  else throw new Error("Env variable must be a non-empty string");
});
const env = cleanEnv(process.env, {
  PROJECT_ID: nonEmptyString(),
  PROJECT_DISPLAY_NAME: nonEmptyString(),
});

const createProject = async () => {
  const auth = new google.auth.GoogleAuth();
  const client = await auth.getClient();
  google.options({ auth: client });

  const cloudresourcemanager = google.cloudresourcemanager("v3");
  try {
    await cloudresourcemanager.projects.get({
      name: `projects/${env.PROJECT_ID}`,
    });
    console.log("Project already exists.");
  } catch (error) {
    try {
      const operation = await cloudresourcemanager.projects.create({
        requestBody: {
          projectId: env.PROJECT_ID,
          displayName: env.PROJECT_DISPLAY_NAME,
        },
      });
      let response;
      do {
        try {
          response = await cloudresourcemanager.operations.get({
            name: operation.name,
          });
          await new Promise((resolve) => setTimeout(resolve, 2000));
        } catch (error) {
          console.error(`Failed to get the Google Operation: ${error}`);
          throw error;
        }
      } while (!response.data.done);
    } catch (error) {
      console.error(`Failed to create the Google Project: ${error}`);
      throw error;
    }
  }
};

await createProject();

已执行的IAM授权命令

export REPO="MyOrg/myrepo"
export WORKLOAD_IDENTITY_POOL_ID="projects/111111111111/locations/global/workloadIdentityPools/github"

gcloud organizations add-iam-policy-binding "222222222222" \
  --role="roles/resourcemanager.projectCreator" \
  --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"

报错信息

Failed to create the Google Project: Error: The caller does not have permission

      error: {
        code: 403,
        message: 'The caller does not have permission',
        errors: [
          {
            message: 'The caller does not have permission',
            domain: 'global',
            reason: 'forbidden'
          }
        ],
        status: 'PERMISSION_DENIED'
      }

排查与解决方案

1. 验证IAM绑定的主体格式与正确性

工作负载身份联盟的主体格式要求严格,重点检查:

  • WORKLOAD_IDENTITY_POOL_ID必须使用项目编号而非项目ID,完整格式应为projects/[PROJECT_NUMBER]/locations/global/workloadIdentityPools/[POOL_NAME]
  • REPO的大小写需与GitHub仓库完全一致(GitHub仓库名区分大小写)
  • 执行以下命令确认组织IAM绑定是否正确:
    gcloud organizations get-iam-policy "222222222222" --flatten="bindings[].members" --format="table(bindings.role, bindings.members)" | grep "resourcemanager.projectCreator"
    

2. 检查额外权限要求

如果组织有项目创建限制,除roles/resourcemanager.projectCreator外,可能还需要:

  • 若项目需归属到特定文件夹,需在文件夹级别绑定roles/resourcemanager.folderProjectCreator角色
  • 确认组织是否启用了「仅允许指定成员创建项目」的限制,需将工作负载身份主体加入允许列表

3. 确认认证身份的有效性

在GitHub Actions中添加步骤,打印当前认证的身份信息,验证是否使用了预期主体:

- name: Verify Auth Identity
  run: |
    gcloud auth list
    gcloud config get-value account

或在Node脚本中添加日志,查看认证客户端的邮箱:

const client = await auth.getClient();
console.log("Authenticated as:", client.email);

4. 等待IAM权限生效

GCP IAM绑定通常需要1-5分钟生效,若刚执行完授权命令就运行工作流,需等待几分钟后重试。

5. 显式指定项目归属父资源

在脚本中明确指定组织作为项目的父资源,避免默认归属权限问题:

const operation = await cloudresourcemanager.projects.create({
  requestBody: {
    projectId: env.PROJECT_ID,
    displayName: env.PROJECT_DISPLAY_NAME,
    parent: {
      type: "organization",
      id: "222222222222" // 你的组织ID
    }
  },
});

6. 查看组织级审计日志

在组织级别的日志资源管理器中筛选以下内容,排查权限检查细节:

  • 日志类型:Data Access
  • 服务:cloudresourcemanager.googleapis.com
  • 操作:CreateProject
    通过日志中的authenticationInfo.principalEmail确认使用的主体是否正确,以及权限拒绝的具体原因。

内容的提问来源于stack exchange,提问作者TinyTiger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:48:14