GCPのCloud FunctionsのGitHub Actions上からのデプロイを、Workload Identity連携で認証してやってみる
方式について、概要の記事は以下。
GitHub Actionsの公式アクションは以下。
"(Preferred) Direct Workload Identity Federation" の方法だとデプロイステップで以下のエラーがでてうまくいかなかった。
Created zip file from 'function' at '/tmp/cfsrc-7ac161f56a859b8e3a675a94.zip'
Error: google-github-actions/deploy-cloud-functions failed with: failed to upload zip file: Failed to POST https://cloudfunctions.googleapis.com/v1/projects/colomney-my-pet-melody-dev/locations/asia-east1/functions:generateUploadUrl: (403) {
"error": {
"code": 403,
"message": "Permission 'cloudfunctions.functions.sourceCodeSet' denied on resource 'projects/colomney-my-pet-melody-dev/locations/asia-east1' (or resource may not exist).",
"status": "PERMISSION_DENIED"
}
}
"Workload Identity Federation through a Service Account" の方法で試してみる。
サービスアカウントを作成し、「Functions 管理者」で権限を付与する。
以下のエラーが出た。
Created zip file from 'function' at '/tmp/cfsrc-cb36cb11604d364859f7b5a0.zip'
Error: google-github-actions/deploy-cloud-functions failed with: failed to upload zip file: Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).
権限が付与されていないようなので、以下コマンドを実行してみる。
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/iam.serviceAccountTokenCreator" \
--member="serviceAccount:deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com"
同じエラーが出た。
Created zip file from 'function' at '/tmp/cfsrc-cb36cb11604d364859f7b5a0.zip'
Error: google-github-actions/deploy-cloud-functions failed with: failed to upload zip file: Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).
全然解決の糸口が掴めないので、最初からドキュメント通りにやってみる
サービスアカウントを作成する。
gcloud iam service-accounts create "deploy-functions-from-github" \
--project "${PROJECT_ID}"
- (Optional) Create a Google Cloud Service Account. If you already have a Service Account, take note of the email address and skip this step.
必要な権限を付与する。
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/cloudfunctions.admin" \
--member="serviceAccount:deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com"
- Create a Workload Identity Pool:
gcloud iam workload-identity-pools create "github-2" \
--project="${PROJECT_ID}" \
--location="global" \
--display-name="GitHub Actions Pool"
削除した識別子と被っているとエラーが出たので、 "github-2" としている。
- Get the full ID of the Workload Identity Pool:
gcloud iam workload-identity-pools describe "github-2" \
--project="${PROJECT_ID}" \
--location="global" \
--format="value(name)"
結果をメモしておく。
- Create a Workload Identity Provider in that pool:
gcloud iam workload-identity-pools providers create-oidc "my-pet-melody" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github-2" \
--display-name="My Pet Melody repo" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
--issuer-uri="https://token.actions.githubusercontent.com"
- Allow authentications from the Workload Identity Pool to your Google Cloud Service Account.
gcloud iam service-accounts add-iam-policy-binding "deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
- Extract the Workload Identity Provider resource name:
gcloud iam workload-identity-pools providers describe "my-pet-melody" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github-2" \
--format="value(name)"
結果をGitHub ActionsのSecretsに登録する。
Additionally, the deployment service account must have permissions to act as (impersonate) the runtime service account, which can be achieved by granting the deployment service account "roles/iam.serviceAccountUser" permissions on the runtime service account. If unspecified, the runtime service account is the App Engine Default Service Account PROJECT_ID@appspot.gserviceaccount.com.
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/iam.serviceAccountUser" \
--member="serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com"
同じエラー。うーん
Created zip file from 'function' at '/tmp/cfsrc-262af1d3b4b345a0a8891410.zip'
Error: google-github-actions/deploy-cloud-functions failed with: failed to upload zip file: Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).
トークン作成の権限を付与してみる。
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/iam.serviceAccountTokenCreator" \
--member="serviceAccount:deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com"
同じエラー。
CloudFunctionsのデフォルトサービスアカウントに権限を付与してみる。
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/iam.serviceAccountTokenCreator" \
--member="serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com"
同じエラー。
5の手順で $REPO の指定方法が間違っているようだった。
my-org/my-repo
としないといけないところを、 my-repo
だけにしていた。
# ${REPO} is the full repo name including the parent GitHub organization,
# such as "my-org/my-repo".
指定を変えてポリシーを付与し、再度実行するとエラーの内容が変わった。
Error: google-github-actions/deploy-cloud-functions failed with: failed to PATCH https://cloudfunctions.googleapis.com/v1/projects/colomney-my-pet-melody-dev/locations/asia-east1/functions/detect?updateMask=availableMemoryMb,buildEnvironmentVariables,buildWorkerPool,description,dockerRegistry,dockerRepository,entryPoint,environmentVariables,eventTrigger,httpsTrigger,ingressSettings,kmsKeyName,labels,maxInstances,minInstances,name,network,runtime,secretEnvironmentVariables,secretVolumes,serviceAccountEmail,timeout,vpcConnector,vpcConnectorEgressSettings,sourceUploadUrl: (403) {
"error": {
"code": 403,
"message": "Caller *** is missing permission 'iam.serviceaccounts.actAs' on service account colomney-my-pet-melody-dev@appspot.gserviceaccount.com. Grant the role 'roles/iam.serviceAccountUser' to the caller on the service account colomney-my-pet-melody-dev@appspot.gserviceaccount.com. You can do that by running 'gcloud iam service-accounts add-iam-policy-binding colomney-my-pet-melody-dev@appspot.gse
エラーメッセージの通りに権限を付与してみる
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/iam.serviceaccounts.actAs" \
--member="serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com"
そんな権限はないとエラーが出る
ERROR: Policy modification failed. For a binding with condition, run "gcloud alpha iam policies lint-condition" to identify issues in condition.
ERROR: (gcloud.projects.add-iam-policy-binding) INVALID_ARGUMENT: Role roles/iam.serviceaccounts.actAs is not supported for this resource.
以下を参考に権限を変えて試してみる。
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/iam.serviceAccountUser" \
--member="serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com"
さらにエラーメッセージ通りに付与してみる。
gcloud iam service-accounts add-iam-policy-binding \
--project="${PROJECT_ID}" \
"${PROJECT_ID}@appspot.gserviceaccount.com" \
--member="serviceAccount:deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountUser"
これでうまくいった。
商用環境デプロイするついでにもう一度、成功した一連の手順を記載する。
商用環境のデプロイ
export PROJECT_ID="colomney-my-pet-melody"
gcloud iam service-accounts create "deploy-functions-from-github" \
--project="${PROJECT_ID}"
gcloud projects add-iam-policy-binding \
"${PROJECT_ID}" \
--role="roles/cloudfunctions.admin" \
--member="serviceAccount:deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam workload-identity-pools create "github-2" \
--project="${PROJECT_ID}" \
--location="global" \
--display-name="GitHub Actions Pool"
gcloud iam workload-identity-pools describe "github-2" \
--project="${PROJECT_ID}" \
--location="global" \
--format="value(name)"
export GITHUB_ORG="shotaIDE"
gcloud iam workload-identity-pools providers create-oidc "my-pet-melody" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github-2" \
--display-name="My Pet Melody repo" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
--issuer-uri="https://token.actions.githubusercontent.com"
export WORKLOAD_IDENTITY_POOL_ID="projects/340853471718/locations/global/workloadIdentityPools/github-2"
export REPO="${GITHUB_ORG}/my-pet-melody"
gcloud iam service-accounts add-iam-policy-binding "deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
gcloud iam workload-identity-pools providers describe "my-pet-melody" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github-2" \
--format="value(name)"
gcloud iam service-accounts add-iam-policy-binding \
--project="${PROJECT_ID}" \
"${PROJECT_ID}@appspot.gserviceaccount.com" \
--member="serviceAccount:deploy-functions-from-github@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountUser"
結論として、以下が原因で詰まっていた。
-
REPO
に指定すべき値が間違っていた - Functionsの標準サービスアカウントに対し、Functionsのデプロイ用のサービスアカウントとして振る舞う権限を付与する必要があるため、その点注意してコマンドを組み立てる必要がある
ドキュメントはしっかり読みましょうというのと、コマンドの内容はしっかり理解しましょう、という教訓だった。