使用GitHub Actions与工作负载身份联盟创建GCP项目的权限问题
问题:GitHub Actions通过工作负载身份联盟创建GCP项目时权限被拒(403)
环境与配置
已完成的配置:
- GitHub Actions中使用
google-github-actions/auth@v2完成工作负载身份联盟认证,认证步骤执行成功 - 在组织级别为GitHub仓库对应的工作负载身份主体分配了
roles/resourcemanager.projectCreator角色 - 通过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
相关产品推荐
相关产品推荐

