Cloud Buildトリガーのサービスアカウントをデフォルトから作成したものへ付け替える
Google Cloud の Cloud Build はアプリケーションをビルドするためのサービスです。Cloud Run や GKE へアプリケーションをデプロイする現場では、使用頻度の高いサービスかと思います。そんなCloud Buildのデフォルトの動作に変更が入ります。2024年の4月29日以降に新しく作成された Google Cloud のプロジェクトでは、Cloud Build トリガー作成時に自動で関連づけられるサービスアカウントがCloud Build サービスアカウント
から、Compute Engine サービスアカウント
に変更されます。
変更点
Cloud Build を利用する場合、トリガーというリソースを作ります。トリガーは、
- ビルドの発動条件
- どこからソースをとってくるか
- ビルドファイル名
といった情報を持っており、Cloud Buildはそれを元にビルドを実行します。トリガーには他にサービスアカウントを指定することができ、ビルド実行中に適用する権限を制限できます。このサービスアカウントですが、もちろん自分で作成したサービスアカウントを明示的に指定することもできる一方、「何も指定しない」という状態にもできます。もしかしたら指定しないことのほうが多いかもしれません。
今回言っている変更というのが、このサービスアカウント未指定の動作です。これまでは、何も指定しないとCloud Build サービスアカウント
が自動的に使われるようになっていました。それが、4月29日以降に新しく作成されたプロジェクトについては、Compute Engine サービスアカウント
が選択されるようになります。Cloud Build サービスアカウント
もCompute Engine サービスアカウント
も、Google Cloud のプロジェクトを作成すると自動的に作成されるサービスアカウントです。
なぜ変更されるのか
ドキュメントには、
これらの変更により、今後のお客様のデフォルト セキュリティ対策が改善されます。
としか記載がありません。私の予想になってしまいますが、強めの権限をデフォルトで持ってしまっているCloud Build サービスアカウント
を廃止したいのだと思います。Cloud Build はアプリケーションのビルドに集中するべく、暗黙的なふるまいがいくつかあり、そのひとつに Cloud Storage バケットへのログ出力 があります。Cloud Build サービスアカウント
では、Cloud Storage バケットへの書き込みをとくに意識しなくても可能なように、書き込み権限が付与されているようです。結果として、必要以上にデフォルトサービスアカウントの権限が強くなっています。4月29日以降に作成されるプロジェクトには、Cloud Build サービスアカウント
はつくられないかもしれません。
これまでのプロジェクトはどうなる?
すでに作成されているプロジェクトについては、この件について何もしなくても支障はありません。しかし、たとえばTerraformなどのIaCでCloud Buildの構成を組んでおり、サービスアカウントが未指定、つまりCloud Build サービスアカウント
を使用しているとします。何らかの事情で4月29日以降に新しいプロジェクトを作ることになり、同じCloud Build設定をIaCから適用すると、こちらはCompute Engine サービスアカウント
が設定されるため、期待する権限と異なるかもしれません。
なので、余裕があるときにCloud Build用のサービスアカウントを作成し、トリガー作成時にそれを明示的に指定すると良いと思います。この記事ではサービスアカウントをTerraformで作成してトリガーへ設定しなおすスニペットを記録します。
やること
Cloud Build用のサービスアカウントを作成し、それを使ってビルドできるようになるまで、以下の作業が必要でした。
- Cloud Build 用のサービスアカウントを作成する
- Artifact Registry でサービスアカウントの IAM Binding を追加する
- Cloud Build トリガーでサービスアカウントを指定する
-
cloudbuild.yml
でロギングオプションを指定する
1. Cloud Build 用のサービスアカウントを作成する
イチから必要なロールを調べ直すのは大変だと思うので、Cloud Build サービスアカウント
に設定されているロールを抽出し、それをそのまま指定してあげると楽できそうです。
Cloud Build サービスアカウント
に設定されているロールの抽出
まず、Cloud Build サービスアカウント
のアカウント名を調べます。IAMの画面で、Google 提供のロール付与を含める
チェックを有効にすると出現します。名前が「Cloud Build Service Account」となっているはずなので、そのプリンシパル名を控えておきます。
つぎに、ロールを調べます。以下のgcloud
コマンドで調べられます。Cloud Shellなどで実行してみてください。xxxxxxxxxxx@cloudbuild.gserviceaccount.com
となっている箇所に先ほど調べた Cloud Build のプリンシパル名を指定します。
gcloud projects get-iam-policy <project-name> \
--flatten="bindings[].members" --format='table(bindings.role)' \
--filter="bindings.members:xxxxxxxxxxx@cloudbuild.gserviceaccount.com"
ROLE: roles/appengine.deployer
ROLE: roles/appengine.serviceAdmin
ROLE: roles/cloudbuild.builds.builder
...
このロールを転記して新しいサービスアカウントを作ります。このコマンドはStack Overflowで知りました。
新しいサービスアカウントの作成(Terraform)
Terraformで書きます。
variable "project_id" {
description = "GCPのproject_idです"
type = string
}
resource "google_service_account" "cloud_build" {
project = var.project_id
account_id = "cloud-build"
display_name = "Cloud Buildのサービスアカウント"
}
resource "google_project_iam_member" "cloud_build_appengine_deployer" {
project = var.project_id
role = "roles/appengine.deployer"
member = "serviceAccount:${google_service_account.cloud_build.email}"
}
ロール分だけgoogle_project_iam_member
を定義するのですが…ロールが多いと大変かもしれません。参考程度に、以下のスクリプトをブラウザで実行すると少し楽できます。適宜改造して使ってください。
// ここに必要なロールをすべて記載する
const roles = ["roles/appengine.deployer",
"roles/appengine.serviceAdmin",
"roles/cloudbuild.builds.builder",
"roles/cloudfunctions.developer"]
// Terraform記述が出力されるのでコピペして使う
roles.forEach(role => {
console.log(`
resource "google_project_iam_member" "cloud_build_${role.split('/')[1].replaceAll('.','_')}" {
project = var.project_id
role = "${role}"
member = "serviceAccount:\${google_service_account.cloud_build.email}"
}
`)
})
サービスアカウントの定義が作成できたら、terraform apply
で作成してOKです。
2. Artifact Registry でサービスアカウントの IAM Binding を追加する
Zennでは、Cloud Run へアプリケーションをデプロイしており、アプリの元になるDockerイメージはArtifact Registryへ保管して使っています。Dockerイメージはビルド時に Cloud Build から Artifact Registry へプッシュしています。つまり、新しく作成したサービスアカウントが、Artifact Registry へ書き込める必要があります。Artifact Registry 側へ設定を追加しましょう。こちらもTerraformで作業します。
variable "gcp_project_id" {
type = string
}
+variable "cloud_build_service_account_email" {
+ type = string
+ description = "Cloud Build サービスアカウントのメールアドレス"
+}
variable "artifact_registry_location" {
type = string
# https://cloud.google.com/storage/docs/locations
description = "Artifact Registry のロケーションをどこにするか"
}
# アプリケーション用の Artifact Registry リポジトリ
resource "google_artifact_registry_repository" "myapp" {
project = var.gcp_project_id
location = var.artifact_registry_location
repository_id = "myapp"
description = "myappアプリケーション"
format = "DOCKER"
}
# ビルドプロジェクトの Cloud Build と Cloud Run からのアクセスを許可する。
resource "google_artifact_registry_repository_iam_binding" "binding_app_and_builder" {
project = var.gcp_project_id
location = var.artifact_registry_location
repository = google_artifact_registry_repository.myapp.name
role = "roles/artifactregistry.repoAdmin"
members = [
# Cloud Buildからプッシュできるように
"serviceAccount:xxxxxxxxxxxx@cloudbuild.gserviceaccount.com",
+ "serviceAccount:${var.cloud_build_service_account_email}",
]
}
terraform apply
します。
3. Cloud Build トリガーでサービスアカウントを指定する
こちらもTerraformで追記します。
variable "project_id" {
description = "プロジェクトID"
type = string
}
+variable "cloud_build_service_account_id" {
+ description = "Cloud Build に設定するサービスアカウントのIDです"
+ type = string
+}
variable "cloud_run_service_account" {
description = "Cloud Run に設定するバックエンドのサービスアカウントです"
type = string
}
resource "google_cloudbuild_trigger" "deploy-cloudrun" {
name = "deploy-cloudrun"
description = "アプリケーションをCloud Runへデプロイする"
+ service_account = var.cloud_build_service_account_id
github {
owner = "zenn-dev"
name = "application"
push {
branch = "main"
}
}
included_files = ["*"]
filename = "cloudbuild.yml"
substitutions = {
_CLOUD_RUN_SERVICE_ACCOUNT = var.cloud_run_service_account
_SHORT_BUILD_ID = "$${BUILD_ID:0:8}"
}
}
terraform apply
するとCloud Buildトリガーにサービスアカウントが追加されます。
cloudbuild.yml
でロギングオプションを指定する
4. cloudbuild.yml
にも修正が必要です。ロギングオプションを明示する必要があります。ざっくり、Cloud Loggingへ出力するか、Cloud Storageに出力するかを指定できますが、今回はCloud Loggingに記録するよう設定しました。デフォルトの動作はCloud LoggingとCloud Storageバケットへ両方出力する設定なので、以下の設定を踏襲すると挙動がかわる点に注意してください。
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'us-east1-docker.pkg.dev/myproject/myimage', '.']
+ options:
+ logging: CLOUD_LOGGING_ONLY
ここまで材料を揃えたうえでビルドを実行すると、作成したサービスアカウントを使って完遂できるはずです。
おわりに
既存のプロジェクトで Cloud Build トリガーのサービスアカウントを変更する手順を記録しました。やってみると、暗黙的に設定されている動作や権限がいくつかあり、それを知るいい機会にもなりました。Cloud Build は今後もヘビーユースしていくため、少し詳しくなれた点は良かったと思います。本記事がどなたかの参考になれば幸いです。
Discussion