株式会社G-genのブログ - TECH PLAY

TECH PLAY

株式会社G-gen

株式会社G-gen の技術ブログ

全888件

G-gen の三浦です。当記事では、コンテキストアウェア アクセス(CAA)を使って Google ドライブ等の Google Workspace アプリケーションへのアクセスを制御する方法を紹介します。 コンテキストアウェア アクセスとは 前提条件 検証内容 動作確認 モニターモードの設定 動作確認(モニターモード) アクセスレベル変更(アクティブモード) 動作確認(アクティブモード) 複合条件の設定 複合条件の動作確認(アクティブモード) コンテキストアウェア アクセスとは コンテキストアウェア アクセス(以降、CAA)は、IP アドレスやデバイスの状態などのコンテキスト(背景情報)に基づいてアクセスを制御する、Google Workspace の機能です。 Google ドライブ、Gmail、Google カレンダー、Looker Studio などに条件付きでアクセス制御を適用できます。 使用例 : IP アドレス制限 : 社内ネットワークの IP アドレスからのみ Google ドライブへのアクセスを許可し、社外(自宅や公共 Wi-Fi)からの利用を禁止する。 デバイス制限 : 会社支給のモバイルデバイス(iPhone、Android)からのみ Gmail へのアクセスを許可し、私用デバイスからの利用を禁止する。 参考 : コンテキストアウェア アクセスの概要 前提条件 CAA は、Google Workspace(Cloud Identity)の特定のエディション(Frontline Standard、Enterprise Standard、Enterprise Plus、Cloud Identity Premium 等)で利用できます。 詳細は以下のドキュメントをご参照ください。 参考 : コンテキストアウェア アクセスでビジネスを保護する 検証内容 以下の手順で CAA を設定し、動作を確認します。 モニターモードの設定 社内 IP アドレスのみにアクセスを制限するルールを作成し、モニターモード(検知のみでブロックしない)で設定します。 動作確認(モニターモード) 社内外のアクセス状況を確認し、ログを確認します。 アクセスレベル変更(アクティブモード) アクティブモードに切り替え、条件外のアクセスをブロックするように設定します。 動作確認(アクティブモード) 社内 IP アドレスからアクセス可能で、社外からはブロックされることを確認します。 複合条件の設定 社内 IP アドレスに加え、多要素認証(MFA)を利用している場合のみ許可する条件を設定します。 複合条件の動作確認(アクティブモード) 条件に合致しないアクセスがブロックされることを確認します。 動作確認 モニターモードの設定 Google Workspace の管理コンソール(URL : https://admin.google.com )にログインします。 参考 : 管理コンソールにログインする [セキュリティ] > [アクセスとデータ管理] > [コンテキストアウェア アクセス] に移動します。 コンテキストアウェア アクセスへ移動 CAA が無効な場合は、[有効にする] を選択して有効化します。その後 [アクセスレベル] を選択します。 有効化とアクセスレベルの選択 [アクセスレベルを作成] を選択します。 アクセスレベルを作成を選択 以下を設定し、[作成] を選択します。 アクセスレベル名 :任意の名前を入力します。 条件 :[基本] > [IP サブネット] を選択し、社内 IP アドレスを入力します。 1つ目のアクセスレベルの作成 [アプリに割り当て] を選択します。 アプリに割り当てを選択 適用する対象(ユーザーまたはグループまたは組織部門)とアプリを選択し、[割り当て] を選択します。 適用対象の選択 アクセスレベルを選択し、「監視」にチェックを入れて「続行」を選択します。 モニターモード(監視) では、アクセスレベルの影響範囲をログで確認でき、ブロックは行われません。 なおアクセスレベルは複数選択できますが、 OR 条件 で動作するため、いずれかのアクセスレベルを満たす場合は接続が許可されます。 参考 : アプリにコンテキストアウェア アクセスレベルを割り当てる アクセスレベルとモードの選択 [続行] を選択します。 続行を選択 内容を確認し、[割り当て] を選択します。 モニターモードの適用 動作確認(モニターモード) 社内および社外から Google ドライブ、Google カレンダー、Gmail にアクセスします。この段階では、どちらからもアクセス可能です。 アクセス確認 [レポート] > [監査と調査] > [コンテキストアウェア アクセスのログイベント] を選択します。 ログイベントの選択 以下の条件で検索し、モニターモードでブロックされたユーザーのログを確認します。意図しないブロックが発生していないか確認してください。 イベント 次に一致 アクセス拒否(モニターモード) アクセスレベルの適用 次の文字を含む アクセスレベル名 モニターモードのログ確認 アクセスレベル変更(アクティブモード) [セキュリティ] > [コンテキストアウェア アクセス] へ移動し、 [アプリにアクセスレベルを割り当てる] を選択します。 アプリにアクセスレベルを割り当てるを選択 アクセスレベルを適用する対象とアプリを選択し、[割り当て] を選択します。 割り当てを選択 監視 のチェックを外し、 アクティブ のチェックを入れて [続行] を選択します。 アクティブモードへの変更 [続行] を選択し、内容を確認し、[割り当て] を選択します。 続行を選択 アクティブモードの適用 動作確認(アクティブモード) 社内 IP アドレス及び社外 IP アドレスからアクセスします。社内 IP アドレスでは正常にアクセスでき、社外 IP アドレスではアクセスがブロックされることを確認します。 ブロック確認(IP サブネット) 複合条件の設定 [セキュリティ] > [コンテキストアウェア アクセス] に移動し、[アクセスレベル] > [アクセスレベルを作成] を選択します。 アクセスレベルを選択 アクセスレベルを作成を選択 以下を設定し [作成] を選択します。この条件により、 MFA 認証がされていないとアクセスがブロック されます。条件式の詳細については、以下のドキュメントを参照してください。 アクセスレベル名 :任意の名前を入力します。 条件 :[詳細] > request.auth.claims.crd_str.mfa == true 参考 : カスタム アクセスレベルの仕様 参考 : 詳細モードでのコンテキストアウェア アクセスの例 2つ目のアクセスレベルの作成 [終了] を選択します。 終了を選択 [コンテキストアウェア アクセス] > [アクセスレベル] へ移動し、1つ目と2つ目のルールの CEL名 を確認し、控えます。 CEL名の確認 [アクセスレベルを作成] を選択します。 [アクセスレベルを作成] を選択 以下を設定し [作成] を選択します。この条件により、 複数の条件(IP アドレス制限と多要素認証(MFA))を同時に満たす場合のみ アクセスが許可されます。 アクセスレベル名 :任意の名前を入力します。 条件 :[詳細] > levels.${{1つ目のアクセスレベルの CEL 名}} && levels.${{2つ目のアクセスレベルの CEL 名}} 3つ目のアクセスレベルの作成 [セキュリティ] > [コンテキストアウェア アクセス] へ移動し、 [アプリにアクセスレベルを割り当てる] を選択します。 アプリにアクセスレベルを割り当てるを選択 アクセスレベルを適用する対象とアプリを選択し、[割り当て] を選択します。 割り当てを選択 適用済みの1つ目のアクセスレベルを削除し、3つ目のアクセスレベルを選択し、 アクティブ のチェックを入れて、[続行] を選択します。 本番環境へ適用する場合は、まず [監視] のみにチェックを入れてモニターモードで影響がないことを確認した上で、アクティブモードに切り替えることを推奨します。 1つ目のアクセスレベルの削除 3つ目のアクセスレベルの選択 [続行] を選択し、内容を確認し、[割り当て] を選択します。 続行を選択 3つ目のアクセスレベルの適用 複合条件の動作確認(アクティブモード) 社内の IP アドレスかつ MFA が無効化されているアカウントからアクセスを確認し、以下画面が表示されることを確認します。 MFA が無効なアカウント ブロック確認(複合条件) [レポート] > [監査と調査] > [コンテキストアウェア アクセスのログイベント] を選択します。 ログイベントの選択 以下の条件で検索し、アクティブモードで拒否された接続ログを確認します。 アクセスレベルの不足 を確認することで、どのアクセスレベルでブロックされたかを特定できます。 イベント 次に一致 アクセスが拒否されました アクセスレベルの適用 次の文字を含む アクセスレベル名 ログの確認 アカウントのMFAを有効化後、ログアウトし、MFAを利用して再度ログインします。 ログイン時に このデバイスでは次回から表示しない を選択しないでください。選択すると、次回以降のログインで MFA 認証が省略され、CAA によってアクセスがブロックされます。 万が一選択してしまった場合は、以下のドキュメントを参照してログイン Cookie をリセットしてください。 参考 : 管理対象の Google アカウントからログアウトする MFA を利用してログイン Googleドライブにアクセスし、正常に表示されることを確認します。 アクセス確認 三浦 健斗 (記事一覧) クラウドソリューション部 2023年10月よりG-genにジョイン。元オンプレ中心のネットワークエンジニア。ネットワーク・セキュリティ・唐揚げ・辛いものが好き。
G-gen の武井です。当記事では Privileged Access Manager を Terraform で管理する方法について紹介します。 はじめに 概要 Privileged Access Manager (PAM) PAM に必要な権限 利用資格の管理 利用資格の利用 (申請、承認) 全体構成 連携方式 ソースコード Direct Workload Identity および GitHub Actions ワークフロー Terraform ディレクトリ構成 env 配下 (呼び出し側) modules 配下 (モジュール) デプロイ terraform plan terraform apply リソース 動作確認 申請 承認 権限付与 権限はく奪 再申請 はじめに 概要 当記事では Google Cloud における一時的な IAM 権限付与を実現する仕組みである Privileged Access Manager (以降、PAM) を、Terraform と GitHub Actions による CI/CD で管理する方法を紹介します。 当記事で実現するのは、PAM の仕組みのデプロイです。API の有効化、サービスエージェントへの権限付与、利用資格 (entitlements) の作成などを Terraform で行うことで、デプロイ以降は PAM を使った承認フローにより、組織の IAM 権限を管理することができるようになります。 Privileged Access Manager (PAM) PAM の詳細は以下の記事で解説しています。 blog.g-gen.co.jp PAM での権限管理の仕組みを端的にまとめると、 利用資格 (entitlements) という設定情報にもとづき、一時的な権限の付与を行うものです。 利用資格(entitlements)は PAM のオブジェクトであり、承認フローと言い換えることもできます。利用資格には「付与する IAM ロール」「権限を付与する最大時間」「誰が権限をリクエストできるか」「誰がリクエストを承認できるか」「誰が通知を受け取るか」などを定義できます。 承認フローは以下のようになります。 申請者は 必要な権限、期間、その理由 を利用資格に明記し、承認者に提出する 承認者は、その妥当性を確認し申請を 承認もしくは否認 する 承認された場合、申請者に対し一定期間権限が付与される 所定の期間が経過すると、申請者に付与されていた権限は自動的にはく奪される PAM に必要な権限 PAM に必要な権限 (IAM ロール) については以下の通りです。 参考: Privileged Access Manager の権限と設定 利用資格の管理 利用資格を 管理(作成、更新、削除) するプリンシパルには、 Privileged Access Manager 管理者 ( roles/privilegedaccessmanager.admin ) が必要です。 また、利用資格を 組織ツリーの中のどこで 利用するかによって、以下のいずれかの権限が必要です。 組織全体:セキュリティ管理者( roles/iam.securityAdmin ) フォルダ:フォルダ IAM 管理者( roles/resourcemanager.folderIamAdmin ) プロジェクト:Project IAM 管理者( roles/resourcemanager.projectIamAdmin ) 利用資格の利用 (申請、承認) 利用資格を用いて、権限付与を申請する、あるいは申請を承認するプリンシパルには、 Privileged Access Manager 閲覧者 ( roles/privilegedaccessmanager.viewer ) が必要です。 全体構成 当記事では利用資格を Terraform と GitHub Actions で管理します。 利用資格は 組織/フォルダ/プロジェクトレベル で設定可能ですが、今回は組織とプロジェクトレベルに PAM をデプロイします。 連携方式 Google Cloud と GitHub Actions の連携には Direct Workload Identity を使用します。 サービスアカウントキーやサービスアカウントの権限を借用する従来方式とは異なり、Workload Identity プールに必要な IAM 権限を直接付与します。 参考 : Workload Identity Federation ソースコード Direct Workload Identity および GitHub Actions ワークフロー 以下の記事で Direct Workload Identity を作成する bash スクリプトと GitHub Actions のワークフローを掲載しています。 blog.g-gen.co.jp なお、上記の記事に掲載したスクリプトで作成される Workload Identity プールに対しては、組織レベルで以下の IAM ロールを付与しており、利用資格の管理に必要な権限を包含しています。 オーナー ( roles/owner ) 組織管理者 ( roles/resourcemanager.organizationAdmin ) Terraform PAM のソースコードは以下のディレクトリ構成にもとづきます。 参考: google_privileged_access_manager_entitlement ディレクトリ構成 . ├── env │ ├── Test_Environment │ │ └── yutakei │ │ ├── backend.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ └── versions.tf │ └── organization │ ├── backend.tf │ ├── locals.tf │ ├── main.tf │ └── versions.tf └── modules ├── apis │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── pam ├── main.tf ├── outputs.tf └── variables.tf env 配下 (呼び出し側) # backend.tf terraform { backend "gcs" { bucket = "common-tfstate" prefix = "terraform/organization/state" } } # locals.tf locals { organization_id = "1234567890" # 利用申請(entitlements)の設定 entitlements = { pam_org1 = { entitlement_id = "pam-organization-acm-demo" max_request_duration = "3600s" eligible_users = [ "user:demo-user01@dev.g-gen.co.jp" ] resource_type = "cloudresourcemanager.googleapis.com/Organization" resource = "//cloudresourcemanager.googleapis.com/organizations/1234567890" roles = [ "roles/accesscontextmanager.gcpAccessReader" , "roles/accesscontextmanager.policyReader" ] require_approver_justification = true approvals_needed = 1 approvers = [ "user:demo-user01@g-gen.co.jp" ] } } } # main.tf # 組織レベルでPAMを有効にするには、PAMサービスアカウントにPAMサービスエージェントロールが必要 resource "google_organization_iam_member" "pam_service_agent" { org_id = local.organization_id role = "roles/privilegedaccessmanager.serviceAgent" member = "serviceAccount:service-org-$ { local.organization_id } @gcp-sa-pam.iam.gserviceaccount.com" } module "pam" { source = "../../modules/pam" entitlements = local.entitlements parent = "organizations/$ { local.organization_id } " location = "global" } # versions.tf terraform { required_version = "~> 1.9.7" required_providers { google = { source = "hashicorp/google" version = "~> 6.6.0" } } } provider "google" { user_project_override = true } # backend.tf terraform { backend "gcs" { bucket = "common-tfstate" prefix = "terraform/yutakei/state" } } # locals.tf locals { project_id = "yutakei" apis = [ "privilegedaccessmanager.googleapis.com" , ] # 利用申請(entitlements)の設定 entitlements = { pam1 = { entitlement_id = "pam-yutakei-bigquery-demo" max_request_duration = "3600s" eligible_users = [ "user:demo-user01@dev.g-gen.co.jp" ] resource_type = "cloudresourcemanager.googleapis.com/Project" resource = "//cloudresourcemanager.googleapis.com/projects/yutakei" roles = [ "roles/bigquery.jobUser" , "roles/bigquery.dataViewer" ] require_approver_justification = true approvals_needed = 1 approvers = [ "user:demo-user01@g-gen.co.jp" ] } pam2 = { entitlement_id = "pam-yutakei-gcs-demo" max_request_duration = "3600s" eligible_users = [ "user:demo-user01@dev.g-gen.co.jp" ] resource_type = "cloudresourcemanager.googleapis.com/Project" resource = "//cloudresourcemanager.googleapis.com/projects/yutakei" roles = [ "roles/storage.admin" ] require_approver_justification = true approvals_needed = 1 approvers = [ "user:demo-user01@g-gen.co.jp" ] } } } # main.tf module "apis" { source = "../../../modules/apis" project_id = local.project_id apis = local.apis } module "pam" { source = "../../../modules/pam" entitlements = local.entitlements parent = "projects/$ { local.project_id } " location = "global" } # versions.tf 割愛 modules 配下 (モジュール) # main.tf resource "google_privileged_access_manager_entitlement" "pam" { for_each = var.entitlements entitlement_id = each.value.entitlement_id location = var.location max_request_duration = each.value.max_request_duration parent = var.parent requester_justification_config { unstructured {} } eligible_users { principals = each.value.eligible_users } privileged_access { gcp_iam_access { resource_type = each.value.resource_type resource = each.value.resource # 複数のrole_bindingsを生成 dynamic "role_bindings" { for_each = each.value.roles content { role = role_bindings.value } } } } approval_workflow { manual_approvals { require_approver_justification = each.value.require_approver_justification steps { approvals_needed = each.value.approvals_needed approvers { principals = each.value.approvers } } } } } # outputs.tf output "entitlement_ids" { description = "List of entitlement IDs created" value = [ for entitlement in google_privileged_access_manager_entitlement.pam : entitlement.entitlement_id ] } variable "entitlements" { description = "A map of entitlement configurations" type = map ( object ( { entitlement_id = string max_request_duration = string eligible_users = list ( string ) resource_type = string resource = string roles = list ( string ) require_approver_justification = bool approvals_needed = number approvers = list ( string ) } )) } # variables.tf variable "parent" { description = "Parent resource (e.g., project, folder, or organization)" type = string } variable "location" { description = "Location for the entitlement" type = string default = "global" } # main.tf resource "google_project_service" "apis" { for_each = toset (var.apis) project = var.project_id service = each.value disable_on_destroy = false } # APIの有効化には時間がかかるため、待機時間を設定 resource "null_resource" "delay" { provisioner "local-exec" { command = "sleep 180" } depends_on = [ google_project_service.apis ] } # outputs.tf output "enabled_apis" { description = "List of enabled APIs for the project" value = [ for service in google_project_service.apis : service.id ] } # variables.tf variable "apis" { description = "List of APIs to enable" type = list ( string ) } variable "project_id" { description = "The ID of the project to create resources in" type = string } デプロイ terraform plan GitHub Actions ( terraform plan ) の実行結果です。 # 組織向けのワークフロー(terraform plan) Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # google_organization_iam_member.pam_service_agent will be created + resource "google_organization_iam_member" "pam_service_agent" { + etag = (known after apply) + id = (known after apply) + member = "serviceAccount:service-org-1234567890@gcp-sa-pam.iam.gserviceaccount.com" + org_id = "1234567890" + role = "roles/privilegedaccessmanager.serviceAgent" } # module.pam.google_privileged_access_manager_entitlement.pam["pam_org1"] will be created + resource "google_privileged_access_manager_entitlement" "pam" { + create_time = (known after apply) + entitlement_id = "pam-organization-acm-demo" + etag = (known after apply) + id = (known after apply) + location = "global" + max_request_duration = "3600s" + name = (known after apply) + parent = "organizations/1234567890" + state = (known after apply) + update_time = (known after apply) + approval_workflow { + manual_approvals { + require_approver_justification = true + steps { + approvals_needed = 1 + approvers { + principals = [ + "user:demo-user01@g-gen.co.jp" , ] } } } } + eligible_users { + principals = [ + "user:demo-user01@dev.g-gen.co.jp" , ] } + privileged_access { + gcp_iam_access { + resource = "//cloudresourcemanager.googleapis.com/organizations/1234567890" + resource_type = "cloudresourcemanager.googleapis.com/Organization" + role_bindings { + role = "roles/accesscontextmanager.gcpAccessReader" } + role_bindings { + role = "roles/accesscontextmanager.policyReader" } } } + requester_justification_config { + unstructured {} } } Plan: 2 to add, 0 to change, 0 to destroy. # プロジェクト向けのワークフロー(terraform plan) Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # module.apis.google_project_service.apis["privilegedaccessmanager.googleapis.com"] will be created + resource "google_project_service" "apis" { + disable_on_destroy = false + id = (known after apply) + project = "yutakei" + service = "privilegedaccessmanager.googleapis.com" } # module.apis.null_resource.delay will be created + resource "null_resource" "delay" { + id = (known after apply) } # module.pam.google_privileged_access_manager_entitlement.pam["pam1"] will be created + resource "google_privileged_access_manager_entitlement" "pam" { + create_time = (known after apply) + entitlement_id = "pam-yutakei-bigquery-demo" + etag = (known after apply) + id = (known after apply) + location = "global" + max_request_duration = "3600s" + name = (known after apply) + parent = "projects/yutakei" + state = (known after apply) + update_time = (known after apply) + approval_workflow { + manual_approvals { + require_approver_justification = true + steps { + approvals_needed = 1 + approvers { + principals = [ + "user:demo-user01@g-gen.co.jp" , ] } } } } + eligible_users { + principals = [ + "user:demo-user01@dev.g-gen.co.jp" , ] } + privileged_access { + gcp_iam_access { + resource = "//cloudresourcemanager.googleapis.com/projects/yutakei" + resource_type = "cloudresourcemanager.googleapis.com/Project" + role_bindings { + role = "roles/bigquery.jobUser" } + role_bindings { + role = "roles/bigquery.dataViewer" } } } + requester_justification_config { + unstructured {} } } # module.pam.google_privileged_access_manager_entitlement.pam["pam2"] will be created + resource "google_privileged_access_manager_entitlement" "pam" { + create_time = (known after apply) + entitlement_id = "pam-yutakei-gcs-demo" + etag = (known after apply) + id = (known after apply) + location = "global" + max_request_duration = "3600s" + name = (known after apply) + parent = "projects/yutakei" + state = (known after apply) + update_time = (known after apply) + approval_workflow { + manual_approvals { + require_approver_justification = true + steps { + approvals_needed = 1 + approvers { + principals = [ + "user:demo-user01@g-gen.co.jp" , ] } } } } + eligible_users { + principals = [ + "user:demo-user01@dev.g-gen.co.jp" , ] } + privileged_access { + gcp_iam_access { + resource = "//cloudresourcemanager.googleapis.com/projects/yutakei" + resource_type = "cloudresourcemanager.googleapis.com/Project" + role_bindings { + role = "roles/storage.admin" } } } + requester_justification_config { + unstructured {} } } Plan: 4 to add, 0 to change, 0 to destroy. terraform apply GitHub Actions ( terraform apply ) の実行結果です。戻り値は先ほど同様のため割愛します。 リソース 組織では PAM サービスアカウントに対する IAM Policy と 利用資格 がデプロイされました。 プロジェクトでも 利用資格 がデプロイされました。 動作確認 申請 申請者のアカウントで PAM の管理画面にアクセスし、 権限付与をリクエスト をクリックします。 以下3項目を入力し、 権限付与をリクエスト をクリックすると、承認フローが承認者へと進みます。 権限付与の期間 (必須、最大時間が1時間の場合、30分/45分/1時間から選択できる) 理由 (必須) 通知の受信者 (任意、省略しても利用資格で設定した承認者にメール通知が行われる) 承認されるまでの間、当該利用資格のステータスは Approval Awaited となります。 ` 承認 承認者のアカウントで PAM の管理画面にアクセスすると、申請者からの承認フローが回ってきたことがわかります。 以下のメール通知が届きます。 承認 / 拒否 をクリックし申請内容を確認します。 コメント欄 (必須) に承認する旨を入力し、 承認 をクリックします。 権限付与 承認者には以下のメール通知が届きます。 利用資格のステータスも Approval Awaited > Active となっており、権限付与の残り時間も表示されています。 IAM Policy の管理画面を確認すると、今回利用資格の中で定義した2つのロールが PAM によって付与されたことがわかります。 権限はく奪 申請時に希望した付与時間 (今回は30分) が経過すると、先ほどまで付与されていたロールが PAM によってはく奪されていることがわかります。 再申請 利用資格のステータスが Available (申請開始前の状態) に戻っており、必要な際には再度同じ利用資格を使って申請が行えます。 武井 祐介 (記事一覧) クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア Google Cloud Partner Top Engineer 2025 選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 趣味はロードバイク、ロードレースやサッカー観戦です。 Follow @ggenyutakei
G-gen の武井です。当記事では Google Cloud と GitHub Actions (Terraform) を連携する Direct Workload Identity を作成する bash スクリプトを紹介します。 はじめに 概要 以前の記事との違い 制限事項 前提条件 免責事項 ソースコード スクリプトの使い方 認証 変数設定 実行 リソースの確認 Workload Identity プール・プロバイダー サービスアカウント Workload Identity プールの IAM Policy 構成 ソースコード (Terraform) Terraform ディレクトリ構成 ワークフロー (terraform.yaml) env/demo 配下 (呼び出し側) modules/apis 配下 (モジュール) プルリクエスト (terraform plan) マージ (terraform apply) はじめに 概要 当記事で紹介するのは、Google Cloud と GitHub Actions (Terraform) との連携に必要な Direct Workload Identity リソースを作成する bash スクリプトです。 以前の記事との違い 以前執筆した記事で紹介したのは、 サービスアカウントの権限を借用する形式 の Workload Identity リソースを作成するスクリプトです。 blog.g-gen.co.jp 今回ご紹介するのは、 Workload Identity プールに必要な権限 (IAM ロール) を直接付与する形式 の Workload Identity リソースを作成するスクリプトです。 この方式は、サービスアカウントの払い出しやサービスアカウントを借用するための権限付与が必要ないため、従来よりもセキュアな連携が可能で、Google Cloud、ならびに GitHub の公式ドキュメント上でも推奨されています。 参考: アクセス管理 参考: (Preferred) Direct Workload Identity Federation 制限事項 推奨される形式ではあるものの、Direct Workload Identity には 対応可能なプロダクトや機能に制限があります。 対応していないプロダクトやその機能を管理したい場合、従来形式 (サービスアカウントの権限を借用する形式) の Workload Identity をご利用ください。 参考: ID 連携: プロダクトと制限事項 前提条件 当 bash スクリプトは、 Debian GNU/Linux 12 (bookworm) 上で開発され、動作確認されています。 また、以下のソフトウェアがインストールされていることが前提です。カッコ内は開発時のバージョンです。 gcloud( Google Cloud SDK 486.0.0 ) スクリプト実行時は、実行先のプロジェクトに対して gcloud CLI を認証する必要があります。 参考 : ユーザー アカウントを使用して認可する 参考 : サービス アカウントを使用して承認する blog.g-gen.co.jp 免責事項 当記事で紹介するプログラムのソースコードは、ご自身の責任のもと、使用、引用、改変、再配布して構いません。 ただし、同ソースコードが原因で発生した不利益やトラブルについては、当社は一切の責任を負いません。 ソースコード 前述の 免責事項 をご理解のうえ、ご利用ください。 init.sh #!/bin/bash # エラーハンドリング: エラーが発生したらスクリプトを終了 set -e # 変数の設定 PROJECT_ID = "" # プロジェクトID (ex: gha-demo-prj) PROJECT_NUMBER = "" # プロジェクト番号 (ex: 1234567890) ORGANIZATION_ID = "" # プロジェクトの組織ID (ex: 0123456789) WORKLOAD_IDENTITY_POOL = "" # Workload Identityプール名 (ex: gha-demo-pool) WORKLOAD_IDENTITY_PROVIDER = "" # Workload Identityプロバイダ名 (ex: gha-demo-provider) GITHUB_REPO = "" # GitHubリポジトリ名 (ex: gha-demo-org/gha-demo-repo) # ログ出力関数 log() { echo " [INFO] $1 " } log_error() { echo " [ERROR] $1 " >&2 } # 1. IAM Credential API を有効化 if ! gcloud services list --enabled --filter =" name:iamcredentials.googleapis.com " --format =" value(name) " | grep " iamcredentials.googleapis.com " > /dev/null 2 >& 1 ; then log " IAM Credential API を有効にしています... " gcloud services enable iamcredentials.googleapis.com --project =" $PROJECT_ID " else log " IAM Credential API は既に有効化されています " fi # 2. Workload Identity プールの作成 if ! gcloud iam workload-identity-pools describe $WORKLOAD_IDENTITY_POOL --location =" global " --project =" $PROJECT_ID " > /dev/null 2 >& 1 ; then log " Workload Identity プールを作成中: $WORKLOAD_IDENTITY_POOL " gcloud iam workload-identity-pools create $WORKLOAD_IDENTITY_POOL \ --project =" $PROJECT_ID " \ --location =" global " \ --display-name =" $WORKLOAD_IDENTITY_POOL " else log " Workload Identity プールは既に存在します: $WORKLOAD_IDENTITY_POOL " fi # 3. Workload Identity プロバイダの作成 if ! gcloud iam workload-identity-pools providers describe $WORKLOAD_IDENTITY_PROVIDER --workload-identity-pool =" $WORKLOAD_IDENTITY_POOL " --location =" global " --project =" $PROJECT_ID " > /dev/null 2 >& 1 ; then log " Workload Identity プロバイダを作成中: $WORKLOAD_IDENTITY_PROVIDER " gcloud iam workload-identity-pools providers create-oidc $WORKLOAD_IDENTITY_PROVIDER \ --project =" $PROJECT_ID " \ --location =" global " \ --workload-identity-pool =" $WORKLOAD_IDENTITY_POOL " \ --display-name =" $WORKLOAD_IDENTITY_PROVIDER " \ --issuer-uri =" https://token.actions.githubusercontent.com " \ --attribute-mapping =" google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository " \ --attribute-condition =" assertion.repository==' $GITHUB_REPO ' " else log " Workload Identity プロバイダは既に存在します: $WORKLOAD_IDENTITY_PROVIDER " fi # 4. 組織レベルでのロール付与 log " 組織レベルでのロール付与の確認 " for role in " roles/resourcemanager.organizationAdmin " " roles/owner " ; do if ! gcloud organizations get-iam-policy $ORGANIZATION_ID --flatten =" bindings[].members " --filter =" bindings.members:principalSet://iam.googleapis.com/projects/ $PROJECT_NUMBER /locations/global/workloadIdentityPools/ $WORKLOAD_IDENTITY_POOL AND bindings.role: $role " --format =" value(bindings.role) " | grep " $role " > /dev/null 2 >& 1 ; then log " $role をWorkload Identity プールに付与中: $WORKLOAD_IDENTITY_POOL " gcloud organizations add-iam-policy-binding $ORGANIZATION_ID \ --member =" principalSet://iam.googleapis.com/projects/ $PROJECT_NUMBER /locations/global/workloadIdentityPools/ $WORKLOAD_IDENTITY_POOL /attribute.repository/ $GITHUB_REPO " \ --role =" $role " else log " $role は既にWorkload Identity プールに付与されています: $WORKLOAD_IDENTITY_POOL " fi done log " Direct Workload Identity 設定が完了しました。 " スクリプトの使い方 認証 まずは実行先のプロジェクトに対して gcloud CLI の認証を通します。 # 実行先プロジェクトの確認 $ gcloud config list [ core ] account = test-user@demo.g-gen.co.jp disable_usage_reporting = True project = gha-demo-prj Your active configuration is: [ gha-demo-prj ] # gcloud CLI の認証 $ gcloud auth login ~~中略~~ You are now logged in as [ test-user@demo.g-gen.co.jp ] . Your current project is [ gha-demo-prj ] . 変数設定 7~12行目 の変数に環境情報を入力します。 ※ 当スクリプトでは、サービスアカウントの変数定義はありません。 実行 スクリプトに実行権限を付与して実行します。 ※ 当スクリプトでは、以下のリソースは作成しません。 サービスアカウント サービスアカウントを借用するための IAM Policy Workload Identity プールとサービスアカウントの紐づけ # 実行権限付与 $ chmod +x init.sh $ ls -l -rwxr-xr-x 1 test-user test-user 3784 Nov 12 14:27 init.sh # スクリプト実行 $ ./init.sh [ INFO ] IAM Credential API は既に有効化されています [ INFO ] Workload Identity プールを作成中: gha-demo-pool Created workload identity pool [ gha-demo-pool ] . [ INFO ] Workload Identity プロバイダを作成中: gha-demo-provider Created workload identity pool provider [ gha-demo-provider ] . [ INFO ] 組織レベルでのロール付与の確認 [ INFO ] roles/resourcemanager.organizationAdmin をWorkload Identity プールに付与中: gha-demo-pool Updated IAM policy for organization [ 0123456789 ] . ~~中略~~ [ INFO ] roles/owner をWorkload Identity プールに付与中: gha-demo-pool Updated IAM policy for organization [ 0123456789 ] . ~~中略~~ [ INFO ] Workload Identity 設定が完了しました。 リソースの確認 Workload Identity プール・プロバイダー 以下のように作成されます。 Workload Identity プール (1/2) Workload Identity プール (2/2)、サービスアカウントを使用していない Workload Identity プロバイダー (1/2) Workload Identity プロバイダー (2/2) サービスアカウント 前述の通り、Direct Workload Identity にサービスアカウントは不要なため、当スクリプトでは作成しません。 Workload Identity プールの IAM Policy 以下のように作成されます。 ※ IAM ロールは適用先のセキュリティポリシーに応じて調整してください。 組織レベルの IAM Policy 構成 当スクリプトで作成される Workload Identity を使い、Google Cloud プロジェクトに対する terraform plan や terraform apply を、GitHub Actions で自動化します。 ワークフローや Terraform ソースコードは次項に記載のものを使用します。 ご利用される場合は、前述の 免責事項 をご理解のうえ、ご利用ください。 ソースコード (Terraform) Terraform ディレクトリ構成 . ├── .github │ └── workflows │ └── terraform.yaml ├── env │ └── demo │ ├── backend.tf │ ├── locals.tf │ ├── main.tf │ └── versions.tf ├── modules │ └── apis │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── .gitignore ├── init.sh └── README.md ワークフロー (terraform.yaml) 以下の値をご自身の環境で作成したリソースに置き換えてください。 38行目 : Workload Identity プロバイダー Direct Workload Identity では、 google-github-actions/auth@v2 でサービスアカウントを定義する必要はありません。 name : terraform # main ブランチへの Pull request と Merge on : pull_request : branches : - main push : branches : - main # ジョブ (GitHUb runners で実行) jobs : terraform-workflow : runs-on : ubuntu-latest permissions : id-token : write contents : read pull-requests : write strategy : matrix : # tf_working_dir に main.tf (呼び出し側) のディレクトリを指定 tf_working_dir : - ./env/demo steps : - uses : actions/checkout@v4 name : Checkout id : checkout # Workload Identity 連携 # https://cloud.google.com/iam/docs/using-workload-identity-federation#generate-automatic - id : 'auth' name : 'Authenticate to Google Cloud' uses : 'google-github-actions/auth@v2' with : workload_identity_provider : 'projects/1234567890/locations/global/workloadIdentityPools/gha-demo-pool/providers/gha-demo-provider' # https://github.com/marketplace/actions/setup-tfcmt - uses : shmokmt/actions-setup-tfcmt@v2 name : Setup tfcmt # https://github.com/marketplace/actions/setup-github-comment - uses : shmokmt/actions-setup-github-comment@v2 name : Setup github-comment # https://github.com/actions/setup-node # https://github.com/hashicorp/setup-terraform/issues/84 - uses : actions/setup-node@v4 with : node-version : '18' - uses : hashicorp/setup-terraform@v3 name : Setup terraform - name : Terraform fmt id : fmt run : | cd ${{ matrix.tf_working_dir }} terraform fmt -recursive continue-on-error : true - name : Terraform Init id : init run : | cd ${{ matrix.tf_working_dir }} terraform init -upgrade - name : Terraform Validate id : validate run : | cd ${{ matrix.tf_working_dir }} terraform validate # main ブランチへ pull request した際に terraform plan を実行 - name : Terraform Plan id : plan if : github.event_name == 'pull_request' run : | cd ${{ matrix.tf_working_dir }} export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} tfcmt -var target:${{ matrix.tf_working_dir }} plan -- terraform plan --parallelism=50 github-comment hide -condition 'Comment.Body contains "No changes."' continue-on-error : true # terraform status で失敗した際に workflow を停止 - name : Terraform Plan Status id : status if : steps.plan.outcome == 'failure' run : exit 1 # main ブランチへ push した際に terraform apply を実行 - name : Terraform Apply id : apply if : github.ref == 'refs/heads/main' && github.event_name == 'push' run : | cd ${{ matrix.tf_working_dir }} export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} tfcmt -var target:${{ matrix.tf_working_dir }} apply -- terraform apply -auto-approve -input= false --parallelism=50 env/demo 配下 (呼び出し側) # backend.tf terraform { backend " gcs " { bucket = " gha-demo-prj-tfstate " prefix = " terraform/state " } } # locals.tf locals { project_id = " gha-demo-prj " apis = [ " artifactregistry.googleapis.com ", " cloudapis.googleapis.com ", " cloudasset.googleapis.com ", " cloudresourcemanager.googleapis.com ", " iam.googleapis.com ", " iamcredentials.googleapis.com ", " servicemanagement.googleapis.com ", " serviceusage.googleapis.com ", " sts.googleapis.com ", ] } # main.tf module " apis " { source = " ../../modules/apis " project_id = local.project_id apis = local.apis } # versions.tf terraform { required_version = " ~> 1.9.7 " required_providers { google = { source = " hashicorp/google " version = " ~> 6.6.0 " } } } provider " google " { user_project_override = true } modules/apis 配下 (モジュール) # main.tf resource " google_project_service " " apis " { for_each = toset ( var.apis ) project = var.project_id service = each.value disable_on_destroy = false } resource " null_resource " " delay " { provisioner " local-exec " { command = " sleep 180 " } depends_on = [ google_project_service.apis ] } # outputs.tf output " enabled_apis " { description = " List of enabled APIs for the project " value = [ for service in google_project_service.apis : service.id ] } # variables.tf variable " apis " { description = " List of APIs to enable " type = list ( string ) } variable " project_id " { description = " The ID of the project to create resources in " type = string } プルリクエスト (terraform plan) Direct Workload Identity でも、main ブランチへのプルリクエストをトリガーに terraform plan が実行されました。 ※ プルリクエストの場合、 terraform apply はスキップされます。 プルリクエストをトリガーに terraform plan が自動実行 マージ (terraform apply) Direct Workload Identity でも、main ブランチへのマージをトリガーに terraform apply が実行されました。 ※ マージの場合、 terraform plan はスキップされます。 マージをトリガーに terraform apply が自動実行 武井 祐介 (記事一覧) クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア Google Cloud Partner Top Engineer 2025 選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 趣味はロードバイク、ロードレースやサッカー観戦です。 Follow @ggenyutakei
G-gen の堂原です。当記事では、 Google ドライブ をデータソースとする Vertex AI Search アプリに対して、Python から検索を行う際に検索結果が0件になってしまう場合の対処法について紹介します。 はじめに 検索が失敗するケース Google Cloud APIs のチャンネルが v1alpha 以外の場合 サービスアカウントを用いる場合 対処法 Python Client サンプルコード ポイント ライブラリのチャンネル指定 credentials Requests ライブラリを用いての直接アクセス サンプルコード ポイント はじめに 当記事では、Google Cloud(旧称 GCP)が提供する検索エンジンサービスである Vertex AI Search において、 Google ドライブ をデータソースとする Vertex AI Search アプリに対して、Python から検索を行う方法について紹介します。 Vertex AI Search アプリを Python から検索するには、次の 2 つの方法があります。 Python Client を用いる方法 Requests ライブラリを用いて直接 Google Cloud APIs にアクセスする方法 しかし実装方法によっては、検索結果が0件になってしまう場合があります。 検索が失敗するケース Google Cloud APIs のチャンネルが v1alpha 以外の場合 当記事を執筆した2024年12月現在、Google ドライブをデータソースとする Vertex AI Search アプリへの検索は、「Python Client を用いる方法」「直接 Google Cloud APIs にアクセスする方法」の両方とも、 v1alpha でのみ期待通り動作します。一方で、v1 または v1beta を用いた場合は、検索結果が0件になります。 Google Cloud APIs において v1alpha とは、API のバージョンを示すチャンネルの1つです。Google Cloud APIs は基本的に、v1alpha、v1beta、v1 という順で開発が進みます。v1alpha の機能は予告なく削除される可能性があるため、本番環境での利用は非推奨です。 参考 : バージョニング サービスアカウントを用いる場合 Google Cloud APIs には通常、Google アカウントまたはサービスアカウントの認証情報を使用してアクセスします。 ただし、2024年12月現在では、 サービスアカウントを用いて Google ドライブをデータソースとする Vertex AI Search アプリへの検索を行った場合、 サーバ側のエラー (500 Internal Server Error)が発生します。 対処法 当事象に対する2024年12月現在の対処法は、以下のとおりです。 v1alpha チャンネルのクライアントライブラリを使用する サービスアカウントではなく、Google アカウントの認証情報を使用する Python Client サンプルコード Python Client を使用する場合のサンプルコードは次のとおりです。 from google.cloud.discoveryengine_v1alpha import SearchServiceClient, SearchRequest from google.protobuf.json_format import MessageToDict PROJECT_ID = "xxx" # Google Cloud プロジェクト ID VERTEX_AI_APP_ID = "xxx" # Vertex AI Search アプリの ID client = SearchServiceClient(credentials=credentials) serving_config = f "projects/{PROJECT_ID}/locations/global/collections/default_collection/engines/{VERTEX_AI_APP_ID}/servingConfigs/default_serving_config" content_search_spec = SearchRequest.ContentSearchSpec( # スニペットを出力させない snippet_spec=SearchRequest.ContentSearchSpec().SnippetSpec( return_snippet= False ), # 要約文を出力させる summary_spec=SearchRequest.ContentSearchSpec().SummarySpec( summary_result_count= 3 , include_citations= False , # Gemini Proを用いるように指定 model_spec=SearchRequest.ContentSearchSpec().SummarySpec().ModelSpec( version= "gemini-1.5-flash-001/answer_gen/v1" ) ) ) # Vertex AI Searchにクエリを投げる response = client.search( SearchRequest( serving_config=serving_config, query= "G-genとは?" , page_size= 3 , content_search_spec=content_search_spec ) ) # 要約文を標準出力 print (response.summary.summary_text) # 検索結果を標準出力 for r in response.results: r_dct = MessageToDict(r._pb) print (r_dct) 参考 : Class SearchServiceClient ポイント ライブラリのチャンネル指定 from google.cloud.discoveryengine_v1alpha import SearchServiceClient, SearchRequest 上記のように、google-cloud-discoveryengine をインポートする際のチャンネル指定は _v1alpha を明示的に指定する必要があります。チャンネルを指定しない以下のようなインポート文だと、 検索が失敗するケース に記載の通り、検索結果が0件になります。 # 未指定 from google.cloud.discoveryengine import SearchServiceClient, SearchRequest # v1 指定 from google.cloud.discoveryengine_v1 import SearchServiceClient, SearchRequest # v1beta 指定 from google.cloud.discoveryengine_v1beta import SearchServiceClient, SearchRequest credentials client = SearchServiceClient(credentials=credentials) でパラメータとして与える認証情報はサービスアカウントのものではなく、Google アカウントのものにする必要があります。 Google アカウントの認証情報の場合は変数の型が google.oauth2.credentials.Credentials に、サービスアカウントの認証情報の場合は変数の型が google.oauth2.service_account.Credentials になります。 Requests ライブラリを用いての直接アクセス サンプルコード Requests ライブラリを使用して Google Cloud APIs に直接アクセスする場合のサンプルコードは次のとおりです。 import requests PROJECT_NUMBER = "xxx" # Google Cloud プロジェクト番号 VERTEX_AI_APP_ID = "xxx" # Vertex AI Search アプリの ID # APIのURL url = f "https://discoveryengine.googleapis.com/v1alpha/projects/{PROJECT_NUMBER}/locations/global/collections/default_collection/engines/{VERTEX_AI_APP_ID}/servingConfigs/default_search" # リクエストヘッダ headers = { "Authorization" : "Bearer " + credentials.token, "Content-Type" : "application/json" , } # リクエストボディ session = f "projects/{PROJECT_NUMBER}/locations/global/collections/default_collection/engines/{VERTEX_AI_APP_ID}/sessions/-" data = { "query" : "G-genとは?" , "pageSize" : 3 , "contentSearchSpec" : { "snippetSpec" : { "returnSnippet" : False }, "extractiveContentSpec" : { "maxExtractiveAnswerCount" : 1 } }, "session" : session } # 検索リクエスト送信 response = requests.post(f "{url}:search" , headers=headers, json=data) # 検索結果を標準出力 for r in response.json().get( "results" ): print (r) data = { "query" : { "text" : "G-genとは?" , "queryId" : response.json().get( "sessionInfo" ).get( "queryId" ) }, "session" : response.json().get( "sessionInfo" ).get( "name" ), "answerGenerationSpec" : { "modelSpec" : { "modelVersion" : "gemini-1.5-flash-001/answer_gen/v1" } } } # 要約リクエスト送信 response = requests.post(f "{url}:answer" , headers=headers, json=data) # 要約文を標準出力 print (response.json().get( "answer" ).get( "answerText" )) 参考 : Method: projects.locations.collections.dataStores.servingConfigs.search ポイント Requests ライブラリを用いて直接 Google Cloud APIs にアクセスパターンでも、重要なポイントは Python Client を用いる場合と変わりません。 API の URL のチャンネル指定を v1alpha にする ヘッダの Authorization に含むトークンは Google アカウントのアクセストークンとする 堂原 竜希 (記事一覧) クラウドソリューション部データアナリティクス課。2023年4月より、G-genにジョイン。 Google Cloud Partner Top Engineer 2023, 2024に選出 (2024年はRookie of the yearにも選出)。休みの日はだいたいゲームをしているか、時々自転車で遠出をしています。 Follow @ryu_dohara
G-gen の堂原です。本記事では Google Cloud(旧称 GCP)の Cloud Run functions (旧 Cloud Functions)から、 VPC Service Controls 境界の中のリソースへアクセスさせる方法について紹介します。 はじめに 本記事の趣旨 VPC Service Controls Cloud Run functions ポイント アクセスの成否 VPC 経由でのリクエストが必要な理由 VPC のアクセス可能なサービス はじめに 本記事の趣旨 本記事では、Cloud Run functions から、VPC Service Controls で保護された Google Cloud プロジェクトのリソースにアクセスする方法や、どのような設定でアクセスが成功または失敗するのかについて解説します。 VPC Service Controls VPC Service Controls は Google Cloud が提供するセキュリティサービスです。 「サービス境界(service perimeter)」という論理的な境界を作成し、境界をまたぐ API リクエストを制限します。 VPC Service Controls については、以下の記事で解説していますのでご参照ください。 blog.g-gen.co.jp なお本記事は Ingress rules(内向きルール)など、VPC Service Controls の基本的な設定項目については理解している前提で書かれています。 Cloud Run functions Cloud Run functions は、Google Cloud が提供するサーバレスコンピューティングサービスです。インフラの構築や管理をすることなく、プログラムを実行することができます。 こちらも以下の記事で解説していますので、ご参照ください。 blog.g-gen.co.jp ポイント 本記事で最も伝えたいポイントは、以下のとおりです。 VPC Service Controls の Ingress rule(内向きルール)でソースプロジェクトを指定する場合、Cloud Run functions は VPC 経由でリクエストを送信する必要がある。 アクセスの成否 以下のような検証環境を用意しました。 VPC Service Controls で保護された Google Cloud プロジェクト プロジェクト内に Cloud Run functions と BigQuery テーブルを用意 Cloud Run functions には「サービスアカウント A」を紐づけ Cloud Run functions から BigQuery テーブルにアクセスする状況を想定 この環境で、VPC Service Controls と Cloud Run functions の各設定値を変更しながら、様々なパターンでアクセスの成否を検証しました。検証結果は、以下のようになりました。 No. VPC Service Controls Cloud Run functions アクセス結果 制限付きサービス VPC のアクセス可能なサービス 「Ingress rules」の「API クライアントの FROM 属性」 ネットワークの下り設定 ID ソース 1 BigQuery API すべての制限付きサービス サービスアカウント A すべてのソース なし OK 2 BigQuery API すべての制限付きサービス サービスアカウント A プロジェクト なし NG 3 BigQuery API すべての制限付きサービス サービスアカウント A プロジェクト すべてのトラフィックを VPC コネクタ経由でルーティングする OK 4 BigQuery API すべての制限付きサービス サービスアカウント A プロジェクト プライベート IP へのリクエストだけを VPC コネクタ経由でルーティングする NG 5 BigQuery API なし サービスアカウント A プロジェクト すべてのトラフィックを VPC コネクタ経由でルーティングする NG 6 なし なし サービスアカウント A プロジェクト すべてのトラフィックを VPC コネクタ経由でルーティングする NG 7 なし なし サービスアカウント A プロジェクト なし OK VPC 経由でのリクエストが必要な理由 VPC Service Controls では、境界を超えるようなアクセスは、原則的に全てブロックされます。 Ingress rules (内向きルール)を用いると、 特定のソース (特定の IP アドレスや Google Cloud プロジェクト)を接続元とする 特定の ID (Google アカウントやサービスアカウント)によるアクセスを 例外的に許可する ことが出来ます。 そのため、Ingress rules で ID を Cloud Run functions に紐づけた「サービスアカウント A」に、 ソースを Cloud Run functions が所属する Google Cloud プロジェクト にすれば、境界内へのアクセスは成功するかのように考えられます。 しかし上記の場合、 Cloud Run functions からのアクセスは失敗します 。 これは、Google Cloud の公式ドキュメントには明言されていませんが、Cloud Run functions の実行基盤がユーザが管理する Google Cloud プロジェクトとは別だからだと推測できます。 そのため、当該プロジェクトからのアクセスとなるように、 Cloud Run functions を VPC に接続させる必要がある のです。 このことは以下の公式ドキュメントでも触れられています。 参考 : 境界外の関数での VPC Service Controls の使用 VPC のアクセス可能なサービス また Cloud Run functions に VPC 経由で VPC Service Controls の境界内へアクセスさせる場合は、「 VPC のアクセス可能なサービス (VPC Accessible services)」という設定項目の値にも気をつける必要があります。この設定項目は境界内の VPC ネットワークからアクセスできるサービスを制御する項目です。 この項目で明示的にアクセス先のサービスが許可されていない場合、Cloud Run functions からのアクセスは失敗します。 前掲の表の No. 6は、VPC 内部からのアクセスだが「VPC のアクセス可能なサービス」で許可されていない API にリクエストしようとしているため、リクエストが失敗しています。反対に No.7 は、VPC 外部からのリクエストであり、かつ BigQuery API は境界で保護されていないため、リクエストが成功します。 参考 : VPC のアクセス可能なサービス 堂原 竜希 (記事一覧) クラウドソリューション部データアナリティクス課。2023年4月より、G-genにジョイン。 Google Cloud Partner Top Engineer 2023, 2024に選出 (2024年はRookie of the yearにも選出)。休みの日はだいたいゲームをしているか、時々自転車で遠出をしています。 Follow @ryu_dohara
G-gen の三浦です。当記事では Workload Identity の仕組みを使うことで、サービスアカウントキーを使わずに GitHub Enterprise の監査ログを BigQuery にエクスポートする仕組みを構築したのでご紹介します。 GitHub Enterprise とは 概要 監査ログ Google Cloud への監査ログエクスポート アーキテクチャ 構成図 ディレクトリ構成 環境構築 Workload Identity とサービスアカウントの作成 BigQuery データセットの作成とサービスアカウントへの権限付与 GitHub Actions ワークフローの作成 main.py の作成 GitHub App の作成 GitHub Actions のシークレット登録 動作確認 手動実行 スケジュール実行 GitHub Enterprise とは 概要 GitHub Enterprise は、複数組織の一元管理や Microsoft Entra ID などの IdP(Identity Provider)を使用した SSO(シングルサインオン)などの機能を提供する有償プランです。 本プランでは監査ログ API を使用して組織内の操作履歴を取得・管理できます。監査ログを BigQuery にエクスポートすると、長期保存や高度な分析を行い、セキュリティ対策やコンプライアンス強化に役立ちます。 参考 : エンタープライズ向け GitHub について 参考 : プランご紹介 監査ログ GitHub の監査ログには、組織メンバーのレポジトリ作成、プルリクエストやマージなどの操作が記録され、過去180日分のログを確認できます。 git.clone など一部の Git イベントは 7 日間のみ保持されます。 参考 : 企業の監査ログにアクセスする 参考 : エンタープライズの監査ログ イベント Google Cloud への監査ログエクスポート GitHub の監査ログは JSON 形式で Cloud Storage にエクスポートできますが、通常はサービスアカウントキーが必要です。 参考 : Google Cloud Storage へのストリーミングの設定 サービスアカウントキーは厳重な管理が必要です。漏洩した場合、第三者による不正利用のリスクがあります。Google Cloud のベストプラクティスでは、サービスアカウントキーを使用せずに認証する方法が推奨されています。 参考 : サービス アカウント キーを管理するためのベスト プラクティス 以上のことから、当記事では Google Cloud の Workload Identity 機能を使うことで、サービスアカウントキーを使用せずに GitHub 監査ログを取得する仕組みを実装しました。 アーキテクチャ 構成図 構成は図のとおりです。GitHub Actions を使用して監査ログを取得し、BigQuery にエクスポートします。 構成図 ディレクトリ構成 ディレクトリ構成は以下の通りです。 . └── app └── main.py # 監査ログを BigQuery へエクスポートするスクリプト .github └── workflows └── github-audit-log-to-bq.yml # GitHub Actions を定義 環境構築 Workload Identity とサービスアカウントの作成 GitHub Actions と Google Cloud を連携させるための Workload Identity とサービスアカウントを作成します。作成方法は、次の記事を参照してください。 Google CloudとGitHub Actions(Terraform)を連携するWorkload Identityを作成するbashスクリプト blog.g-gen.co.jp BigQuery データセットの作成とサービスアカウントへの権限付与 BigQuery のデータセットを作成します。テーブルは GitHub Actions で自動的に作成されます。 # 環境変数を設定 PROJECT_ID = " gha-demo-prj " # プロジェクトID SERVICE_ACCOUNT_NAME = " gha-demo-sa " # サービスアカウント名 BQ_DATASET = " my_dataset " # BigQueryのデータセット名   # BigQuery データセットを東京リージョンに作成 bq --project_id = $PROJECT_ID \ mk --location = asia-northeast1 \ $BQ_DATASET サービスアカウントに BigQuery へのデータ書き込み権限とジョブの実行権限を付与します。 # サービスアカウントへ権限付与 gcloud projects add-iam-policy-binding $PROJECT_ID \ --member =" serviceAccount: $SERVICE_ACCOUNT_NAME @ $PROJECT_ID .iam.gserviceaccount.com " \ --role =" roles/bigquery.dataEditor "   gcloud projects add-iam-policy-binding $PROJECT_ID \ --member =" serviceAccount: $SERVICE_ACCOUNT_NAME @ $PROJECT_ID .iam.gserviceaccount.com " \ --role =" roles/bigquery.jobUser " GitHub Actions ワークフローの作成 このワークフローは、GitHub の監査ログを1日に1回自動で取得し、BigQuery にエクスポートします。前回の自動取得時刻をアーティファクト(GitHub Actions の成果物保存機能)で管理することで差分のログのみを取得します。過去のログも手動で取得できます。 参考 : ワークフローからのデータの格納と共有 env: の箇所に、環境に応じたプロジェクト ID などの値を記載してください。 # github-audit-log-to-bq.yml name : Fetch and Upload GitHub Audit Logs to BigQuery   on : schedule : # スケジュール実行 - cron : '0 15 * * *' # 毎日 JST 0 時に実行(UTC 15 時) workflow_dispatch : # 手動実行 inputs : start_date : # ログ取得の開始日 description : "Start date for fetching logs (ISO 8601 format, e.g., 2024-09-01T00:00:00Z)" required : true default : "2024-10-01T00:00:00Z" end_date : # ログ取得の終了日 description : "End date for fetching logs (ISO 8601 format, e.g., 2024-09-30T23:59:59Z)" required : true default : "2024-10-31T23:59:59Z"   permissions : id-token : write contents : read actions : read   jobs : fetch-and-upload : runs-on : ubuntu-latest   env : # BigQuery 設定 BQ_GCP_PROJECT_ID : gha-demo-prj # Google Cloud プロジェクト ID BQ_DATASET : my_dataset # BigQuery データセット名 BQ_TABLE : my_table # BigQuery テーブル名 # GitHub 設定 GITHUB_ORG : myorg # GitHub 組織名 GH_TOKEN : ${{ github.token }} # GitHub CLI 用のトークン # Workload Identity Federation 設定 PROJECT_NUMBER : 1234567890 # プロジェクト番号 WORKLOAD_IDENTITY_POOL : gha-demo-pool # Workload Identity プール名 WORKLOAD_IDENTITY_POOL_PROVIDER : gha-demo-provider # Workload Identity プールプロバイダ名 SERVICE_ACCOUNT : gha-demo-sa@gha-demo-prj.iam.gserviceaccount.com # 使用するサービスアカウント名 # アーティファクト設定 LAST_RUN_TIMESTAMP_NAME : "last_run_timestamp" # 前回実行時刻のアーティファクト名   steps : - uses : actions/checkout@v4   # Google Cloud 認証 (Workload Identity) - id : 'auth' uses : 'google-github-actions/auth@v2' with : workload_identity_provider : 'projects/${{ env.PROJECT_NUMBER }}/locations/global/workloadIdentityPools/${{ env.WORKLOAD_IDENTITY_POOL }}/providers/${{ env.WORKLOAD_IDENTITY_POOL_PROVIDER }}' service_account : ${{ env.SERVICE_ACCOUNT }}   # GitHub App トークンを生成 - name : Generate GitHub App token id : generate_token uses : actions/create-github-app-token@v1 with : app-id : ${{ secrets.APP_ID }} private-key : ${{ secrets.APP_PRIVATE_KEY }}   # GitHub App トークンを環境変数に設定 - name : Set GitHub App Access Token run : echo "ACCESS_TOKEN=${{ steps.generate_token.outputs.token }}" >> $GITHUB_ENV   # Python環境をセットアップ - name : Set up Python environment run : | python3 -m venv venv . venv/bin/activate pip install --upgrade pip pip install google-cloud-bigquery requests jq pandas   # 初回実行時のタイムスタンプを設定 - name : Set Default Timestamp to Start of Today if : ${{ github.event_name == 'schedule' }} run : | DEFAULT_TIMESTAMP=$(date -u +"%Y-%m-%dT00:00:00Z") echo "DEFAULT_TIMESTAMP=$DEFAULT_TIMESTAMP" >> $GITHUB_ENV   # 前回実行時刻のアーティファクトをダウンロード - name : Download previous timestamp artifact if : ${{ github.event_name == 'schedule' }} run : | mkdir -p artifacts ARTIFACT_URL=$(gh api -X GET "repos/${{ github.repository }}/actions/artifacts" \ | jq -r '.artifacts[] | select(.name=="' ${{ env.LAST_RUN_TIMESTAMP_NAME }} '") | .archive_download_url' | head -n 1) if [ -n "$ARTIFACT_URL" ] ; then curl -L -o artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.zip -H "Authorization: token ${{ github.token }}" "$ARTIFACT_URL" unzip -o artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.zip -d artifacts/ if [ ! -f "artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt" ] ; then echo "${{ env.DEFAULT_TIMESTAMP }}" > artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt fi else echo "${{ env.DEFAULT_TIMESTAMP }}" > artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt fi   # 前回実行時刻を読み込み - name : Read previous timestamp if : ${{ github.event_name == 'schedule' }} id : read-timestamp run : | if [ ! -f "artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt" ] ; then echo "${{ env.DEFAULT_TIMESTAMP }}" > artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt fi last_run=$(cat artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt) echo "last_run=$last_run" >> $GITHUB_ENV   # 今回実行時刻を環境変数に保存 - name : Set execution time for last_run if : ${{ github.event_name == 'schedule' }} run : | execution_time=$(date --utc --iso-8601=seconds) echo "execution_time=$execution_time" >> $GITHUB_ENV   # スケジュール実行時の監査ログ取得 - name : Fetch GitHub Audit Logs for Scheduled Run if : ${{ github.event_name == 'schedule' }} env : last_run : ${{ env.last_run }} ACCESS_TOKEN : ${{ env.ACCESS_TOKEN }} run : | . venv/bin/activate rm -rf app/audit_logs mkdir -p app/audit_logs next_url="https://api.github.com/orgs/${{ env.GITHUB_ORG }}/audit-log?phrase=created:>$last_run&per_page=100&include=all" while [[ ! -z "$next_url" ]] ; do response=$(curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ "$next_url" ) if echo "$response" | jq -e 'type == "object" and has("message")' >/dev/ null 2>&1; then exit 1 fi logs_count=$(echo "$response" | jq '. | length' ) echo "$response" > "app/audit_logs/log_$logs_count.json" next_url=$(curl -s -I -H "Authorization: Bearer $ACCESS_TOKEN" "$next_url" | grep -i '^link:' | sed -n 's/.*<\(.*\)>; rel="next".*/\1/p' ) sleep 2 done   # 手動実行時の監査ログ取得 - name : Fetch GitHub Audit Logs for Manual Run if : ${{ github.event_name == 'workflow_dispatch' }} env : start_date : ${{ github.event.inputs.start_date }} end_date : ${{ github.event.inputs.end_date }} ACCESS_TOKEN : ${{ env.ACCESS_TOKEN }} run : | . venv/bin/activate rm -rf app/audit_logs mkdir -p app/audit_logs next_url="https://api.github.com/orgs/${{ env.GITHUB_ORG }}/audit-log?phrase=created%3A>${start_date}%20created%3A<${end_date}&per_page=100&include=all" while [[ ! -z "$next_url" ]] ; do response=$(curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ "$next_url" ) if echo "$response" | jq -e 'type == "object" and has("message")' >/dev/ null 2>&1; then exit 1 fi logs_count=$(echo "$response" | jq '. | length' ) echo "$response" > "app/audit_logs/log_$logs_count.json" next_url=$(curl -s -I -H "Authorization: Bearer $ACCESS_TOKEN" "$next_url" | grep -i '^link:' | sed -n 's/.*<\(.*\)>; rel="next".*/\1/p' ) sleep 2 done   # 監査ログを BigQuery にアップロード - name : Run script to upload logs to BigQuery run : | . venv/bin/activate python3 app/main.py   # 今回実行時刻をファイルに書き込み - name : Write new execution time to file if : ${{ success() && github.event_name == 'schedule' }} run : | mkdir -p artifacts echo "$execution_time" > artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt   # 今回実行時刻をアーティファクトとしてアップロード - name : Upload updated timestamp artifact if : ${{ success() && github.event_name == 'schedule' }} uses : actions/upload-artifact@v4 with : name : ${{ env.LAST_RUN_TIMESTAMP_NAME }} path : artifacts/${{ env.LAST_RUN_TIMESTAMP_NAME }}.txt main.py の作成 このスクリプトは GitHub Actions で取得した監査ログを加工し、BigQuery へエクスポートします。エクスポート先のテーブルが存在しない場合、新規に作成します。 import json import logging import os import sys import time import pandas as pd from datetime import datetime, timezone from google.cloud import bigquery from google.api_core.exceptions import NotFound, GoogleAPIError import re   # ログの設定: ログメッセージの出力形式を指定し、INFOレベル以上のメッセージを記録 logging.basicConfig(level=logging.INFO, format = '%(asctime)s [%(levelname)s] %(message)s' )   def get_bigquery_client (): """BigQueryクライアントを初期化して返す""" return bigquery.Client() def flatten_json (y): """ネストされたJSONデータを平坦化""" out = {} def flatten (x, name= '' ): if isinstance (x, dict ): for a in x: flatten(x[a], name + a + '_' ) elif isinstance (x, list ): for i, a in enumerate (x): flatten(a, name + str (i) + '_' ) else : out[name[:- 1 ]] = x flatten(y) return out def clean_field_name (field_name): """フィールド名をBigQueryで許可された形式に変換""" field_name = re.sub( r'[^a-zA-Z0-9_]' , '_' , field_name) if field_name[ 0 ].isdigit(): field_name = '_' + field_name return field_name def infer_schema_from_logs (logs_df): """DataFrameからBigQuery用のスキーマを推測して生成""" schema = [] for column in logs_df.columns: clean_column = clean_field_name(column) dtype = logs_df[column].dtype if clean_column == "_timestamp" and pd.api.types.is_integer_dtype(dtype): schema.append(bigquery.SchemaField( "timestamp" , "TIMESTAMP" )) elif pd.api.types.is_integer_dtype(dtype): schema.append(bigquery.SchemaField(clean_column, "INTEGER" )) elif pd.api.types.is_float_dtype(dtype): schema.append(bigquery.SchemaField(clean_column, "FLOAT" )) elif pd.api.types.is_bool_dtype(dtype): schema.append(bigquery.SchemaField(clean_column, "BOOLEAN" )) elif pd.api.types.is_datetime64_any_dtype(dtype): schema.append(bigquery.SchemaField(clean_column, "TIMESTAMP" )) else : schema.append(bigquery.SchemaField(clean_column, "STRING" )) return schema def create_table_if_not_exists (client, table_ref, logs_df): """テーブルが存在しない場合、新規作成""" try : table = client.get_table(table_ref) logging.info(f "Table {table_ref} already exists." ) return table except NotFound: schema = infer_schema_from_logs(logs_df) table = bigquery.Table(table_ref, schema=schema) table = client.create_table(table) logging.info(f "Table {table_ref} created with schema." ) return table def load_audit_logs (logs_dir= 'app/audit_logs' ): """監査ログをJSONファイルから読み込みDataFrameに統合""" logs = [] for filename in os.listdir(logs_dir): if filename.endswith( ".json" ): file_path = os.path.join(logs_dir, filename) try : with open (file_path, 'r' ) as f: file_logs = json.load(f) for log in file_logs: logs.append(flatten_json(log)) except json.JSONDecodeError as e: logging.error(f "Failed to load {file_path}: {e}" ) return pd.DataFrame(logs) def transform_audit_logs (logs_df, schema): """監査ログをBigQuery用の形式に変換""" transformed_logs = [] schema_field_names = {field.name for field in schema} logs_df.columns = [clean_field_name(col) for col in logs_df.columns] if "_timestamp" in logs_df.columns: logs_df[ "_timestamp" ] = pd.to_datetime(logs_df[ "_timestamp" ], unit= 'ms' , utc= True ) logs_df = logs_df.rename(columns={ "_timestamp" : "timestamp" }) for _, log in logs_df.iterrows(): transformed_log = {} for key, value in log.items(): if key in schema_field_names and not pd.isnull(value): if isinstance (value, pd.Timestamp): value = value.isoformat() elif isinstance (value, bool ): value = int (value) transformed_log[key] = value if transformed_log: transformed_logs.append(transformed_log) return transformed_logs def insert_rows_with_retry (client, table_ref, rows, retries= 3 , delay= 10 ): """BigQueryにデータをリトライ付きで挿入""" for attempt in range (retries): try : errors = client.insert_rows_json(table_ref, rows) if errors: logging.error(f "Errors occurred during insertion: {errors}" ) time.sleep(delay) else : logging.info( "Data uploaded successfully." ) return except GoogleAPIError as e: logging.error(f "Failed to upload data to BigQuery: {e}" ) time.sleep(delay) logging.error(f "Failed to insert rows into {table_ref} after {retries} attempts." ) sys.exit( 1 ) def main (): project_id = os.getenv( 'BQ_GCP_PROJECT_ID' ) dataset_id = os.getenv( 'BQ_DATASET' ) table_id = os.getenv( 'BQ_TABLE' ) table_ref = f "{project_id}.{dataset_id}.{table_id}" client = get_bigquery_client() logs_df = load_audit_logs() if not logs_df.empty: table = create_table_if_not_exists(client, table_ref, logs_df) rows_to_insert = transform_audit_logs(logs_df, table.schema) insert_rows_with_retry(client, table_ref, rows_to_insert) else : logging.error( "No logs to process. Exiting." ) sys.exit( 1 ) if __name__ == "__main__" : main() GitHub App の作成 監査ログを取得するための GitHub App を作成します。詳細な手順は以下を参照してください。 参考 : Enterprise 向け GitHub Apps の作成 GitHub App の Permissions は以下のとおりに設定します。 Repository permissions Administration: Read-only Metadata: Read-only Organization permissions Administration: Read-only 参考 : GitHub Appの権限について (General)から App ID を確認し、控えておきます。(後で GitHub の secret に登録します) App ID の確認 同画面から(Private keys)を作成し、ダウンロードします。(後で GitHub の secret に登録します) Private keys の作成 (Install App)から対象の組織を確認し、(Install)を選択します。 組織へのインストール (Only select repositories)から GitHub Actions を構築するレポジトリを選択し、(Install)を選択します。 GitHub App をインストールするレポジトリの選択 GitHub Actions のシークレット登録 作成した GitHub App を GitHub Actions で使用するため、リポジトリの Secrets(シークレット)に登録します。登録対象は以下の 2 つです。 App ID 名前 : APP_ID 値 : 前手順で確認した GitHub App の App ID Private keys 名前 : APP_PRIVATE_KEY 値 : PEM キーの値をコピーして貼り付ける Secrets の登録 参考 : リポジトリのシークレットの作成 動作確認 手動実行 GitHub の(Actions)タブから対象ワークフローを選択し、取得期間のパラメータ( Start date 、 End date )を入力して(Run workflow)で実行します。 手動実行 ※ 一度に180日分を取得可能ですが、ログ数が多い場合、BigQuery へエクスポートする際に以下エラーで失敗することがあります。発生した場合、取得期間を短くして再実行してください。 Error: -06 08:40:51, 290 [ ERROR ] Failed to upload data to BigQuery: Timeout of 600 .0s exceeded, last exception: HTTPSConnectionPool ( host = ' bigquery.googleapis.com ' , port = 443 ) : Max retries exceeded with url: /bigquery/v2/projects/xxxxx/datasets/xxxxx/tables/xxxxx/insertAll? prettyPrint =false ( Caused by SSLError ( SSLEOFError ( 8 , ' EOF occurred in violation of protocol (_ssl.c:2426) ' ))) また、監査ログ API にはレート上限があり、1 時間に最大 1,750 クエリ(1 クエリで最大 100 件のログ取得が可能)の制限があります。この制限を超えた場合、403 または 429 エラー応答で失敗するため、ご注意ください。 参考 : レート上限 実行が成功したら、BigQuery を確認し、指定した期間の監査ログが格納されたテーブルがあることを確認します。 手動実行成功 監査ログの確認 スケジュール実行 JST 0時にスケジュール実行され、ログが BigQuery にエクスポートされます。GitHub 側の負荷により実行までに遅延が発生する場合もありますので、ご注意ください。 参考 : schedule GitHub の(Actions)から対象のワークフローが成功していることを確認します。(Event)から(schedule)を選択することで、スケジュール実行されたワークフローのみが表示されます。 フィルタ方法 処理が成功し、Artifacts にファイル(実行時間が記録された txt ファイル)があることを確認します。次回の実行時はこの時間以降のログを取得し、BigQuery へエクスポートします。 スケジュール実行成功 last_run_timestamp の中身 BigQuery を確認し、監査ログが出力されていることを確認します。 監査ログの確認 三浦 健斗 (記事一覧) クラウドソリューション部 2023年10月よりG-genにジョイン。元オンプレ中心のネットワークエンジニア。ネットワーク・セキュリティ・唐揚げ・辛いものが好き。
G-gen の杉村です。2024年11月のイチオシ Google Cloud アップデートをまとめてご紹介します。記載は全て、記事公開当時のものですのでご留意ください。 はじめに Eventarc Advanced が登場(Preview) Vertex AI Search で streaming answer メソッド(GA with Allowlist) Dataplex で automatic discovery of Cloud Storage data が Preview データ分類ラベルが Gmail にも対応 Google Cloud 認定試験に新試験が誕生 Application Load Balancer で Service Extensions が Preview 公開 Pub/Sub で Cloud Storage import topic が利用可能に 新サービス Audit Manager が公開(GA) Vertex AI で Gemini による「バッチ推論」が可能に(GA) GKE のコントロールプレーンに DNS ベースのアクセスが登場 Gemini サイドパネルの日本語版が Alpha 版 → GA Cloud Run で in-memory volume が Preview -> GA Cloud Storage で Bucket IP filtering が Preview 公開 BigQuery の Search index で数値列に対する最適化が Preview → GA Cloud SQL Enterprise Plus edition で write endpoint が Preview 公開 Cloud SQL Studio で IAM データベース認証が使えるように Cloud SQL for PostgreSQL のバックアップから AlloyDB クラスタが起動できるように 2025年1月25日より Cloud Run 関連ロールに仕様変更あり Google Chat に音声ミーティング huddles が導入 はじめに 当記事では、毎月の Google Cloud アップデートのうち特に重要なものをまとめます。 また当記事は、Google Cloud に関するある程度の知識を前提に記載されています。前提知識を得るには、ぜひ以下の記事もご参照ください。 blog.g-gen.co.jp リンク先の公式ガイドは、英語版で表示しないと最新情報が反映されていない場合がありますためご注意ください。 Eventarc Advanced が登場(Preview) Choose Eventarc Advanced or Eventarc Standard (2024-10-31) Eventarc Advancedが登場(Preview)。 従来版(Standard)との違いは、より詳細な制御、他対他のファンイン・ファンアウト、メッセージの変換など。Bus、Enrollment、Pipelineの3要素で構成される。 Vertex AI Search で streaming answer メソッド(GA with Allowlist) Stream answers (2024-10-31) Vertex AI Search に streaming answer メソッドが登場。 回答の一部が生成され次第ストリーミングで返却される。現在のところ英語のみ対応。利用には申請が必要。 Dataplex で automatic discovery of Cloud Storage data が Preview Discover and catalog Cloud Storage data (2024-11-05) Dataplex で automatic discovery of Cloud Storage data が Preview 公開。 Cloud Storage 上の CSV、JSONL、Parquet、Avro、ORC を自動で検出し、BigQuery の外部テーブルやオブジェクトテーブルを生成してくれる。 以前より Dataplex には、同様の Data Discovery 機能が存在していたが、今回のアップデートでは Dataplex の Lake が作成されず、そのまま BigQuery から使える(Dataplex の管理機構を通らない)ことが違い。 データ分類ラベルが Gmail にも対応 Data classifications labels for Gmail are now available in open beta (2024-11-01) データ分類ラベルが Gmail にも対応。"Sensitive"、"Confidential" といったラベルをコンテンツ内容に応じて自動付与し、ラベルに応じて外部送信をブロックするなどが可能。エディションごとに使える機能が違うことに注意。 Google Cloud 認定試験に新試験が誕生 Google Cloud Certification Associate Google Workspace Administrator(Beta) 2024年10月22日、Beta 版として公開 Google Workspace の管理業務やトラブルシューティングに関する知識を問う Associate Data Practioner(Beta) 2024年10月30日、Beta 版として公開 Google Cloud 上のデータやデータパイプラインの管理に関する知識を問う Professional Cloud Architect 新しい更新試験の Beta 版が受験可能に これまでは、新規受験と更新受験に問題の区別がなかった 2つの新試験については、G-gen から既に試験対策記事が公開された。 blog.g-gen.co.jp blog.g-gen.co.jp Application Load Balancer で Service Extensions が Preview 公開 Now run your custom code at the edge with the Application Load Balancers (2024-10-30) Application Load Balancer で、エッジ側でサーバーレスプログラムを動作させる Service Extensions が Preview 公開。 プログラムのフォーマットは WebAssembly (Wasm)。ALB側でコードが動くので低遅延で稼働する。HTTPヘッダ操作、カスタム認証、カスタムロギング、HTML書き換えなどに利用できる。 Pub/Sub で Cloud Storage import topic が利用可能に Create a Cloud Storage import topic (2024-11-06) Pub/Sub で Cloud Storage import topic が利用可能に。 Publisher サービスを開発することなく Cloud Storage オブジェクトを Pub/Sub に取り込んで宛先に挿入できる。イベントドリブンなアーキテクチャをより容易に実装可能に。 以下の記事も参照。 blog.g-gen.co.jp 新サービス Audit Manager が公開(GA) Audit Manager overview (2024-11-07) Google Cloudで新サービスAudit Managerが公開(GA)。 ISO 27001 や SOC2、PCI DSS 等に準拠するために証跡を自動収集。料金は Free と Premium の 2 tiers。 Free tier では以下の監査対応が可能。 SOC2 Google-recommended AI controls Premium tier($7,500/yr)では以下の監査対応が可能。 NIST 800-53 Revision 4 CIS Controls v8 PCI DSS 4.0 Cloud Controls Matrix 4.0 NIST CSF v1 CIS Google Cloud Foundation Benchmark 2.0 ISO 27001 2022 Vertex AI で Gemini による「バッチ推論」が可能に(GA) Batch prediction (2024-11-08) Vertex AI で Gemini による「バッチ推論」が可能に(GA)。 1度のリクエストで多数のプロンプトを送信。同量の標準リクエストより50%引きの料金で利用できる。レスポンスは BigQuery または Cloud Storage に非同期出力。Gemini 1.5 Pro/Flash 等で利用可能。 GKE のコントロールプレーンに DNS ベースのアクセスが登場 About network isolation in GKE (2024-11-11) Google Kubernetes Engine(GKE)で、コントロールプレーンへのアクセス方法に DNS ベースのアクセスが登場。 従来の IP ベースだと、クラスタをプライベートネットワークに閉じ込め、接続元 IP アドレス(承認済みネットワーク)で制御していたが、DNS ベースのアクセスでは IAM で認証。 以下の記事も参照。 blog.g-gen.co.jp Gemini サイドパネルの日本語版が Alpha 版 → GA Now generally available: use Gemini in the side panel of Workspace apps in seven additional languages (2024-11-07) Gemini for Google Workspace サイドパネルの日本語版が Alpha 版 → GA(一般公開)。2024-10-16 に Alpha 版として公開されていた。 なお、他の言語も同時に GA。今回の公表で GA された言語は以下のとおり。 フランス語 ドイツ語 イタリア語 日本語 韓国語 ポルトガル語 スペイン語 Cloud Run で in-memory volume が Preview -> GA Configure in-memory volume mounts for services (2024-11-12) Cloud Run で in-memory volume が Preview -> GA。 コンテナのメモリ領域をファイルシステムとして利用。Cloud Run service と jobs の両方で利用可能。 Cloud Storage で Bucket IP filtering が Preview 公開 Bucket IP filtering (2024-11-14) Cloud Storage で Bucket IP filtering が Preview 公開。 バケットで接続元 IP アドレス制限が可能になった。従来も VPC Service Controls で IP アドレスベースの制限はできたが、当機能ではバケット単位での制御が可能になる。また、接続元 VPC も制限できる。 ただし、東京・大阪リージョン未対応なので注意。 BigQuery の Search index で数値列に対する最適化が Preview → GA Optimize with numeric predicates (2024-11-19) BigQuery の Search index で INT64 と TIMESTAMP 列に対する述語(=、IN)での最適化が Preview → GA。 Search index はテーブルの特定列にインデックスを作成しておけば WHERE 句での絞り込み(検索)が高速化する仕組み。インデックスは自動更新。 Cloud SQL Enterprise Plus edition で write endpoint が Preview 公開 Optimize with numeric predicates (2024-11-19) Cloud SQL(for PostgreSQL、MySQL)Enterprise Plus edition で write endpoint が Preview。 書き込みエンドポイント(write endpoint)とは、常にプライマリインスタンスのプライベート IP アドレスを指すエンドポイントのことで、DNS 名を持つ。 書き込みエンドポイントを使って接続することで、別リージョンに構成したリードレプリカをプライマリインスタンスとして昇格した際も、アプリケーション側で向き先 IP アドレスを変更する必要がない。 Cloud SQL Auth Proxy からは書き込みエンドポイントを使えないことに注意。 Cloud SQL Studio で IAM データベース認証が使えるように IAM database authentication (2024-11-20) Cloud SQL Studio で IAM データベース認証が使えるように。Cloud SQL Studio とは、Google Cloud コンソールからデータベースにクエリしたり管理できる UI。これまではユーザー・パスワードを入力して認証する必要があった。 Cloud SQL for PostgreSQL のバックアップから AlloyDB クラスタが起動できるように Migrate from Cloud SQL for PostgreSQL to AlloyDB for PostgreSQL (2024-11-21) Cloud SQL for PostgreSQL のバックアップから AlloyDB クラスタが起動できるようになった(Preview)。 Cloud SQL から AlloyDB への移行が容易に実現できるようになった。 2025年1月25日より Cloud Run 関連ロールに仕様変更あり [Action Required] Ensure read access on container images deployed to Cloud Run - G-gen Tech Blog (2024-11-25) 2025年1月25日より、Cloud Runのデプロイを行うことができる事前定義ロール(Cloud Run 管理者 / デベロッパー)の仕様に変更。 放置すると CI/CD パイプラインに影響がある可能性があるため、要確認。詳細は以下の記事を参照。 blog.g-gen.co.jp Google Chat に音声ミーティング huddles が導入 Introducing huddles: instant-on, audio-first meetings in Google Chat (2024-11-27) Google Chat に音声ミーティング huddles が導入。Chat からシームレスに音声・動画通話をスタートできる。バックエンドは Google Meet。2024-11-27から順次ロールアウトされる。 ほとんどのエディションで利用可能。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
G-gen の武井です。当記事では IAM Deny policies(拒否ポリシー)を使った予防的統制について解説します。 はじめに 当記事について 予防的統制 拒否ポリシー 拒否ポリシーの使い所 拒否ポリシーと組織のポリシーの違い 検証の概要 目的 前提 環境 必要な IAM ロール 拒否ポリシーでサポートされる権限 環境構築 ソースコード 実行結果 動作確認 拒否ポリシー設定前 拒否ポリシー設定後 関連記事 はじめに 当記事について 当記事では、 IAM Deny policies (以下、拒否ポリシー)で特定の操作を制限し、Google Cloud 環境に予防的統制を効かせる方法を解説します。 予防的統制 予防的統制とは、 リスク (意図しない操作、不正な操作) を未然に防ぐための統制 を意味します。 以下は Google Cloud における予防的統制の一例です。 使用するプロダクト 効果 拒否ポリシー (Cloud IAM) リソースに対する特定の操作を IAM パーミッション (権限) に基づき制限する 組織のポリシー (Resource Manager) リソースに対する特定の操作を 制約 (定義済みのアクション) に基づき制限する VPC Service Controls API へのアクセスをコンテキストベースのルールに基づき制限する 拒否ポリシー 拒否ポリシーは、 通常の IAM Policy よりも強い強制力で Google Cloud リソースへの操作を制限 します。 IAM ポリシーの評価フローでは、拒否ポリシー(明示的な Deny)が通常の IAM Policy(明示的な Allow)より先に評価され、優先的に適用されます。 そのため、拒否ポリシーが設定されている場合、IAM Policy を上回る強制力で当該操作を拒否します。 IAM におけるポリシーの評価フロー 拒否ポリシーの詳細は以下の記事で解説しています。 blog.g-gen.co.jp 拒否ポリシーの使い所 ポリシーの評価順は前述の通り、拒否ポリシー(明示的な Deny)が最も強い強制力を持ちます。 そのため、IAM による権限設計では、まずは拒否ポリシーを使わずに IAM Policy(明示的な Allow)で管理することを原則とし、 どうしても強い権限で拒否したい(権限にフタをしたい)場合に拒否ポリシーを使う という方針が望ましいと言えます。 拒否ポリシーは統制の度合いとしては強力なため、安易に使ってしまうと後から修正が難しくなる場合があるのでご注意ください。 拒否ポリシーと組織のポリシーの違い Google Cloud の予防的統制には、拒否ポリシーに似た仕組みとして 組織のポリシー (Resource Manager の1機能)があります。 前者は IAM パーミッション(権限) 、後者は 制約 と呼ばれる定義済みのアクションにもとづいて操作を制限するという点に違いがあります。 参考 : ロールのコンポーネント (権限) 参考 : 組織のポリシーの制約 検証の概要 目的 以下の要件にもとづき、組織リソースに対して拒否ポリシーを適用し、実際の動作を確認します。 組織のポリシー(制約)の作成・削除・更新 についてはすべてのプリンシパルで禁止にしつつ、Terraform が使用するサービスアカウント、ならびに管理者グループだけは例外(操作可能)としています。 # 拒否したい操作 拒否対象のプリンシパル 例外のプリンシパル 1 組織のポリシーの作成 すべて ・Terraform 用 サービスアカウント ・管理者グループ 2 組織のポリシーの削除 すべて ・Terraform 用 サービスアカウント ・管理者グループ 3 組織のポリシーの更新 すべて ・Terraform 用 サービスアカウント ・管理者グループ 前提 デモを実施するにあたり、以下の IAM Policy を設定しています。 # プリンシパル 付与した IAM ロール 付与したリソース (場所) 1 Terraform 用 サービスアカウント ・オーナー ・組織管理者 ・組織ポリシー管理者 ・拒否管理者 組織リソース 2 管理者グループ ・オーナー ・組織管理者 ・組織ポリシー管理者 ・拒否管理者 組織リソース 3 非管理者グループ ・組織ポリシー管理者 組織リソース 環境 拒否ポリシーの設定は Terraform で行います。 また、拒否ポリシー設定後は以下の条件で動作確認を行います。 # プリンシパル 操作 期待する動作 1 管理者グループ 組織のポリシーを作成 成功 2 非管理者グループ 組織のポリシーを削除 失敗 必要な IAM ロール 拒否ポリシーを管理する場合、組織レベルで 拒否管理者ロール(roles/iam.denyAdmin) が必要です。 今回のデモでは Terraform が使用するサービスアカウントに対し、組織レベルで上記ロールを付与しています。 参考 : 必要なロール 拒否ポリシーでサポートされる権限 拒否ポリシーでは一部の権限を指定することはできません。 拒否ポリシーにてサポートされる権限については以下をご確認ください。 参考 : Permissions supported in deny policies 環境構築 ソースコード 今回の検証では、Terraform で環境構築を実施します。使用した Terraform のソースコードは以下のとおりです。 $ tree . ├── env │ └── organization │ ├── backend.tf │ ├── locals.tf │ ├── main.tf │ └── versions.tf └── modules └── preventive_controls └── organization_deny_policies ├── main.tf ├── outputs.tf └── variables.tf # main.tf (modules) resource "google_iam_deny_policy" "default" { parent = urlencode ( "cloudresourcemanager.googleapis.com/organizations/$ { var.organization_id } " ) name = "dev-ggen-deny-policy-organization" display_name = "Dev G-gen Deny Policy Organization" # https://cloud.google.com/iam/docs/deny-permissions-support # 組織のポリシーの編集禁止 rules { description = "First rule" deny_rule { denied_principals = [ "principalSet://goog/public:all" ] denied_permissions = [ "orgpolicy.googleapis.com/policies.create" , "orgpolicy.googleapis.com/policies.delete" , "orgpolicy.googleapis.com/policies.update" , ] exception_principals = var.permitted_principals } } } # variables.tf variable "permitted_principals" { description = "List of principals that are exempt from the deny policy" type = list ( string ) } variable "organization_id" { description = "The ID of the organization to create resources in" type = string } # main.tf (env) module "organization_deny_policies" { source = "../../modules/preventive_controls/organization_deny_policies" organization_id = local.organization_id permitted_principals = local.permitted_principals } # locals.tf locals { organization_id = "1234567890" # https://cloud.google.com/iam/docs/principal-identifiers#v2 permitted_principals = [ "principal://iam.googleapis.com/projects/-/serviceAccounts/terraform-gha-sa@iam-demo-prj.iam.gserviceaccount.com" , "principalSet://goog/group/ggen-administrator-test@dev.g-gen.co.jp" , ] } 参考 : google_iam_deny_policy 参考 : プリンシパルID (IAM v2 API) 実行結果 terraform apply の実行結果は以下のとおりです。 Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # module.organization_deny_policies.google_iam_deny_policy.default will be created + resource "google_iam_deny_policy" "default" { + display_name = "Dev G-gen Deny Policy Organization" + etag = (known after apply) + id = (known after apply) + name = "dev-ggen-deny-policy-organization" + parent = "cloudresourcemanager.googleapis.com%2Forganizations%2F1234567890" + rules { + description = "First rule" + deny_rule { + denied_permissions = [ + "orgpolicy.googleapis.com/policies.create" , + "orgpolicy.googleapis.com/policies.delete" , + "orgpolicy.googleapis.com/policies.update" , ] + denied_principals = [ + "principalSet://goog/public:all" , ] + exception_principals = [ + "principal://iam.googleapis.com/projects/-/serviceAccounts/terraform-gha-sa@iam-demo-prj.iam.gserviceaccount.com" , + "principalSet://goog/group/ggen-administrator-test@dev.g-gen.co.jp" , ] } } } Plan: 1 to add, 0 to change, 0 to destroy. module.organization_deny_policies.google_iam_deny_policy.default: Creating... module.organization_deny_policies.google_iam_deny_policy.default: Still creating... [ 10s elapsed ] module.organization_deny_policies.google_iam_deny_policy.default: Creation complete after 11s [ id=cloudresourcemanager.googleapis.com%2Forganizations%2F1234567890/dev-ggen-deny-policy-organization ] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Cloud コンソール上で表示される拒否ポリシーの詳細 動作確認 拒否ポリシー設定前 両グループとも IAM Policy によって組織のポリシーの編集が許可されているため、当該操作が実行できました。 #1、組織のポリシーが編集(作成)できた #2、組織のポリシーが編集(削除)できた 拒否ポリシー設定後 非管理者グループにおいては、拒否ポリシー設定前には実行できた操作が、設定後には制限されました。 # プリンシパル 操作 期待する動作 実際の結果 1 管理者グループ 組織のポリシーを作成 成功 成功 2 非管理者グループ 組織のポリシーを削除 失敗 失敗 #1、拒否ポリシー設定後も組織のポリシーが編集(作成)できた #2、拒否ポリシーによって組織のポリシーの編集(削除)が制限された 関連記事 今回ご紹介した他にも、G-gen Tech Blog では予防的統制に関する記事を多数公開しています。これらもあわせてご確認ください。 blog.g-gen.co.jp blog.g-gen.co.jp blog.g-gen.co.jp 武井 祐介 (記事一覧) クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア Google Cloud Partner Top Engineer 2025 選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 趣味はロードバイク、ロードレースやサッカー観戦です。 Follow @ggenyutakei
2024年11月25日、Google Cloud から管理者宛てに「 [Action Required] Ensure read access on container images deployed to Cloud Run 」というタイトルのメール通知がありました。当記事では、通知の内容とユーザー側で必要なアクション、影響範囲の確認方法などを解説します。 通知の内容 変更の適用前(2025年1月15日以前) 変更の適用後(2025年1月15日以降) ユーザー側で必要なアクション 想定される影響範囲 影響範囲の確認方法 通知メール Cloud Asset Inventory Cloud Logging Cloud Run functions への影響 対処していない場合に発生するエラーの例 通知の内容 Cloud Run のデプロイに使用することができる事前定義の IAM ロール「 Cloud Run 管理者 ( roles/run.admin )」および「 Cloud Run デベロッパー ( roles/run.developer )」には、 暗黙的に Artifact Registory の読み取り権限がついています。 Cloud Run 管理者や Cloud Run デベロッパーには Artifact Registry の読み取り権限が「明示的には」付与されていない 2025年1月15日(米国時間)以降 、この暗黙的な読み取り権限がなくなり、 Cloud Run 管理者 ロールと Cloud Run デベロッパー ロールでは Artifact Registry にあるコンテナイメージの読み取りができなくなります。したがって、このロールだけでは Cloud Run の作成、更新ができなくなります。 今後、Cloud Run の作成、更新を行うプリンシパル(ユーザー、サービスアカウント)は、コンテナイメージに対する明示的な読み取り権限が必要となります。具体的には、コンテナイメージが存在する Artifact Registry リポジトリ、もしくはプロジェクトに対する Artifact Registry 読み取り ( roles/artifactregistry.reader )ロールが必要となります。 以下に、変更の適用前・適用後の状況を要約します。 変更の適用前(2025年1月15日以前) 暗黙的な権限により、 Cloud Run 管理者 または Cloud Run デベロッパー ロールを紐づけられたプリンシパルは Artifact Registry にあるコンテナイメージを読み取ることができる。 変更の適用後(2025年1月15日以降) Cloud Run 管理者 ロールおよび Cloud Run デベロッパー ロールでは Artifact Registry にあるコンテナイメージを読み取ることはできない。そのため Cloud Run の作成、更新操作ができない。 今まで Cloud Run のデプロイに Cloud Run 管理者 または Cloud Run デベロッパー ロールを使用していたプリンシパルには、 Artifact Registry 読み取り ロールも紐づける必要がある。 オーナー ( roles/owner )ロール、または 編集者 ( roles/editor )ロールが紐づいているプリンシパルについては、この変更による影響はない。 ユーザー側で必要なアクション 2025年1月15日 までに、 Cloud Run 管理者 または Cloud Run デベロッパー ロールが紐づけられたプリンシパルを洗い出し、必要に応じて Artifact Registry 読み取り ロールを付与する必要があります。 想定される影響範囲 Cloud Build や GitHub Actions 等の CI/CD パイプラインから Cloud Run のデプロイを行っているようなケースでは、パイプラインが使用するサービスアカウントに Cloud Run 管理者 もしくは Cloud Run デベロッパー ロールが付与されている可能性があります。 今回の変更への対処が漏れてしまった場合、パイプラインが停止してしまう恐れがあるため、現在サービスアカウントに紐づけられているロールを確認し、権限が不足している場合は Artifact Registry 読み取り ロール等を付与する必要があります。 影響範囲の確認方法 通知メール 変更の影響を受ける可能性がある組織やプロジェクトのオーナーや、エッセンシャルコンタクトに登録された連絡先には、2024年11月25日に Google Cloud から [Action Required] Ensure read access on container images deployed to Cloud Run というタイトルのメールが配信されています。 変更の影響があるプロジェクトは、同メール最下部の「 Your affected projects are listed below: 」以下に記載されています。 変更の影響があるプロジェクトはメールに記載されている エッセンシャルコンタクトについては以下の記事を参照してください。 参考 : Google Cloudの組織(Organization)を徹底解説 - エッセンシャルコンタクト Cloud Asset Inventory また、Cloud Asset Inventory を使用することで、特定のロールが紐づいたプリンシパルの一覧を確認できます。 Google Cloud コンソールから確認する場合、たとえば Cloud Run デベロッパー ロールであれば、Cloud Asset Inventory の画面の IAM ポリシー タブにて roles: run.developer でフィルタリングします。プロジェクトに対してロールが紐づけられているプリンシパルと、特定の Cloud Run リソースにロールが紐づけられているプリンシパルの両方を検索することができます。 Cloud Run 管理者 ロールについては roles: run.admin でフィルタリングすることができます。 Asset Inventory を使用して、対処が必要なプリンシパルを確認する gcloud CLI では gcloud asset search-all-iam-policies コマンドで確認できます。 # Cloud Run デベロッパーロールが付与されたプリンシパルの一覧を表示する $ gcloud asset search-all-iam-policies --query =' roles:run.developer ' # 出力例 $ gcloud asset search-all-iam-policies --query =' roles:run.developer ' --- assetType: run.googleapis.com/Service # ロールが特定の Cloud Run に紐付いている場合 folders: - folders/xxxxxxxxxxxx - folders/xxxxxxxxxxxx organization: organizations/xxxxxxxxxxxx policy: bindings: - members: - serviceAccount:my-sa@myproject.iam.gserviceaccount.com role: roles/run.developer project: projects/xxxxxxxxxxxx resource: //run.googleapis.com/projects/myproject/locations/asia-northeast1/services/hello --- assetType: cloudresourcemanager.googleapis.com/Project # ロールがプロジェクトに紐付いている場合 folders: - folders/xxxxxxxxxxxx - folders/xxxxxxxxxxxx organization: organizations/xxxxxxxxxxxx policy: bindings: - members: - serviceAccount:xxxxxxxxxxxx-compute@developer.gserviceaccount.com role: roles/run.developer project: projects/xxxxxxxxxxxx resource: //cloudresourcemanager.googleapis.com/projects/myproject Cloud Asset Inventory については、以下の記事も参考にしてください。 参考 : Cloud Asset Inventoryを徹底解説! Cloud Logging 2025年1月15日の変更が適用されるまでは、該当のロールを使用して Cloud Run のデプロイが可能ですが、Cloud Logging に以下のエラーログが記録されるようになっています。 Cloud Run API check failed. Requests will be rejected after January 2025 hard enforcement deadline. User does not have access to image {Artifact Registry 内のコンテナイメージの URL} Cloud Logging にて以下のクエリを実行することで、当該ログを検索することができます。 resource . type = " cloud_run_revision " severity=ERROR " User does not have access to image " 変更の適用前もデプロイは可能だが、エラーログが記録される ログが出ている場合、対処が必要なプリンシパルを使用してデプロイが行われているので、 Artifact Registry 読み取り ロール等の付与を行います。 上記のクエリを使用してログベースのアラートを設定しておくことで、対象のログの確認漏れを防ぐことができます。 ログベースのアラートについては、以下の記事を参考にしてください。 参考 : Cloud Loggingの概念と仕組みをしっかり解説 - ログベースのアラート Cloud Run functions への影響 Cloud Run functions(第2世代)は、実行基盤として Cloud Run がベースになっています。Cloud Run functions に、今回の変更は影響があるのでしょうか。 今回の変更について、Cloud Run functions への影響はありません。Cloud Run functions のデプロイ時は、サービスエージェントと呼ばれる特殊なサービスアカウントが、ビルドや Artifact Registry からのイメージ取得を行います。これらのアカウントにデフォルトで付与されているロールを変更しなければ、今回の変更では影響がないようになっています。 Cloud Run functions のデプロイの仕組みに関する詳細は、以下のドキュメントに記載されています。 参考 : 管理者サービス アカウント 対処していない場合に発生するエラーの例 たとえば、Cloud Run のデプロイを行っているサービスアカウントが、別プロジェクトにある Artifact Registry リポジトリの読み取り権限を持たない場合、監査ログに以下のようなエラーが記録されます。 "User must have permission to read the image, asia-northeast1-docker.pkg.dev/<プロジェクトID>/<リポジトリ名>/<イメージ名>:latest. Ensure that the provided container image URL is correct and that the above account has permission to access the image. If you just enabled the Cloud Run API, the permissions might take a few minutes to propagate. Note that the image is from project [<プロジェクトID>], which is not the same as this project [<サービスアカウントがあるプロジェクトのID>]. Permission must be granted to the User from this project. See https://cloud.google.com/run/docs/deploying#other-projects \n Cause: 403 Forbidden\nGET https://asia-northeast1-docker.pkg.dev/v2/ プロジェクトID>/<リポジトリ名>/<イメージ名>/manifests/latest\n{\"errors\":[{\"code\":\"DENIED\",\"message\":\"Permission \\"artifactregistry.repositories.downloadArtifacts\\" denied on resource \\"projects/<プロジェクトID>/locations/asia-northeast1/repositories/<リポジトリ名>\\" (or it may not exist)\"}]}\n" 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
当記事では、生成 AI の出力を迅速かつ効率的に評価できる Vertex AI 上の API である、 Gen AI evaluation service を紹介します。 概要 ユースケース 評価指標について 評価タイプ 計算ベース モデルベース 料金 使ってみる 概要 準備 実行と結果 その他 クォータの制限について 評価データセットの件数 概要 Gen AI evaluation service は、生成 AI アプリケーションの出力を効率的に評価するための機能です。Vertex AI の1機能として、API で提供されます。この機能を使うと、事前定義された評価指標や、ユーザーが独自に定義したカスタム評価指標を用いて、生成 AI アプリケーションのパフォーマンスを 定量的に評価 できます。 同様の LLM 評価ツールとしては、オープンソースのフレームワークである Ragas などがありますが、Gen AI evaluation service は Vertex AI とシームレスに統合されている 点と、 マネージドサービスでありインフラの管理が不要な点 がメリットです。一方で、Ragas に比べ評価指標テンプレートが少ない点や、少額ではありますが API 利用料金が発生するといったデメリットがあります。 参考 : Gen AI evaluation service overview ユースケース Gen AI Evaluation Service は、以下のようなユースケースで役立ちます。 生成 AI モデルの選定 最適なモデルパラメータを探索 プロンプトエンジニアリングの調整 ファインチューニングの評価 RAG(Retrieval Augmented Generation)の評価 Function calling の評価 以下の公式ドキュメントでは、ユースケース別のサンプル Notebook が公開されており、参考にすることができます。 参考 : Notebooks for evaluation use cases 評価指標について 評価タイプ Gen AI evaluation service には、 計算ベース (Computation-based)と モデルベース (Model-based)の 2 種類の評価タイプがあります。 計算ベースの評価は、正解データとの比較に基づいてスコアを算出します。処理速度が速いため、リアルタイム評価にも適しています。代表的な指標として、自然言語処理で広く使われる BLEU や ROUGE などがあります。 しかし、LLM の出力評価では、そもそも「正解データ」を準備することが難しい場合があります。これは、LLM の出力に対して単一の正解を定めにくいためです。このため、多くの場合、人間の判断による評価が行われますが、スケールするにつれて手間が増加するという課題もあります。こうした背景から、現在ではモデルベースの評価が注目されています。 モデルベースの評価は、LLM 自体を判定モデルとして用い、人間による評価に近い形で評価します。正解データを必須とせず、「流暢さ」や「一貫性」といった複雑な基準での評価が可能で、プロンプトの調整により柔軟な評価基準を設定できます。 評価タイプ 評価アプローチ 正解データ(Ground truth) レイテンシ 計算ベース 数式を用いて評価する 必須 早い モデルベース 判定モデル (LLM) に評価させる 任意 遅い 計算ベース 計算ベースの評価指標は、候補モデル(評価対象の LLM)の出力が正解データにどれだけ一致しているかを数値化します。以下はテキスト生成向けの評価指標です。 評価指標 説明 適するユースケース Exact match 候補モデルの出力が正解データと完全一致する場合は「1」、しない場合は「0」を出力 QA や分類タスク BLEU 候補モデルの出力と正解データの n-gram の一致度を算出。出力は [0 ~ 1] の範囲で、スコアが高いほど生成テキストが正解に近いことを示す。 翻訳タスク ROUGE 候補モデルの出力と正解データの n-gram の F1-score を算出。出力は [0 ~ 1] の範囲で、スコアが高いほど内容が類似していることを示す。 要約タスク その他、Function Calling 向けの評価指標などもあります。詳細は以下の公式ドキュメントをご参照ください。 参考 : Computation-based metrics モデルベース モデルベースの評価指標では、LLM が判定モデルとして機能し、候補モデルの出力を評価します。この手法は一般的に、 LLM-as-a-Judge とも呼ばれます。 評価方式には、単一の出力に対する Pointwise と、複数の出力の比較を行う Pairwise があります。 方式 説明 ユースケース Pointwise 単一の候補モデルの出力にスコアを付与 運用段階での継続的なモニタリング Pairwise 2 つの候補モデルの出力を比較し、より適切な方を選択 モデル選定やプロンプト比較 また、Gen AI evaluation service には、様々なタスクに合わせた事前定義済みのプロンプトテンプレートが用意されています。 テキスト生成 マルチターン会話形式 要約 QA 品質 Pointwise ・ Fluency ・ Groundedness など ・ Multi-turn Chat Quality ・ Multi-turn Safety ・ Summarization Quality ・ Question Answering Quality Pairwise ・ Fluency ・ Groundedness など ・ Multi-turn Chat Quality ・ Multi-turn Safety ・ Summarization Quality ・ Question Answering Quality これらのテンプレートは更新される可能性があるため、最新情報は以下の公式ドキュメントをご参照ください。 参考 : Metric prompt templates for model-based evaluation 料金 Gen AI Evaluation Serviceの料金は、入出力の文字数と評価タイプに基づいて計算されます。 評価タイプ 価格 モデルベース (Pointwise, Pairwise) 入力: $0.005 per 1k characters 出力: $0.015 per 1k characters 計算ベース 入力: $0.00003 per 1k characters 出力: $0.00009 per 1k characters 参考 : Vertex AI Pricing - Gen AI Evaluation Service 使ってみる 概要 ここでは、RAG システムで生成された回答の精度を評価する例を紹介します。 構成図 また、筆者の実行環境としては Colab Enterprise を使用します。Colab Enterprise の利用方法は以下の公式ドキュメントのクイックスタートをご参考下さい。 参考 : Create a notebook by using the Google Cloud console 準備 1. ライブラリのインストール !pip install google-cloud-aiplatform[evaluation]== 1.71 . 0 2. Vertex AI インスタンスの初期化 import vertexai from vertexai.evaluation import EvalTask, MetricPromptTemplateExamples, PointwiseMetric import pandas as pd PROJECT_ID = "" # @param {type:"string"} LOCATION = "" # @param {type:"string"} EXPERIMENT = "" # @param {type:"string"} vertexai.init( project=PROJECT_ID, location=LOCATION ) 3. 事前定義済み評価指標テンプレートの確認 MetricPromptTemplateExamples.list_example_metric_names() 出力は以下の通りです。 ['coherence', 'fluency', 'safety', 'groundedness', 'instruction_following', 'verbosity', 'text_quality', 'summarization_quality', 'question_answering_quality', 'multi_turn_chat_quality', 'multi_turn_safety', 'pairwise_coherence', 'pairwise_fluency', 'pairwise_safety', 'pairwise_groundedness', 'pairwise_instruction_following', 'pairwise_verbosity', 'pairwise_text_quality', 'pairwise_summarization_quality', 'pairwise_question_answering_quality', 'pairwise_multi_turn_chat_quality', 'pairwise_multi_turn_safety'] 4. question_answering_quality テンプレートの内容を確認 # プロンプトテンプレートの中身を表示 print (MetricPromptTemplateExamples.get_prompt_template( "question_answering_quality" )) 出力は以下の通りです。 question_answering_quality テンプレートの評価基準は、RAG アーキテクチャを使ったアプリケーションの評価にも使えそうであるため、今回はこのテンプレートをそのまま利用します。 # Instruction You are an expert evaluator. Your task is to evaluate the quality of the responses generated by AI models. We will provide you with the user input and an AI-generated response. You should first read the user input carefully for analyzing the task, and then evaluate the quality of the responses based on the Criteria provided in the Evaluation section below. You will assign the response a rating following the Rating Rubric and Evaluation Steps. Give step-by-step explanations for your rating, and only choose ratings from the Rating Rubric. # Evaluation ## Metric Definition You will be assessing question answering quality, which measures the overall quality of the answer to the question in user input. The instruction for performing a question-answering task is provided in the user prompt. ## Criteria Instruction following: The response demonstrates a clear understanding of the question answering task instructions, satisfying all of the instruction's requirements. Groundedness: The response contains information included only in the context if the context is present in user prompt. The response does not reference any outside information. Completeness: The response completely answers the question with sufficient detail. Fluent: The response is well-organized and easy to read. ## Rating Rubric 5: (Very good). The answer follows instructions, is grounded, complete, and fluent. 4: (Good). The answer follows instructions, is grounded, complete, but is not very fluent. 3: (Ok). The answer mostly follows instructions, is grounded, answers the question partially and is not very fluent. 2: (Bad). The answer does not follow the instructions very well, is incomplete or not fully grounded. 1: (Very bad). The answer does not follow the instructions, is wrong and not grounded. ## Evaluation Steps STEP 1: Assess the response in aspects of instruction following, groundedness, completeness and fluency according to the criteria. STEP 2: Score based on the rubric. # User Inputs and AI-generated Response ## User Inputs ### Prompt {prompt} ## AI-generated Response {response} 5. カスタム評価指標 helpfulness の定義 # カスタムテンプレートを作成 helpfulness_prompt_template = """ You are a professional writing evaluator. Your job is to score writing responses according to pre-defined evaluation criteria. You will be assessing helpfulness, which measures the ability to provide important details when answering a prompt. You will assign the writing response a score from 5, 4, 3, 2, 1, following the rating rubric and evaluation steps. ## Criteria Helpfulness: The response is comprehensive with well-defined key details. The user would feel very satisfied with the content in a good response. ## Rating Rubric 5 (completely helpful): Response is useful and very comprehensive with well-defined key details to address the needs in the instruction and usually beyond what explicitly asked. The user would feel very satisfied with the content in the response. 4 (mostly helpful): Response is very relevant to the instruction, providing clearly defined information that addresses the instruction's core needs. It may include additional insights that go slightly beyond the immediate instruction. The user would feel quite satisfied with the content in the response. 3 (somewhat helpful): Response is relevant to the instruction and provides some useful content, but could be more relevant, well-defined, comprehensive, and/or detailed. The user would feel somewhat satisfied with the content in the response. 2 (somewhat unhelpful): Response is minimally relevant to the instruction and may provide some vaguely useful information, but it lacks clarity and detail. It might contain minor inaccuracies. The user would feel only slightly satisfied with the content in the response. 1 (unhelpful): Response is useless/irrelevant, contains inaccurate/deceptive/misleading information, and/or contains harmful/offensive content. The user would feel not at all satisfied with the content in the response. ## Evaluation Steps STEP 1: Assess comprehensiveness: does the response provide specific, comprehensive, and clearly defined information for the user needs expressed in the instruction? STEP 2: Assess relevance: When appropriate for the instruction, does the response exceed the instruction by providing relevant details and related information to contextualize content and help the user better understand the response. STEP 3: Assess accuracy: Is the response free of inaccurate, deceptive, or misleading information? STEP 4: Assess safety: Is the response free of harmful or offensive content? Give step by step explanations for your scoring, and only choose scores from 5, 4, 3, 2, 1. # User Inputs and AI-generated Response ## User Inputs ### Prompt {prompt} ## AI-generated Response {response} """ # カスタム評価指標を定義 helpfulness = PointwiseMetric( metric= "helpfulness" , metric_prompt_template=helpfulness_prompt_template, ) 6. サンプルデータの定義 今回利用する以下のサンプルデータは以下のとおりです。 questions :ユーザーの質問 retrieved_contexts :コンテキストとなる検索結果 generated_answers :LLM の出力 golden_answers :正解データ questions = [ "富士山はどこの県に位置していますか?" , "東京タワーはどの区に位置していますか?" , "沖縄の主要な伝統料理は何ですか?" , "沖縄の伝統舞踊で有名なものは何ですか?" ] retrieved_contexts = [ "富士山は、静岡県と山梨県にまたがって位置しています。標高3,776メートルのこの山は、日本の最高峰であり、象徴的な自然のシンボルとして親しまれています。" , "東京タワーは、東京都港区に位置しており、1958年に建てられた高さ333メートルの電波塔です。東京のシンボルとして、観光名所となっています。" , "日本の主要な伝統料理には、寿司や天ぷらがあります。また、天ぷらは沖縄でもソウルフードとなっています" , "沖縄の伝統舞踊には、エイサーがあり、太鼓のリズムに合わせて踊られるもので、祭りなどで広く披露されています。" ] generated_answers = [ "静岡県" , "港区" , "天ぷら" , "エイサー" ] golden_answers = [ "静岡県と山梨県" , "港区" , "ゴーヤーチャンプルー、ソーキそば" , "エイサー" , ] 7. 評価データセットの作成 各評価指標の入力に必要なカラム名は以下の通りです。 評価指標 評価タイプ 必要なカラム名 question_answering_quality モデルベース(Pontwise) ・prompt ・response helpfulness モデルベース(Pontwise) ・prompt ・response exact_match 計算ベース ・response ・reference 必要なカラム名に沿って評価データセットを作成します。 # 評価データセットを作成 eval_dataset = pd.DataFrame( { "prompt" : [ "Answer the question: " + question + " Context: " + item for question, item in zip (questions, retrieved_contexts) ], "response" : generated_answers, # 候補モデルの出力 "reference" : golden_answers, # 正解データ } ) 実行と結果 8. 評価を実行 # 評価タスクを定義 eval_task = EvalTask( dataset=eval_dataset, metrics=[ "question_answering_quality" , # モデルベース(事前定義の評価指標) helpfulness, # モデルベース(ユーザー独自の評価指標) "exact_match" # 計算ベース ], experiment=EXPERIMENT, ) # 評価リクエストを実行 result = eval_task.evaluate() evaluate メソッドの戻り値は EvalResult クラスであり以下の属性を持っています。 属性名 説明 summary_metrics 各指標の平均値や標準偏差などのサマリ情報 metrics_table 評価結果の詳細情報 metadata 評価時の実験名などのメタデータ情報 9. 評価結果のサマリを出力 result.summary_metrics 出力は以下の通りです。 {'row_count': 4, 'question_answering_quality/mean': 4.0, 'question_answering_quality/std': 1.1547005383792515, 'helpfulness/mean': 3.0, 'helpfulness/std': 0.816496580927726, 'exact_match/mean': 0.5, 'exact_match/std': 0.5773502691896257} 10. 評価結果の詳細を出力 result.metrics_table 出力は以下の通りです。 文字が見えづらいため、 question_answering_quality 評価指標の最初のレコードのみを以下に記載します。 [ prompt ] Answer the question: 富士山はどこの県に位置していますか? Context: 富士山は、静岡県と山梨県にまたがって位置しています。標高3,776メートルのこの山は、日本の最高峰であり、象徴的な自然のシンボルとして親しまれています。 [ response ] 静岡県 [ reference ] 静岡県と山梨県 [ question_answering_quality/explanation ] The response is incomplete. Although grounded in the given context and fluent, it only mentioned one of the two prefectures where Mt. Fuji is located. The prompt asked "In which prefecture is Mt. Fuji located?" The context clearly stated it's located in both Shizuoka and Yamanashi prefectures. Therefore, the instruction following is weak. ~日本語に翻訳~ 回答は不完全です。与えられた文脈に基づいており流暢ではありますが、富士山がある 2 つの県のうちの 1 つしか言及されていません。プロンプトは「富士山はどの県にありますか?」と尋ねており、文脈から静岡県と山梨県の両方にあることは明らかです。したがって、指示に従うことが不十分です。 [ question_answering_quality/score ] 3.0 定量的なスコアが取得できていることが確認できました。 なお、モデルベースの評価指標の出力には explanation 列が含まれており、判定モデルがスコアを算出するために行った思考の過程が記録されています。つまり、explanation 列の内容が 判定モデルが出力したスコアの根拠 となります。 参考 : View and interpret evaluation results その他 クォータの制限について モデルベースの評価タイプでは、判定モデルに Vertex AI Gemini API が使用されるためクォータには注意が必要です。特に、1 度に大量の評価データセットを含める場合や、評価リクエストの同時実行数が高くなる場合は、 gemini-1.5-pro のクォータの制限緩和をご検討ください。 制限緩和の申請については、公式ドキュメントをご参照ください。 参考 : Run model-based evaluation with increased rate limits and quota 評価データセットの件数 高品質な評価結果を得るためには、評価データセットを 100〜400 件にすることが推奨されています。この範囲であれば、外れ値の影響を最小限に抑えつつ、さまざまなシナリオでのパフォーマンスが期待できます。また、400 件を超えると前述の改善効果が薄れる傾向があるため、一般的な目安として 400 件を上限とすることが推奨されます。 参考 : Best practices G-gen 編集部 (記事一覧) 株式会社G-genは、サーバーワークスグループとして「クラウドで、世界を、もっと、はたらきやすく」をビジョンに掲げ、クラウドの導入から最適化までを支援している Google Cloud 専業のクラウドインテグレーターです。
G-gen の杉村です。Google Cloud(旧称 GCP)で、サービスアカウントから認証キーを作成しようとした際に サービス アカウント キーの作成が無効になっています 組織ポリシーの制約「iam.disableServiceAccountKeyCreation」が組織に適用されています。 と表示されてキーが作成できない場合の、対処法を紹介します。 事象とメッセージ 原因 対処する前に 対処方法 対処手順 IAM 権限の確認 組織、フォルダまたはプロジェクトを選択 組織のポリシー画面へ遷移 制約の編集画面へ遷移 制約を編集 結果の確認 事象とメッセージ Google Cloud(旧称 GCP)で、サービスアカウントから認証キーを作成しようとした際に、以下のメッセージが表示され、サービスアカウントキーが作成できませんでした。 サービス アカウント キーの作成が無効になっています サービス アカウント キーの作成が無効になっています 組織ポリシーの制約「iam.disableServiceAccountKeyCreation」が組織に適用されています。 考えられる原因: 組織ポリシー管理者が、サービス アカウント キーに関連するセキュリティ インシデントを防ぐために、この組織ポリシーを適用しました。また、 「デフォルトで保護」の適用 により、組織にポリシーが自動的に適用された可能性もあります。 推奨される次のステップ: サービス アカウント キーは、適切に管理しなかった場合、セキュリティ リスクとなります。可能であれば、 より安全な代替手段 を選択してください。サービス アカウント キーで認証する必要がある場合は、 組織の「組織ポリシー管理者」(roles/orgpolicy.policyAdmin)のロールを持つ管理者が 「iam.disableServiceAccountKeyCreation」の制約を 無効にする 必要があります。 追跡番号: (英数字) コンソールが英語版の場合、以下のようなメッセージです。 Service account key creation is disabled Service account key creation is disabled The organization policy constraint 'iam.disableServiceAccountKeyCreation' is enforced on your organization. Possible Causes: Your Organization Policy Administrator enforced the Organization Policy to prevent security incidents related to Service Account keys. Alternatively, your organization may have been automatically enforced with the policy as part of Secure by Default enforcements. Recommended Next Steps: Service account keys are a security risk if not managed correctly. You should choose a more secure alternative whenever possible. If you must authenticate with a service account key, an administrator with the "Organization Policy Administrator" (roles/orgpolicy.policyAdmin) role on the organization needs to disable the "iam.disableServiceAccountKeyCreation" constraint. Tracking number: (alphanumeric characters) 原因 この事象は、組織ポリシーの制約 iam.disableServiceAccountKeyCreation が組織レベル、フォルダレベルまたはプロジェクトレベルで有効化されているときに発生します。 組織のポリシー は、セキュリティや統制の向上のために、所定のルールを Google Cloud 環境全体に適用する仕組みのことです。組織のポリシーの詳細は、以下の記事を参照してください。 blog.g-gen.co.jp iam.disableServiceAccountKeyCreation は、サービスアカウントキーの作成を禁止する制約です。この制約は、2024年初頭以降に作成された Google Cloud 組織では デフォルトで有効化 されています。それ以前に作成された組織でも、管理者が明示的にこの制約を有効化している場合は、この事象が発生します。 参考 : サービス アカウント キー作成の無効化 対処する前に サービスアカウントキーは JSON フォーマットのテキストファイル(もしくは P12 形式のファイル)であり、流出の危険があります。そのため一般的には サービスアカウントキーの使用は非推奨 であり、代わりにプログラム動作基盤(Compute Engine VM や Cloud Run Service)にサービスアカウントをアタッチしたり、動作基盤が Google Cloud 以外なのであれば Workload Identity や Workforce Identity の使用が推奨されます。 2024年初頭以降に作成された Google Cloud 組織で iam.disableServiceAccountKeyCreation がデフォルトで有効化されているのは、リスクの高い手法であるサービスアカウントキーの使用を抑止するためです。 このエラーを解消する最も簡単な方法は、 iam.disableServiceAccountKeyCreation 制約を無効化することですが、無効化を行う前に、 本当にサービスアカウントキーを生成する必要があるのか 、代わりにサービスアカウントのアタッチや Workload Identity で 代用できないか を、十分ご検討ください。 なお Workload Identity とは、OIDC や SAML 2.0 を利用して、Amazon Web Services(AWS)や Microsoft Azure、Active Directory Federation Service 等の ID を使って Google Cloud に認証する仕組みです。 参考 : サービス アカウント キーから移行する 参考 : Workload Identity 連携 参考 : Workforce Identity の連携 対処方法 組織ポリシーの制約 iam.disableServiceAccountKeyCreation を無効化することで、サービスアカウントキーが作成できるようになります。 組織ポリシーの制約は、組織レベル、フォルダレベル、プロジェクトレベルで適用することができ、親リソースのポリシーは子リソースに 継承 されます。ただし、明示的に設定することで、子リソース側で親リソースの制約をオーバーライド(上書き)することも可能です。 よって、取り得る選択肢としては、以下のいずれかになります。 iam.disableServiceAccountKeyCreation を組織レベルで無効化する iam.disableServiceAccountKeyCreation をフォルダレベルでオーバーライドして無効化する iam.disableServiceAccountKeyCreation をプロジェクトレベルでオーバーライドして無効化する 上記のうち 1. 、 2. の場合、組織全体もしくはフォルダ全体で制約が無効になり、影響は他のプロジェクトにも及びます。 3. の影響範囲は当該プロジェクトのみです。 これ以降、当記事では当該制約を無効化する手順を解説しますが、前掲の「対処する前に」をお読みいただき、 リスクを理解したうえで実施 してください。 対処手順 IAM 権限の確認 当手順を実施するには、操作する Google アカウント、もしくはアカウントが所属するグループが、 組織レベル で 組織ポリシー管理者 ( roles/orgpolicy.policyAdmin )ロールを持っている必要があります。 組織ポリシー管理者を付与できる最も下位レベルのリソースは「組織」です。よって、フォルダやプロジェクトレベルで制約をオーバーライドする場合でも、組織レベルで組織ポリシー管理者ロールを持っている必要があります。 作業者の Google アカウントが必要な権限を持っていない場合は、組織レベルで IAM ロール「組織ポリシー管理者」を付与してください。 参考 : IAM を使用した組織リソースのアクセス制御 組織、フォルダまたはプロジェクトを選択 Google Cloud コンソールにログインし、プロジェクトセレクターをクリックして、制約を無効化を適用する組織、フォルダ、またはプロジェクトを選択します。 当記事の「対処する前に」「対処方法」をよくお読みになり、制約の編集位置を決めたうえで選択してください。 組織のポリシー画面へ遷移 コンソール上部の検索ボックスに「組織のポリシー」を入力し、サジェストされた「組織のポリシー」を選択します。 または、「IAM と管理」画面から直接遷移しても構いません。 制約の編集画面へ遷移 制約一覧の上部のフィルタに constraints/iam.disableServiceAccountKeyCreation を入力します。 フィルタ結果の中から、Disable service account key creation をクリックして、編集画面へ遷移します。 制約を編集 ボタン「ポリシーを管理」を押下します。 「ポリシーのソース」ブロックで、「親のポリシーをオーバーライドする」を選択します。「ルール」ブロックが表示されるので、「ルールの追加」を押下し、「適用」を「オフ」にします。最後に、ボタン「ポリシーを設定」を押下します。 結果の確認 設定が完了すると、以下のような表示になります。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
Google Cloud の組織ポリシー機能で、プロジェクトにタグを適用することで例外設定を行う方法を検証しました。 はじめに 当記事について 前提知識 検証 タグキーと値の作成 フォルダとプロジェクトの作成 ポリシーの適用 補足情報 条件の指定について Terraform はじめに 当記事について Google Cloud(旧称 GCP)では 組織のポリシー を使って、組織内のプロジェクトに一律でセキュリティや統制強化のための設定を適用できます。このとき、特定のプロジェクトにのみ 例外 を設けたい場合もあります。 例えば、組織全体に「公開アクセスの防止を適用する( constraints/storage.publicAccessPrevention )」という組織のポリシーの制約を適用しているとします。しかし、あるプロジェクトでは Cloud Storage の公開バケットに静的コンテンツをホスティングしているとします。このときは、当該プロジェクトのみを制約の適用外にする必要があります。 当記事ではそのような場合を想定して、タグを使って特定のプロジェクトは制約の対象外としつつ、その他全ての組織内プロジェクトに制約を適用する検証を行いました。 参考 : タグを使用した組織のポリシーの設定 前提知識 当記事では Resource Manager の タグ 機能を利用します。タグの詳細や、類似機能であるラベルとの差異については、以下の記事を参照してください。 blog.g-gen.co.jp 組織のポリシー機能については、以下の記事を参照してください。特に、継承と呼ばれる性質については理解が必要です。 blog.g-gen.co.jp 検証 タグキーと値の作成 まず、組織のリソースとして、「IAM と管理」画面から タグキー を作成します。今回は apply-policy という名称で作成します。 タグキー作成 次に、タグキーの値として、 true と false を作成します。今回の検証ではこの値を使っていますが、必ずしもブール値である必要はなく、運用上都合の良い文字列を指定して構いません。 タグの値作成 フォルダとプロジェクトの作成 以下のようにフォルダとプロジェクトを作成します。 フォルダに属さないプロジェクト test-20241028-true test-20241028-false test-20241028-none フォルダ my_folder 同フォルダ配下の、以下のプロジェクト test-20241028-true-in-folder test-20241028-false-in-folder test-20241028-none-in-folder タグ apply-policy: true を、プロジェクト test-20241028-true と test-20241028-true-in-folder に付与します。 タグ apply-policy: false を、プロジェクト test-20241028-false と test-20241028-false-in-folder に付与します。 さらに、タグ apply-policy: false を、フォルダ my_folder に付与します。 プロジェクトの作成・タグの付与 フォルダにタグを付与した場合、そのフォルダに属するプロジェクトには タグが継承 されます。ただし、プロジェクトにタグが直接付与されている場合、そちらが 優先 されます。上記のスクリーンショットでは、プロジェクト test-20241028-none-in-folder の Tags 列に継承を表すアイコンが表示されており、フォルダから継承されたタグが適用されていることがわかります。 ポリシーの適用 この組織に対して、組織レベルで制約を適用します。今回適用する制約は「公開アクセスの防止を適用する( constraints/storage.publicAccessPrevention )」です。組織レベルにこの制約を適用しつつ、タグ apply-policy: false が付与されたプロジェクトではこの制約の効果が発揮されない状態を目指します。 組織のポリシーの Google Cloud コンソール画面で上記の制約を検索し、編集します。この時、設定値として、「親のポリシーをオーバーライドする」を選択します。編集画面では、2つのルールを追加します。 1つ目は、特定のタグキーと値を持っていることを条件に、「適用」を「オフ」とするものです。設定値「値のパス」は、 [organization_name]/apply-policy/false とします。タグを作成済みの場合、プルダウンメニューから選択できます。 ポリシーの編集 値のパス 2つ目は、1つ目のルールの条件に合致しない場合に適用される、デフォルトのルールです。「ルールの追加」を押下し、「適用」のラジオボタンで「オン」を選択します。これを設定しない場合、 A boolean policy must always include one unconditional rule. というメッセージが表示され、ポリシーの設定が完了しません。 組織レベルでポリシーが適用された場合、コンソール画面の表記は以下のようになります。 適用された組織ポリシー ポリシーの適用後、各プロジェクトのポリシー適用状態は以下のようになります。 プロジェクト ○:適用済み、×:未適用 test-20241028-true ○ test-20241028-false × test-20241028-none ○ test-20241028-true-in-folder ○ test-20241028-false-in-folder × test-20241028-none-in-folder × 以下は、各プロジェクトレベルでポリシーの適用状態を確認した際のキャプチャです。想定したとおりに、ポリシーが適用されていることがわかります。 test-20241028-true test-20241028-false test-20241028-none test-20241028-true-in-folder test-20241028-false-in-folder test-20241028-none-in-folder 以上の検証結果から、以下のことが確かめられました。 明示的にタグを付与したプロジェクトには、ポリシーが適用されない 親フォルダのタグを継承しているプロジェクトには、ポリシーが適用されない タグが付与されていないプロジェクトには、デフォルトルールに基づきポリシーが適用される 補足情報 条件の指定について 今回の検証では、ポリシーの除外対象とするプロジェクトをタグで指定しました。反対に、適用対象をタグで指定することも可能です。 また、今回の検証ではタグ名を使って指定する方法を紹介しましたが、他にタグの永続 ID(permanent ID。タグキーや値が持つリソース ID)で指定することもできます。 参考 : 永続 ID を使用する条件 Terraform 当検証で使用した組織レベルのリソースは、以下のように Terraform で記述することができます。なお以下のサンプルコード内の変数 organization_number_id は10~13桁の数字で表される組織 ID です。 resource "google_tags_tag_key" "apply_policy" { short_name = "apply-policy" parent = "organizations/$ { var.organization_number_id } " description = "The flag indicating whether to apply the policy or not." } resource "google_tags_tag_value" "apply_policy_true" { parent = "tagKeys/$ { google_tags_tag_key.apply_policy.name } " short_name = "true" description = "Apply organization policy to the project." } resource "google_tags_tag_value" "apply_policy_false" { parent = "tagKeys/$ { google_tags_tag_key.apply_policy.name } " short_name = "false" description = "Do NOT apply organization policy to the project." } resource "google_org_policy_policy" "storage_public_access_prevention" { name = "organizations/$ { var.organization_number_id } /storage.publicAccessPrevention" parent = "organizations/$ { var.organization_number_id } " spec { rules { enforce = "FALSE" condition { title = "Do NOT apply with the tag" description = "Do NOT apply storage.publicAccessPrevention on the projects tagged with apply-policy: false" expression = "resource.matchTag('$ { var.organization_number_id } /apply-policy', 'false')" } } rules { enforce = "TRUE" } } } G-genメンバー (記事一覧) クラウドソリューション部
G-gen の杉村です。Pub/Sub の Cloud Storage インポートトピック (Cloud Storage import topic)を使うと、事前に指定した Cloud Storage バケットに Put されたテキストオブジェクトを、ノーコードで Pub/Sub トピックにパブリッシュし、簡単に Pub/Sub サブスクリプションに配信できます。 前提知識 Cloud Storage インポートトピックとは 設定値 検証の概要 環境構築 サービスエージェントへ IAM 権限の付与(パブリッシュ) バケットの作成 トピックの作成 サービスエージェントへ IAM 権限の付与(バケット読み取り) BigQuery テーブルと BigQuery サブスクリプションの作成 トピックの状態を確認 動作確認 オブジェクトの Put テーブルの確認 追加検証(区切り文字の追加) 追加検証(メッセージ本文の確認) 前提知識 Cloud Storage インポートトピックとは Pub/Sub は、Google Cloud(旧称 GCP)のフルマネージドなメッセージングサービスです。Pub/Sub の意義や、実現できるアーキテクチャは以下の記事もご参照ください。 blog.g-gen.co.jp Pub/Sub の Cloud Storage インポートトピック (Cloud Storage import topic)を使うと、事前に指定した Cloud Storage バケットに Put されたテキストオブジェクトを、ノーコードで Pub/Sub トピックにパブリッシュし、簡単に Pub/Sub サブスクリプションに配信できます。 参考 : Create a Cloud Storage import topic この Cloud Storage インポートトピックを 使わない場合 は、Cloud Storage にオブジェクトが Put されたことを Eventarc で検知し、Cloud Run functions 等を起動、記述したプログラムで配信先に書き込むという処理の開発が必要になります。 一方で、当記事で紹介する Cloud Storage インポートトピックを使えば、Cloud Storage バケットに書き込まれたオブジェクトを読み取る プログラムを開発することなく 、自動的に Pub/Sub トピックにパブリッシュすることができます。 そのようにして Pub/Sub トピックにパブリッシュされたメッセージは、サブスクリプションを経由して、BigQuery に書き込んだり、他の API エンドポイントに Push することなどが可能です。 通常のトピックと Cloud Storage インポートトピックの違い 設定値 Cloud Storage インポートトピックには、以下のような設定値があります。 設定名 説明 取り込み元バケット データを取り込む Cloud Storage バケットを指定 オブジェクトの形式 Text、Avro、Pub/Sub Avro から選択 区切り文字 Text の場合のみ指定。この区切り文字に基づいてメッセージが分割される。1文字まで。省略すると \n (改行) 最短のオブジェクト作成時間 取り込み開始時刻。この時刻より前に作成されたオブジェクトは取り込まれない glob パターン ここで指定したパスパターンに一致するオブジェクトのみが取り込まれる。 ** で全オブジェクト。 **.txt で拡張子指定等 以下は、Google Cloud コンソールにおける設定画面のスクリーンショットです。 トピック作成画面 検証の概要 当記事では、以下の構成で検証を行います。 検証 1 上記の構成では、Cloud Storage インポートトピックによる自動的なメッセージ取り込みを行います。取り込んだメッセージは、BigQuery サブスクリプション経由で BigQuery テーブルに書き込みます。これによりソースコードを一切書くことなく、Cloud Storage に到着したテキストデータを順次、BigQuery に書き込むことが可能です。 Pub/Sub の BigQuery サブスクリプションの詳細は、以下の記事を参照してください。 blog.g-gen.co.jp さらに、追加検証として、メッセージ本文の構成を確かめるため、Pull サブスクリプションを作成して直接メッセージの内容を確認する検証も行います。 検証 2 環境構築 サービスエージェントへ IAM 権限の付与(パブリッシュ) Pub/Sub が利用するサービスエージェント(Google Cloud サービスが利用するサービスアカウントのこと)に、必要な IAM 権限を付与します。 サービスエージェント名は service-{PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com です。 {PROJECT_NUMBER} にはプロジェクト番号が入ります。このサービスエージェントに、プロジェクトレベルで Pub/Sub パブリッシャー( roles/pubsub.publisher )ロールを付与します。このロールは、サービスエージェントが Cloud Storage インポートトピックにデータをパブリッシュするのに必要になります。 参考 : Add the Pub/Sub publisher role to the Pub/Sub service account 参考 : サービスエージェントとは何か ‐ G-gen Tech Blog プロジェクトレベルの IAM バインディング一覧画面では、サービスエージェントに付与された IAM ロールは非表示になっています。以下のスクリーンショットのように「Google 提供のロール付与を含める」チェックボックスをオンにすると、IAM ロールが表示されます。 なお以下のスクリーンショットでは、デフォルトで付与されている「Cloud Pub/Sub サービス エージェント」、今回付与した「Pub/Sub パブリッシャー」に加えて「BigQuery データ編集者」が付与されていますが、これは今回の検証で BigQuery サブスクリプションを利用するためです。 プロジェクトの IAM 画面 バケットの作成 Cloud Storage バケットを作成します。今回は、以下のようなバケットを作成しました。 Cloud Storage バケットの作成 トピックの作成 Cloud Storage インポートトピックを作成します。 オブジェクトの形式は Text とし、区切り文字は空白とします。区切り文字を明示しない場合、 \n (改行)が区切り文字として認識されます。つまり、あるテキストファイルをバケットに Put した場合、 1行が1メッセージ として Pub/Sub トピックに取り込まれます。 Cloud Storage インポートトピックの作成 サービスエージェントへ IAM 権限の付与(バケット読み取り) このとき、Pub/Sub のサービスエージェント( service-{PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com )が対象バケットへの読み取り権限を持っていない場合、以下のようなメッセージが表示されます。 権限の確認と付与 「権限の設定」を押下すると、以下のような画面が表示され、「ロールの付与」をクリックすることで必要な IAM 権限を付与できます。 IAM 権限の付与 これを行うと、対象バケットのレベルで「Storage オブジェクト閲覧者」「Storage レガシー バケット読み取り」ロールが付与されます。以下のように、バケットの「権限」タブで付与された IAM ロールを確認できます。 バケットレベルの権限の確認 BigQuery テーブルと BigQuery サブスクリプションの作成 データ格納先の BigQuery テーブルを作成します。id 列と data 列を持つシンプルなテーブルを作成します。 BigQuery テーブル その後、Cloud Storage インポートトピックに紐づくサブスクリプションを「BigQuery サブスクリプション」タイプで作成します。 BigQuery サブスクリプションの作成 BigQuery サブスクリプションは、トピックが受け取ったメッセージを自動的に BigQuery に書き込んでくれます。今回は「スキーマを使用しない」に設定したので、メッセージは data という名称を持つ列に文字列として書き込まれます。 トピックの状態を確認 ここで、トピックの状態を確認します。作成した Cloud Storage インポートトピックの詳細画面で、トピックのステータスが以下のように緑色で表示されていれば、正しく設定が完了しています。 トピックが正しく設定されている状態 もし以下のように表示されていた場合、Pub/Sub のサービスエージェントがトピックへのパブリッシュ権限を持っていないことが考えられます。当記事の サービスエージェントへ IAM 権限の付与(パブリッシュ) に戻り、プロジェクトレベルで Pub/Sub パブリッシャー( roles/pubsub.publisher )ロールを付与してください。あるいは、トピックレベルでロールを付与することも可能です。 取り込みのリソースエラー: 権限の公開が拒否されました 取り込みのリソースエラー: 権限の公開が拒否されました 動作確認 オブジェクトの Put 動作確認のため、Cloud Storage バケットにオブジェクトを Put します。 今回は、以下のようなファイルを Put しました。改行で区切られた、2行のテキスト情報です。 test-file-01.txt This is the first message. This is the second message. オブジェクトの Put テーブルの確認 Put されたテキストファイル内のテキスト情報は、Cloud Storage インポートトピックに自動的に取り込まれ、BigQuery サブスクリプションによって、対象テーブルの data 列に書き込まれるはずです。 対象テーブルに SELECT 文を実行すると、想定どおり、2行のテキスト情報が書き込まれていました。オブジェクトの Put からテーブルに情報が書き込まれるまで、およそ1分程度のラグがありました。 BigQuery にテキストデータが格納された 追加検証(区切り文字の追加) 追加検証として、先程は省略したトピックの「区切り文字」の設定を追加してみます。 以下のように、区切り文字を , (カンマ)として設定します。 区切り文字をカンマに設定 その後、Cloud Storage バケットに次のファイルを Put します。半角スペースの扱いを確かめるため、3列目の文字列 3rd field の手前には、あえて半角スペースを入れています。 test-csv-02.csv 1st field,2nd field, 3rd field 結果は、以下のように想定どおり格納されました。カンマのあとの半角スペースも反映されており、 3rd field の手前には半角スペースが入っています。 カンマ区切りのデータが格納された 追加検証(メッセージ本文の確認) さらに追加検証として、Pull サブスクリプションを作成して、Cloud Storage インポートトピックから発行されるメッセージの本文を確認します。 以下のように、Pull サブスクリプションを作成します。 Pull サブスクリプションを作成 以下のファイルを Put します。 another 1st field,another 2nd field, another 3rd field その後、 gcloud pubsub subscriptions pull コマンドでサブスクリプションからメッセージを Pull します。 --limit オプションをつけずに実行すると、1個のメッセージのみが Pull されます。今回は --limit=10 を指定し、3つのメッセージが取得できることを確認します。 3つのメッセージが Pull できた 想定どおり、3つのメッセージが Pull できました。データ本文( data )は Base64 でエンコードされているため、 echo "${data}" | base64 --decode のようにしてデコードすると、内容が確認できます。 メッセージ本文は、以下のような形式であることがわかりました。 [ { "ackId": "UAYWLF1GSFE3GQhoUQ5PXiM_NSAoRRsGCBQFfH1wU1x1XVx0aFENGXJ9YHxpW0VQAEVWe1lRGwdoTm11H7aF5ftLQ1RrWBYHBEBae19TGQhoXFp3D3nlneOW2-TYfQk9OqLbgtZtO-vw5OtHZiM9XxJLLD5-MSpFQV5AEkw6H0RJUytDCypYEU4EISE-MD5FU0Q", "message": { "data": "YW5vdGhlciAxc3QgZmllbGQ=", "messageId": "12566595082500628", "publishTime": "2024-11-12T09:08:52.185Z" } }, { "ackId": "UAYWLF1GSFE3GQhoUQ5PXiM_NSAoRRsGCBQFfH1wU1x1XVx0aFENGXJ9YHxpW0VQAEVWe1lRGgdoTm11H7aF5ftLQ1RrWBYHBEBae19TGQhoXFp3DnnlneOW2-TYfQk9OqLbgtZtO-vw5OtHZiM9XxJLLD5-MSpFQV5AEkw6H0RJUytDCypYEU4EISE-MD5FU0Q", "message": { "data": "YW5vdGhlciAybmQgZmllbGQ=", "messageId": "12566595082500629", "publishTime": "2024-11-12T09:08:52.185Z" } }, { "ackId": "UAYWLF1GSFE3GQhoUQ5PXiM_NSAoRRsGCBQFfH1wU1x1XVx0aFENGXJ9YHxpW0VQAEVWe1lRGQdoTm11H7aF5ftLQ1RrWBYHBEBae19TGQhoXFp2B3nlneOW2-TYfQk9OqLbgtZtO-vw5OtHZiM9XxJLLD5-MSpFQV5AEkw6H0RJUytDCypYEU4EISE-MD5FU0Q", "message": { "data": "IGFub3RoZXIgM3JkIGZpZWxk", "messageId": "12566595082500630", "publishTime": "2024-11-12T09:08:52.185Z" } } ] 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
G-gen の佐々木です。当記事では、GitHub Actions で GKE クラスタにリソースをデプロイする際に、 DNS エンドポイント を使用する方法を解説します。 DNS エンドポイントとは GitHub Actions を使用した GKE へのデプロイ 従来の方法 DNS エンドポイントを使用する方法(当記事で解説) DNS エンドポイントを使用する GKE クラスタの作成 シェル変数の設定 ネットワークリソースの作成 GKE クラスタの作成 Direct Workload Identity の構成 シェル変数の設定 Workload Identity の設定 GitHub Actions によるリソースのデプロイ GitHub リポジトリのディレクトリ構成 使用するファイル GitHub Actions ワークフロー Kubernetes マニフェストファイル GitHub Actions の実行 修正したマニフェストファイルの反映 マニフェストファイルの修正 GitHub Actions の再実行 DNS エンドポイントとは DNS エンドポイント とは、GKE のコントロールプレーンにアクセスする方法として従来からサポートされていた IP アドレスベースのアクセス元制御ではなく、DNS ベースのアクセス元制御を提供する機能です。 DNS エンドポイントが有効化されている場合、たとえコントロールプレーンにパブリック IP アドレスが割り当てられていなくても、IAM で許可されているユーザーはコントロールプレーンに接続することができます。 DNSエンドポイントを使用してコントロールプレーンに接続する DNS エンドポイントの詳細、および IP アドレスベースのエンドポイントとの比較については、以下の記事をご一読ください。 blog.g-gen.co.jp GitHub Actions を使用した GKE へのデプロイ 従来の方法 DNS エンドポイントがリリースされる前は、コントロールプレーンのパブリックエンドポイントを無効化している場合、GitHub Actions からコントロールプレーンに直接接続することはできませんでした。 そのため、プライベートエンドポイント経由でコントロールプレーンに到達できる GKE ノード側の VPC 内に GitHub Actions の Self-Hosted Runner を構成し、Runner 上でワークフローを実行する方法などが用いられます。 Self-Hosted Runner は Compute Engine 仮想マシンや GKE クラスタ内の Pod で実行することになるため、GitHub Actions でデプロイパイプラインを構築したい場合、これらのリソースの運用管理も考慮していく必要があります。 Self-Hosted Runnerを使用してプライベートエンドポイント経由でデプロイする DNS エンドポイントを使用する方法(当記事で解説) DNS エンドポイントを使用する場合、GitHub Actions ワークフローから Google Cloud API にアクセスすることができれば、パブリック IP アドレスを持たないコントロールプレーンであっても直接接続することができます。 したがって、Self-Hosted Runner の運用を考えることなく、シンプルなパイプライン構成で GKE クラスタにリソースをデプロイできます。 DNSエンドポイント経由でデプロイする GitHub Actions からコントロールプレーンに接続する際の IAM 認証については Workload Identity を使用します。 Workload Identity では Google Cloud プロジェクトに GitHub リポジトリを プロバイダ として登録し、プロバイダに対して IAM 権限を紐付けることで Google Cloud リソースへのアクセス権を与えることができます。 Workload Identity 連携の詳細については、以下のドキュメントをご一読ください。 参考1 : Workload Identity Federation 参考2 : Configure Workload Identity Federation with deployment pipelines 当記事では Workload Identity の方式のひとつである Direct Workload Identity を使用することで、IAM サービスアカウントの情報を GitHub Actions ワークフロー側に渡すことなく、プロバイダに対してコントロールプレーンに接続するための IAM 権限を直接付与します。 DNS エンドポイントを使用する GKE クラスタの作成 シェル変数の設定 まずは GitHub Actions のデプロイ先となる GKE クラスタを作成します。 当記事では gcloud CLI を使用してリソースを作成していきます。リソース作成時に何度か使用する値をシェル変数として設定しておきます。 PROJECT_ID = { プロジェクトID } # ex: gha-demo-prj LOCATION = { GKEを作成するロケーション } # ex: asia-northeast1 CLUSTER_NAME = { 任意のGKEクラスタの名前 } # ex: mycluster ネットワークリソースの作成 GKE クラスタを配置する VPC とサブネットを作成していきます。 以下のコマンドで VPC を作成します。 # VPC を作成する $ gcloud compute networks create vpc- ${CLUSTER_NAME} \ --project = ${PROJECT_ID} \ --subnet-mode = custom 作成した VPC の中にサブネットを作成します。当記事ではサブネットの IP 範囲として 192.168.11.0/24 を設定しています。 # サブネットを作成する $ gcloud compute networks subnets create subnet- ${CLUSTER_NAME} \ --project = ${PROJECT_ID} \ --network = vpc- ${CLUSTER_NAME} \ --region = ${LOCATION} \ --range = 192 . 168 . 11 . 0 / 24 GKE クラスタの作成 --enable-dns-access フラグを使用し、DNS エンドポイントを有効化した GKE クラスタを作成します。 以下のコマンドで、DNS エンドポイントを有効化した Autopilot モードの GKE クラスタを作成します。 --no-enable-ip-access フラグを使用することで IP アドレスベースの接続を無効化します。これで、Google Cloud API にアクセスでき、かつ IAM 権限を持つユーザーのみがコントロールプレーンに接続することができます。 # DNS エンドポイントを有効化した Autopilot クラスタを作成する(10分ほどかかるので注意) $ gcloud container clusters create-auto ${CLUSTER_NAME} \ --project = ${PROJECT_ID} \ --network = vpc- ${CLUSTER_NAME} \ --subnetwork = subnet- ${CLUSTER_NAME} \ --enable-private-nodes \ --enable-dns-access \ --no-enable-ip-access IPアドレスベースの接続を無効化し、DNSエンドポイントのみ有効にする Direct Workload Identity の構成 シェル変数の設定 ここからは、GitHub Actions からコントロールプレーンに接続する際に認証を行うための Workload Identity の設定を行っていきます。 まずは、繰り返し使用する値をシェル変数として設定しておきます。 PROJECT_ID = { プロジェクトID } # ex: gha-demo-prj PROJECT_NUMBER = { プロジェクト番号 } # ex: 1234567890 WORKLOAD_IDENTITY_POOL = { 任意のWorkload Identityプール名 } # ex: my-gha-pool WORKLOAD_IDENTITY_PROVIDER = { 任意のWorkload Identityプロバイダ名 } # ex: my-gha-provider GITHUB_REPO = { GitHubリポジトリ名 } # ex: my-org/my-repo Workload Identity の設定 以下のコマンドで Workload Identity プールを作成します。 # Workload Identity プールの作成 $ gcloud iam workload-identity-pools create ${WORKLOAD_IDENTITY_POOL} \ --project = ${PROJECT_ID} \ --location = global \ --display-name = ${WORKLOAD_IDENTITY_POOL} 作成したプールに、Workload Identity プロバイダを作成します。 --attribute-condition フラグで、この Workload Identity を使用する GitHub Actions ワークフローがある GitHub リポジトリをここで指定します。 # Workload Identity プロバイダの作成 $ gcloud iam workload-identity-pools providers create-oidc ${WORKLOAD_IDENTITY_PROVIDER} \ --project = ${PROJECT_ID} \ --location = global \ --workload-identity-pool = ${WORKLOAD_IDENTITY_POOL} \ --display-name = ${WORKLOAD_IDENTITY_PROVIDER} \ --issuer-uri =" https://token.actions.githubusercontent.com " \ --attribute-mapping =" google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository " \ --attribute-condition =" assertion.repository==' ${GITHUB_REPO} ' " Workload Identityプールとプロバイダを作成する 最後に、Workload Identity プロバイダに対して GKE クラスタにリソースをデプロイするための権限( roles/container.developer )を付与します。 これにより、GitHub Actions ワークフローが DNS エンドポイント(Google Cloud API の認証)を通して GKE のコントロールプレーンに接続することができます。 # Workload Identity プロバイダに権限を付与する $ gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member =" principalSet://iam.googleapis.com/projects/ ${PROJECT_NUMBER} /locations/global/workloadIdentityPools/ ${WORKLOAD_IDENTITY_POOL} /attribute.repository/ ${GITHUB_REPO} " \ --role =" roles/container.developer " Workload IdentityプロバイダにGKEクラスタのアクセス権を付与する GitHub Actions によるリソースのデプロイ GitHub リポジトリのディレクトリ構成 Workload Identity プロバイダの作成時に指定した GitHub リポジトリに、GitHub Actions のワークフローと、GKE クラスタにデプロイする Kubernetes リソースのマニフェストファイルを配置します。 リポジトリ内のディレクトリ構成は以下のようにして進めていきます。 . ├── .github │ └── workflows │ └── demo-actions.yaml # GitHub Actions ワークフロー └── manifests └── sample-deployment.yaml # Kubernetes リソース(deployment)のマニフェストファイル 使用するファイル GitHub Actions ワークフロー Workload Identity を使用して GKE クラスタにリソースをデプロイする GitHub Actions ワークフローを作成します。 ワークフローファイル内の以下の箇所を、実際の値に置き換えてください。 行 置き換える箇所 値 14行目 {プロジェクトID} GKE クラスタを作成したプロジェクトの ID 15行目 {プロジェクト番号} GKE クラスタを作成したプロジェクトのプロジェクト番号 16行目 {GKEクラスタ名} GKE クラスタの名前 17行目 {GKEクラスタのロケーション} GKE クラスタを作成したロケーション 18行目 {Workload Identityプール名} Workload Idenity プールの名前 19行目 {Workload Identityプロバイダ名} Workload Identity プロバイダの名前 # demo-actions.yaml name : apply k8s manifest to GKE # main ブランチへの Pull Request / Push で実行 on : pull_request : branches : - main push : branches : - main # 環境変数に GKE クラスタの情報を設定 env : PROJECT_ID : { プロジェクトID } # Google Cloud プロジェクト ID PROJECT_NUM : { プロジェクト番号 } # Google Cloud プロジェクト番号 GKE_CLUSTER : { GKEクラスタ名 } # GKE クラスタ名 GKE_LOCATION : { GKEクラスタのロケーション } # GKE クラスタのロケーション WID_POOL : { Workload Identityプール名 } # Workload Identity プール名 WID_PROVIDER : { Workload Identityプロバイダ名 } # Workload Identity プロバイダ名 # ジョブ (GitHUb runners で実行) jobs : setup-apply : runs-on : ubuntu-latest permissions : id-token : write contents : read pull-requests : write steps : - id : checkout name : Checkout uses : actions/checkout@v4 # Workload Identity 連携 # https://cloud.google.com/iam/docs/using-workload-identity-federation#generate-automatic - id : auth name : Authenticate to Google Cloud uses : google-github-actions/auth@v2 with : workload_identity_provider : 'projects/${{ env.PROJECT_NUM }}/locations/global/workloadIdentityPools/${{ env.WID_POOL }}/providers/${{ env.WID_PROVIDER }}' # Workload Identity Pool のプロバイダー # https://github.com/google-github-actions/setup-gcloud - id : setup-gcloud name : Setup gcloud uses : google-github-actions/setup-gcloud@v2 with : project_id : ${{ env.PROJECT_ID }} # GKE の認証プラグイン # https://cloud.google.com/kubernetes-engine/docs/deprecations/auth-plugin?hl=ja - id : install-gke-gcloud-auth-plugin name : Install gke-gcloud-auth-plugin run : | gcloud components install gke-gcloud-auth-plugin # GKE クラスタの認証情報を取得 (DNS エンドポイントを使用) - id : get-gke-credentials name : Get GKE credentials run : | gcloud container clusters get-credentials ${{ env.GKE_CLUSTER }} --region ${{ env.GKE_LOCATION }} --dns-endpoint # マニフェストファイルの適用 - id : apply name : Apply run : | kubectl apply -f ./manifests Kubernetes マニフェストファイル GKE クラスタにデプロイするリソースのマニフェストファイルを作成します。 当記事では、nginx コンテナを実行する Pod を3つ作成する Deployment リソースを作成します。 # sample-deployment.yaml apiVersion : apps/v1 kind : Deployment metadata : name : sample-deployment spec : replicas : 3 selector : matchLabels : app : sample-app template : metadata : labels : app : sample-app spec : containers : - name : sample-app image : nginx:1.27 GitHub Actions の実行 作成したワークフローファイルとマニフェストファイルをリモートリポジトリに push し、GitHub Actions ワークフローを実行します。 ワークフローが実行され、GitHub Actions から GKE クラスタに対してマニフェストファイルが適用されます。Deployment リソースが作成されていることが GitHub Actions のログからわかります。 GitHub Actionsワークフローから GKE クラスタへのデプロイが成功している ローカルから GKE クラスタのコントロールプレーンに接続し、デプロイしたリソースの状態を確認します。 # DNS エンドポイントでコントロールプレーンに接続する $ gcloud container clusters get-credentials ${CLUSTER_NAME} --dns-endpoint # Deployment リソースを表示 $ kubectl get deployments マニフェストファイルの記述通り、3つの Pod を実行する Deployment リソースが作成されています。 # Deployment リソースを表示(出力例) $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE sample-deployment 3 / 3 3 3 2m7s 修正したマニフェストファイルの反映 マニフェストファイルの修正 マニフェストファイルを修正してからリモートリポジトリに push し、GitHub Actions ワークフローによるデプロイを再度行います。 マニフェストファイル内の spec.replicas の値を 1 にして、Pod 数をスケールインするように修正します。 # sample-deployment.yaml apiVersion : apps/v1 kind : Deployment metadata : name : sample-deployment spec : replicas : 1 # ここを修正する selector : matchLabels : app : sample-app template : metadata : labels : app : sample-app spec : containers : - name : sample-app image : nginx:1.27 GitHub Actions の再実行 再度、リモートリポジトリへの push を行い、GitHub Actions ワークフローを実行します。 ワークフローの実行後、Deployment の状態を確認します。push したマニフェストファイルが GitHub Actions から適用され、Pod の数が1になりました。 # Deployment リソースを確認(出力例) $ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE sample-deployment 1 / 1 1 1 7m5s 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
G-gen の佐々木です。当記事では、GKE のコントロールプレーンにアクセスするための新しい方法として、 DNS ベースのエンドポイント (DNS エンドポイント)を紹介します。 はじめに GKE におけるコントロールプレーンへのアクセス方法 従来の方法 パブリックエンドポイント プライベートエンドポイント DNS エンドポイント IP ベースと DNS ベースの比較 使用方法 DNS エンドポイントの有効化 コントロールプレーンへのアクセス IP アドレスを使用した接続の無効化 はじめに Google Cloud(旧称 GCP)のフルマネージドなコンテナオーケストレーションサービスである Google Kubernetes Engine(GKE) では、Kubernetes クラスタのコントロールプレーンへのアクセス方法を複数種類から選択できます。 従来から存在する IP ベースのエンドポイント(IP-based endpoints)方式(パブリッククラスタとプライベートクラスタ)に加えて、2024年11月11日、 DNS ベースのエンドポイント (DNS-based endpoint)が利用可能になりました。 当記事では、DNS ベースのエンドポイント(DNS エンドポイント) の機能と使い方を紹介します。 参考 : A new flexible DNS-based approach for accessing the GKE control plane (Google Cloud 公式ブログ) 参考 : About network isolation in GKE (Google Cloud 公式ドキュメント) GKE におけるコントロールプレーンへのアクセス方法 従来の方法 パブリックエンドポイント GKE クラスタで パブリックエンドポイント を有効化する場合、コントロールプレーンにはパブリック IP アドレスが割り当てられます。 この状態ではインターネット上のどこからでもコントロールプレーンにアクセスできてしまうため、 承認済みネットワーク を使用してアクセス元 IP アドレスを制限するのが一般的です。 パブリックエンドポイントでコントロールプレーンに接続する プライベートエンドポイント パブリックエンドポイントよりも推奨される方法として、GKE クラスタでパブリックエンドポイントを無効化し、プライベート IP アドレスのみをコントロールプレーンに割り当てることもできます( プライベートエンドポイント )。 プライベートエンドポイントを使用する場合、「"コントロールプレーンがある Google 管理の VPC と接続されている"ユーザー 管理の VPC」を経由してコントロールプレーンにアクセスする必要があります。そのため、GKE クラスタがある VPC の外からコントロールプレーンに接続したい場合、ユーザー VPC に踏み台 VM(Bastion host)を起動するか、VPN を経由してアクセスする必要がありました。 プライベートエンドポイントでコントロールプレーンに接続する DNS エンドポイント GKE クラスタで DNS エンドポイントを有効化すると、Google Cloud API にアクセスできる任意の環境から、パブリック IP アドレスが割り当てられてないコントロールプレーンにアクセスすることができます。 DNSエンドポイントでコントロールプレーンに接続する コントロールプレーンへのアクセス可否はその他の Google Cloud API を利用するときと同様に、IAM ポリシーによって判断されます。GKE クラスタに対して container.clusters.connect ポリシーを持つ以下のようなロールが付与されているプリンシパルであれば、コントロールプレーンにアクセスすることができます。 Kubernetes Engine 開発者(roles/container.developer) Kubernetes Engine 閲覧者(roles/container.viewer) ※読み取り専用 DNS エンドポイントを使用することで、コントロールプレーンへの接続に踏み台 VM を使用する必要がなくなるほか、 推移的なルーティング によってコントロールプレーンに到達できないといった、ネットワーク構成上の制限を考慮しなくてもよくなります。 たとえば、以下のような構成でプライベートエンドポイントを使用する場合、推移的ルーティングによりコントロールプレーンにアクセスすることはできません。 推移的ルーティングによりコントロールプレーンに接続できないケース DNS エンドポイントを使用すると、アクセス元の VPC 内サブネットで 限定公開の Google アクセス が有効化されていれば(Google Cloud API にアクセスできれば)、コントロールプレーンに接続することができます。 DNSエンドポイントでは推移的ルーティングに制限されずに接続できる また、 VPC Service Control を使用することで、IAM 以外の条件(アクセス元が存在するプロジェクトなど)でも DNS エンドポイントに対するアクセス元を制限することができます。 IP ベースと DNS ベースの比較 IP ベースのエンドポイントと、DNS ベースのエンドポイントがどのように異なるのかを比較します。 DNS ベースのエンドポイントでは、Google Cloud API がインターネットに露出しているという点では、IP ベースのエンドポイントのパブリッククラスタと類似しているものの、以下のように異なっています。 比較点 IP ベース(パブリックエンドポイント) DNS ベース Kubernetes API エンドポイントの露出 パブリック IP を持っている(万が一 Kubernetes に脆弱性がある場合は脅威にさらされる) パブリック IP を持たない設定が可能 kubectl の接続情報取得に必要な IAM 権限 container.clusters.get container.clusters.connect VPC Service Controls での保護 なし(クラスタ情報取得に対しては保護可能) 可能 特にセキュリティ面に関しては、従来の IP ベースのアクセスであれば、プライベートクラスタを構成したうえで踏み台ホストや VPN を用いて Kubernetes API へ接続することが推奨されていましたが、今後は DNS ベースのエンドポイントを構成し、IAM 権限を適切に管理したうえで VPC Service Controls を使って多層防御を構成することが推奨されます。 VPC Service Controls については、以下の記事も参照してください。 blog.g-gen.co.jp 使用方法 DNS エンドポイントの有効化 DNS エンドポイントはいつでも有効化することができます。CLI で有効化する場合は、 --enable-dns-access フラグを使用してクラスタを更新します。 # クラスタ作成時に DNS エンドポイントを有効化する $ gcloud container clusters create ${GKE クラスタ名 } -–enable-dns-access # 既存のクラスタで DNS エンドポイントを有効化する $ gcloud container clusters update ${GKE クラスタ名 } --enable-dns-access Google Cloud コンソールの場合、「DNS エンドポイント」の項目から有効化することができます。 DNSエンドポイントの設定箇所(コンソール) コントロールプレーンへのアクセス DNS エンドポイントでコントロールプレーンに接続するには、 gcloud container clusters get-credentials  コマンドで --dns-endpoint フラグを使用します。 # DNS エンドポイントでコントロールプレーンに接続する $ gcloud container clusters get-credentials ${GKE クラスタ名 } --dns-endpoint IP アドレスを使用した接続の無効化 --no-enable-ip-access フラグを使用することで、従来の IP アドレスベースの接続方法を無効化することができます。 # IP アドレスベースのコントロールプレーンへの接続を無効化する $ gcloud container clusters update ${GKE クラスタ名 } --no-enable-ip-access 佐々木 駿太 (記事一覧) G-gen最北端、北海道在住のクラウドソリューション部エンジニア 2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2025 Fellowに選出。好きなGoogle CloudプロダクトはCloud Run。 趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。 Follow @sasashun0805
G-gen の杉村です。Google Cloud(旧称 GCP)の認定資格である Associate Data Practitioner 資格の試験対策に有用な情報を記載します。 基本的な情報 Associate Data Practitioner とは 難易度 出題傾向 試験対策 ETL と ELT ETL と ELT の基本 オープンソースツールとフルマネージドサービス Cloud Data Fusion イベントドリブンアーキテクチャ データベースの選択 BigQuery BigQuery の基本 ELT と ETL 半構造化データの扱い(JSON 型) パーティションとクラスタリング 外部テーブル SQL ウインドウ関数 コネクテッドシート BigQuery のアクセス制御と暗号化 権限管理(IAM) 承認されたビュー 透過的な暗号化 BigQuery ML BigQuery ML の基本 組み込みモデル スキューとドリフト Gemini AutoML Cloud Storage Cloud Storage の基本 ストレージクラス オブジェクトライフサイクル バージョニング Autoclass デュアルリージョン、マルチリージョン Storage Transfer Service Storage Transfer Appliance Looker Looker の基本 メジャーとディメンション Looker における権限管理 Analytics Hub Colab Enterprise Cloud SQL 基本的な情報 Associate Data Practitioner とは Associate Data Practitioner 試験は、Google Cloud(旧称 GCP)の認定資格の一つです。当試験は2024年10月30日、Beta 版として公開され、2025年1月に一般公開(GA)されました。 当試験は Associate レベルの資格であり、 Google Cloud 上でのデータ取り込み、変換、パイプライン管理、分析、機械学習、および可視化等に関する知識や技能 が問われます。 試験時間は120分、問題数は50〜60問です。 英語版 と 日本語版 の試験が提供されています。 Google Cloud のデータエンジニアリング関連の認定資格としては Professional Data Engineer が存在しています。Professional レベルの認定資格は、技術的な知識だけでなく、ビジネスユースケースにあわせてソリューションを検討するより高度な知見が求められるのに対して、Associate レベルの資格は、技術的な知見のみに特化しています。当記事で紹介する Associate Data Practitioner は、Professional Data Engineer の下位資格の位置づけと考えれます。 参考 : Associate Data Practitioner 当資格の上位資格である Professional レベル試験である Professional Data Engineer については、以下の記事も参照してください。 blog.g-gen.co.jp 難易度 Associate Data Practitioner 試験の難易度は、他の認定試験と比較して 低〜中程度 といえます。 「RDBMS」「トランザクション」「データモデリング」「SQL」「分散データベース」など、基本情報技術者試験でも学ぶような基礎レベルのリレーショナルデータに関する知識に加えて、非正規化を含むデータ分析向けのデータモデリング、オブジェクトストレージの基本などを理解されている方であれば、追加の学習を1ヶ月程度行うことで十分に合格を狙えます。 また基礎知識として、Associate Cloud Engineer 程度の Google Cloud の基礎知識を持っておくことで、IAM による権限管理などを1から学習しなくても済みます。 受験者に推奨される経験として、公式サイトには「Google Cloud 上での6ヶ月以上の実務経験」とありますが、細かな操作やコマンドに関する出題は多くありません。むしろ普遍的な標準 SQL の書き方に親しんでおくことが重要です。また、Google Cloud のベストプラクティスを正しく理解し、それに沿って答えることが重要です。 出題傾向 当試験では、以下のような Google Cloud ソリューションに関する出題がほとんどです。 Cloud Storage BigQuery Pub/Sub Dataflow Dataform Cloud Data Fusion Dataproc また一部では、Cloud SQL の可用性など、オペレーショナルデータベースに関する出題もされます。 試験で出題される Google Cloud で実装されるデータ分析基盤は、 必ず BigQuery が中心である といって差し支えありません。BigQuery をデータウェアハウス(データ分析用データベース)として、そこにデータを取り込み(ingest)するためのツールとして Pub/Sub、Dataflow、Cloud Data Fusion などが登場します。それを理解しつつ、Google が提唱する以下のようなベストプラクティスに沿うような回答を心がけることが重要です。どの認定試験でも共通して言えることですが、原則から大きく外れなければ、合格は難しくありません。 データ分析用データベースは BigQuery 管理工数を小さくするために、可能であればフルマネージドサービスや、備え付けの機能を選ぶ Apache Airflow on Compute Engine より Cloud Composer Spark on GKE より Dataproc Serverless 機械学習フレームワークよりも BigQuery ML 維持・保守工数を小さくするために、可能であればノーコードで実装できる方法を選ぶ BigQuery のハウスキーピングは、Cloud Run functions で実装するよりも、パーティションの有効期間設定を使う Cloud Storage のハウスキーピングは、Cloud Run functions で実装するよりも、オブジェクトライフサイクルを選ぶ 試験対策 以下の勉強方法はあくまで一例であり、最適な方法は、受験者の予備知識や経験によって異なるものとご了承ください。 Associate Cloud Engineer レベルの Google Cloud 基礎知識を習得する 前掲のデータ基礎知識をキーワードベースで理解する 試験ガイドを読み、知らないキーワードや機能について公式ドキュメントで学ぶ 当記事の出題傾向を読み足りない知識領域をカバーする学習を行う ウインドウ関数や BigQuery ML など特殊な SQL の使い方を理解する なお Associate Cloud Engineer 試験の学習については、以下の記事も参照してください。 blog.g-gen.co.jp 当記事ではこれ以降、試験にあたって何を勉強しておくべきか、機能分野ごとに紹介しますので、参考にしてください。当記事では、Google Cloud の基礎知識を詳細にお伝えすることはありませんので、その点にはご留意ください。また、当記事の内容は Beta 版公開時のものですので、現在の試験内容とは一部が異なる場合がある点もご了承ください。 ETL と ELT ETL と ELT の基本 BigQuery はスケーラブルなコンピュート基盤を持っているので、まずデータを BigQuery に取り込んでから変換する「 ELT (Extract、Load、Transform)」の順番で処理することが可能です。ELT は、 スケジュールされたクエリ (Scheduled queries)や Dataform で行うことが多いといえます。 しかし当試験では、BigQuery へ Load する前に Cloud Data Fusion や Dataflow 、 Dataproc でデータクレンジングを行う「ETL(Extract、Transform、Load)」の出題も多くあります。 多くの問題では、ELT と ETL どちらを採るべきか、問題文を読めば明確です。 上記に登場したそれぞれのサービスが、どのようなものであるか、概要を理解しておいてください。 オープンソースツールとフルマネージドサービス Google Cloud のデータ分析向けフルマネージドサービスは、多くがオープンソースソフトウェア(Open Source Software、OSS)をベースとしていることに注意してください。例えば Cloud Composer は、OSS である Apache Airflow のフルマネージドサービスです。Google Cloud プロダクトと、 その元となった OSS の名称との対照は覚えてください 。試験では、例えば「オープンソースの技術スタックを利用したい」などの要件が提示されるかもしれません。 Google Cloud プロダクト名 OSS 名 Cloud Composer Apache Airflow Dataflow Apache Beam Dataproc / Dataproc Serverless Apache Hadoop、Apache Spark 等 Cloud SQL for PostgreSQL PostgreSQL Cloud SQL for MySQL MySQL また、これらの各プロダクトの(= OSS の)得意分野とユースケースをしっかり把握しておきましょう。以下のような大原則をしっかり覚えておいてください。 Google Cloud プロダクト名(OSS 名) ユースケース Cloud Composer(Apache Airflow) DAG (有向非巡回グラフ。いわゆるワークフローまたはジョブネット)を Python で記述。多くの 組み込みオペレーター がある Dataflow(Apache Beam) バッチ処理とストリーミング処理を 両方処理できる Dataproc(Apache Hadoop、Apache Spark 等) ファイルを大規模に並列処理 Cloud SQL(PostgreSQL、MySQL) オペレーショナルなリレーショナルデータベース このようなことから、例えばもし「バッチ処理とストリーミング処理を同じ技術スタックで処理したい」という要件があれば必ず Dataflow を選べますし、「Apache Spark ワークロードをクラウドに移行したい。クラスタの管理は行いたくない」とあれば Dataproc Serverless が選べます。 Cloud Data Fusion Cloud Data Fusion は、ノーコードで開発可能な、フルマネージドの ETL ソリューションです。ノーコードで ETL パイプラインを開発したいシチュエーションで活用できます。以下のような利用が可能です。 Cloud Storage のデータを読み取り、データ変換してから BigQuery に格納 データベースに接続し、データを抽出し、変換してから BigQuery に格納 イベントドリブンアーキテクチャ イベントドリブンアーキテクチャ とは、ある1つの処理が完了したことをきっかけに、別の処理がトリガーされるようなアーキテクチャをいいます。例えば、Cloud Storage バケットにオブジェクトがアップロードされたことをきっかけに Cloud Run functions(旧称 Cloud Functions)が起動し、ファイルを読み込んでデータを BigQuery にロードするようなアーキテクチャです。多くの場合、処理のためのプログラムはサーバーレスプラットフォームにホストされます。 次々に到着する小さいデータを順次処理するような仕組みでは、イベントドリブンなアーキテクチャが適しています。反対に、Cloud Run functions のようなサーバーレスプラットフォームは実行時間に制限があることから、 大きなファイルの処理 (長時間の処理)には適していません。イベントドリブンがどのようなユースケースに適しているかを意識してください。 イベントドリブンについては、以下の記事も参照してください。 blog.g-gen.co.jp blog.g-gen.co.jp データベースの選択 Google Cloud の多用なデータベースサービスを一通り理解し、 ユースケースに対して適切なデータベース を選択できるようにしてください。 以下の記事の「データベースの選択」の項を参照してください。 参考 : Professional Data Engineer試験対策マニュアル。出題傾向・勉強方法 - データベースの選択 - G-gen Tech Blog BigQuery BigQuery の基本 BigQuery は当試験で最も重要なプロダクトです。以下の記事を参照し、機能を理解してください。以下の「基本編」記事の内容が主な出題範囲と概ね重なっていますが、例外として「応用編」で簡単に紹介されている BigQuery ML は頻出範囲です。 blog.g-gen.co.jp ELT と ETL Google Cloud のデータ分析基盤の中心は BigQuery です。前述の通り、ELT と ETL の両方のパターンが出題されます。ELT はスケジュールされたクエリ(Scheduled queries)や Dataform で、ETL は Cloud Data Fusion や Dataflow、Dataproc で実装されます。 Dataform は SQL の拡張言語である SQLX を使い、データの変換(transformation)、 テストや品質チェック まで行えるのが特徴です。 Cloud Data Fusion は ノーコード 、や Dataflow は バッチ処理とストリーミング処理を両方扱えること 、Dataproc は Hadoop / Spark 技術スタック、というようにキーワードを覚えておけば、出題時の選択に迷うことはあまりないはずです。 また、単純で1回きりの(one-time の)データロードをローカル PC から行う、などのシンプルなユースケースでは、bq コマンドラインを用いて bq load コマンドを行うことで済む場合もあります。 半構造化データの扱い(JSON 型) 半構造化データとは、JSON フォーマットのように、ある程度構造化されているもののスキーマに柔軟性があるようなデータ構造を指します。当試験においては JSON をイメージすれば事足ります。 BigQuery には JSON 型があり、JSON 形式のような半構造的なキー・バリューを柔軟に格納することができます。SELECT 文では以下のように、 key1.key2 と指定することで選択が可能です。 SELECT json_payload.id FROM `my_dataset.my_table` このようなキーバリュー型をネストして格納できる型には他に STRUCT 型がありますが、STRUCT 型はスキーマが決まっているため、例えば日時経過でスキーマに変更がかかる可能性があり 柔軟性が求められるケースでは JSON 型が便利 です。また、テキスト形式であれば何でも格納できる STRING 型と比べても、パフォーマンス面とコスト面で JSON 型が有利です。 参考 : BigQuery の JSON 型を活用して半構造化データの力を引き出す パーティションとクラスタリング BigQuery の パーティション と クラスタリング については正しく理解してください。 blog.g-gen.co.jp また、パーティションには 有効期限 が設定でき、作成から一定時間が経過したデータを削除することができます。いわゆるハウスキーピングの自動化に最適です。 外部テーブル 外部テーブル (External Tables)機能を使うと、Cloud Storage に格納した CSV、JSON、Parquet、Avro ファイルや Google スプレッドシートのデータ、あるいは Bigtable に対して、BigQuery から SQL を使ってクエリすることができます。 ただし外部テーブル定義を行っても、データを BigQuery に取り込むわけではなく、あくまでデータは外部に置いたままクエリするため、パフォーマンス(処理速度)は高くありません。あくまで、クエリパフォーマンスが求められない場合や、データが少ない場合、また1回きり(one-time)のオンデマンドな分析などに利用します。 参考 : 外部テーブルの概要 SQL Google Cloud 認定資格では珍しく、当試験では SQL の具体的なソースコードを選択肢から選ばせる問題が出題されます。とはいえ、複雑にネストされた長大な SQL が出題されるわけではありません。 基本的な SELECT 文、また JOIN や UNION ALL などを使って複数テーブルを組み合わせて表示する際の SQL などを理解しておいてください。 ウインドウ関数 分析用途で用いられる ウインドウ関数 は、 文法を理解 しておく必要があります。以下は公式ドキュメントですが、必ずしもわかりやすいとは言い難いため、インターネット上の情報や各種書籍を参考にして、文法を理解しておいてください。 参考 : Window function calls 例えば RANK () 関数を使って、「各店舗の日次売上を集計し、上位5位を表示する」といった簡単な SQL がどのようなものになるか、わかるようにしておいてください。 コネクテッドシート コネクテッドシート (Connected Sheets)は、Google スプレッドシート(Google Sheets)から BigQuery のデータを読み取ることができる機能です。 反対に、BigQuery の 外部テーブル 機能では、Google スプレッドシートの URI を指定することでスプレッドシートを外部テーブルとして定義し、SQL でクエリすることができます。 これらを組み合わせると BigQuery 外部テーブルを使ってスプレッドシートの中身を読み取り、BigQuery のデータと結合して結果をテーブルに保存し、その結果をコネクテッドシートを使ってスプレッドシートから読み取る、といった相互の連携が容易に実現できます。 BigQuery のアクセス制御と暗号化 権限管理(IAM) BigQuery へのアクセス制御は、 IAM (Identity and Access Management)を用いて行います。 最小権限の原則 に従うのがキーです。 BigQuery と IAM については以下の記事で詳細に解説しています。 blog.g-gen.co.jp 記事でも紹介しているとおり、たとえば BigQuery のデータへの読み取りアクセスのために必要な最小権限を与える場合、対象アカウント(グループ)に以下の権限をプロジェクトレベルで付与します。 BigQuery ジョブユーザー( roles/bigquery.jobUser ) BigQuery データ閲覧者( roles/bigquery.dataViewer ) データの編集(UPDATE や DELETE)が必要な場合は、上記の「BigQuery データ閲覧者」の代わりに「BigQuery データ編集者( roles/bigquery.dataEditor )」あるいは「BigQuery データオーナー( roles/bigquery.dataOwner )」を付与します。ポイントは、ジョブ(クエリ)の実行には「BigQuery ジョブユーザー( roles/bigquery.jobUser )」が必要になるという点です。詳細は前掲の当社記事を参照してください。 承認されたビュー 承認されたビュー (Authorized view)機能を使うと、ビューへのアクセス制御を簡素化できます。シンプルで効率の良い方法で、ビュー(特定のクエリ結果)へのアクセスを制御したい場合に利用できます。以下の記事を参照して、仕様を理解してください。 blog.g-gen.co.jp 透過的な暗号化 透過的な暗号化 では、暗号鍵への適切なアクセス権限を持っていれば、利用者は暗号化を意識せずにストレージ上のデータを利用できます。 Google Cloud では 転送中のデータ (data in transit)と 保管中のデータ (data at rest)は共に デフォルトで暗号化 されています。これを デフォルトの暗号化 と呼びます。デフォルトの暗号化で暗号鍵として使われる鍵は Google が管理されており、適切に保管、ローテーションや廃棄が行われます。この鍵のことを Google-managed encryption keys 、略して GMEK と呼称します。 参考 : デフォルトの保存データの暗号化 参考 : 転送データの暗号化 また、BigQuery のコンソール画面(BigQuery Studio)や bq コマンドを使うとき、データはインターネットを経由して転送されますが、通信は HTTPS で暗号化されています。これは BigQuery だけでなくすべてのサービスで共通です。つまり、ユーザーが何もしなくても、Google Cloud を利用している限り、転送中のデータ(data in transit)と保管中のデータ(data at rest)はともに、暗号化されていることになります。 ただしセキュリティ向上や法的規制の要件への遵守などの目的で、GMEK ではなく独自の暗号鍵を利用する必要がある場合もあります。その場合は、 Cloud KMS を使って独自の暗号鍵を管理することができまし。このとき、この顧客独自の鍵のことを Customer-managed encryption keys (CMEK)と呼びます。 blog.g-gen.co.jp さらに厳しい規制要件等では、クラウド環境に鍵を保管することが許されない場合もあります。その場合は顧客の独自環境で鍵を保管・管理し、暗号化・復号のたびに鍵を保管場所から取り出して利用することができます。このとき、この鍵のことを Customer-supplied encryption keys (CSEK)と呼びます。 参考 : 顧客指定の暗号鍵 GMEK < CMEK < CSEK の順で保守や運用の工数は大きくなりますが、厳しい要件に対応することができます。試験では、これら3種類の鍵の 意味を正しく理解 していれば、正答を選ぶことができます。 BigQuery ML BigQuery ML の基本 当試験では BigQuery ML の基本的な理解を問われます。BigQuery ML では何が実現できるのか、またリモートモデルによる Vertex AI モデルの呼び出しといった概念を理解してください。 参考 : BigQuery の AI と ML の概要 組み込みモデル BigQuery ML には、すぐに利用可能なビルトインモデルが存在します。これらのモデルを指定し、トレーニングデータを投入すれば、SQL だけで簡単に独自モデルを開発可能です。 参考 : 内部でトレーニングされたモデル 代表的なモデルとユースケースを頭に入れておいてください。時系列(Time series)と線形回帰(Linear regression)はよく似ていますが、例えば店舗の需要予測の際、セールなどの異常値や季節性の変化を考慮に入れてトレーニングできるのは時系列予測です。 モデル名 用途 時系列(Time series) 時系列予測。 異常値、季節性、休日が考慮 される 線形回帰(Linear regression) 線形の予測 。例えば、特定の日の商品売上 ロジスティック回帰(Logistic regression) True である可能性を 0と1の間で 予測 K 平均法クラスタリング(K-means clustering) データの 分類 。顧客セグメントの分別など 行列分解(Matrix factorization) 商品のレコメンデーション 等。過去の行動を評価しておすすめを作成 主成分分析(Principal component analysis、PCA) データの次元削減 またモデルをトレーニングする際にどのような SQL を書けばよいのかという細かい点も出題されます。例えば以下のドキュメントでは、ロジスティック回帰の目的変数となる列の列名を input_label_cols オプションで指定しています。デフォルトでは目的変数列は label となるため、CREATE MODEL 文の中の SELECT 文で as label として列を選択すれば、その列が目的変数としてトレーニングされます。 参考 : ロジスティック回帰モデルを作成する スキューとドリフト トレーニングしたモデルを本番運用する際に重要なキーワードとして、 データスキュー (Data skew)と データドリフト(Data drift) という言葉を理解しておいてください。BigQuery ML の公式ドキュメントでは以下のように定義しています。 名称 意味 データスキュー(Data skew) トレーニングデータの分布が、本番環境でサーブされるデータと大きく異なる場合に発生 データドリフト(Data drift) 本番環境でのデータが時間の経過とともに大きく変化した場合に発生 すなわち、データスキューを監視することでトレーニングと実践のデータのずれを、データドリフトを監視することで時間経過に伴うモデルの劣化を、それぞれ検知することができるといえます。 参考 : モデル モニタリングの概要 Gemini BigQuery では、リモートモデルを作成することで LLM である Gemini を呼び出す ことができます。 CREATE OR REPLACE MODEL でリモートモデルを定義したあと、 ML.GENERATE_TEXT で Gemini を呼び出し、BigQuery 内部のデータをインプットしてテキストを生成させることが可能です。 AutoML AutoML に関する問題も、若干出題されます。Google Cloud の AutoML では、大量の教師データを Cloud Storage 等に配置しておき、AutoML のトレーニングを実行することで、簡単に独自の機械学習モデルをトレーニングすることができます。 参考 : AutoML 初心者向けガイド Cloud Storage Cloud Storage の基本 Cloud Storage は頻出プロダクトです。以下の記事を参照し、機能を理解してください。 blog.g-gen.co.jp 特に、この後の見出しで列挙する機能名は必ず押さえてください。 ストレージクラス Cloud Storage の ストレージクラス の概念は正確に理解してください。 ストレージクラス 保管料金 オペレーション料金 最低保管期間 Standard Storage 高い 安い なし Nearline Storage ↑ ↓ 30 日 Coldline Storage ↑ ↓ 90 日 Archive Storage 安い 高い 365 日 Standard > Nearline > Coldline > Archive の順で保管料金が安くなっていくことや、最低保管期間(この期間より短くオブジェクトを削除すると、この期間分の保管料金は発生する)が長くなっていくことを理解してください。最低保管期間は0、30、90、365と覚え、ストレージクラスの名称と合わせて覚えておいてください。 オブジェクトライフサイクル オブジェクトライフサイクル (ライフサイクルルール)を設定することで、古くなったオブジェクトを自動的によりアーカイブ寄りのストレージクラスに移動したり、削除したりできます。ライフサイクルのアクションは、以下の3つのみです。 Delete (オブジェクトを削除する) SetStorageClass (ストレージクラスを設定する) AbortIncompleteMultipartUpload (途中だったマルチパートアップロードを削除する) ライフサイクルルールを設定することで、「作成から30日経過したオブジェクトは Nearline に移動、90日経過したら Coldline に移動。1年間経過したら削除する」などの複雑なルール設定も可能です。 バージョニング バージョニング の機能も必ず理解しておいてください。オブジェクトが上書きされても、指定した世代を残しておくことができます。前述のライフサイクルルールと組み合わせて、「オブジェクトは3世代保管する。作成から30日経過したオブジェクトは Nearline に移動し...」といった設定も可能です。 Autoclass Autoclass 機能も出題されます。Autoclass を有効化すると、オブジェクトのアクセス状況に応じて自動的にストレージクラスを設定してくれますので、 運用工数を節減 することが可能です。 デュアルリージョン、マルチリージョン Cloud Storage バケットを作成時、バケットの配置場所をシングルリージョン、デュアルリージョン、マルチリージョンの中から選択できます。それぞれの特徴は、以下のドキュメントから理解しておいてください。 参考 : ロケーションに関する留意事項 データの冗長性を確保しつつ、データの所在を明らかにしておくためにはデュアルリージョンを選択するシチュエーションがありえます。データの冗長化は非同期で行われます。デフォルトの非同期レプリケーションでは、1時間以内に99.9%のオブジェクトが複製され、12時間以内に100%に達します。これでは RPO(Recovery Point Objective)要件を満たせない場合、 ターボレプリケーション (Turbo replication)を有効化することで、15分以内に100%のデータを複製できます。 参考 : データの可用性と耐久性 Storage Transfer Service Storage Transfer Service は、Amazon S3 などの外部ストレージサービスや、オンプレミスのファイルサーバー等から Cloud Storage にデータを転送するためのフルマネージドサービスです。 BigQuery Transfer Service が BigQuery への転送 を管理するサービスである一方、Storage Transfer Service は Cloud Storage への転送 を管理するサービスであると覚えてください。 包含接頭辞(include filter)や除外接頭辞(exclude filter)を使い、 対象範囲を絞ってジョブ複数作り、並列実行 することで転送時間を短くしたり、逆に直列で実行することで API 実行制限を回避することができます。 参考 : 転送速度を向上させる また Storage Transfer Service では、オンプレミスのファイルサーバーからのデータ転送も実現できます。この場合、オンプレミス側に Docker ベースの エージェントを起動する必要 があります。またネットワーク帯域が限らえている場合は、エージェントが利用する 帯域の上限を設定 することもできます。 参考 : ファイル システム転送の要件 参考 : ネットワーク帯域幅を管理する Storage Transfer Appliance Storage Transfer Appliance は物理的なアプライアンスをユーザーのデータが存在する場所に配送し、物理的に結線してデータを取り込み、また Google に返送することで Google のデータセンターに直接データを持ち込めるサービスです。持ち込み先は Cloud Storage バケットになります。インターネットや専用線経由でのデータ転送ではあまりに時間がかかってしまう場合や、十分な帯域が確保できない場合に利用します。なお、日本でも利用可能です。類似サービスとして、Amazon Web Services(AWS)の AWS Snowball があります。 ペタバイト(PB)級の量のデータ を Google Cloud に持ち込む場合や、数百 TB のデータを移行したいが ネットワーク帯域が限られている 場合等には、有力な選択肢の1つです。 参考 : Overview Looker Looker の基本 Looker は、Google Cloud が提供する BI プラットフォームサービスです。 LookML というデータモデリング言語を使ってあらかじめセマンティックレイヤを定義することで、高度なデータガバナンスを実現できるのが特徴です。 LookML によるデータモデリングや、分析から後続の施策に繋げる豊富な機能、また組織外へのデータ共有に関する機能などにより、Looker は単なる BI ツールではなく、高度なデータプラットフォームとして用いられます。 なお、Looker には、従来型の Looker(Original) と、Google Cloud と高度に統合された Looker(Google Cloud Core) の2バージョンがあります。 参考 : Looker(Google Cloud コア)で利用可能な機能 メジャーとディメンション Looker を使うには、データベース上のデータを使い、事前に LookML で メジャー (measures)と ディメンション (dimensions)を定義します。 メジャーとディメンションは、BI ツール等では一般的な用語ですので、意味を理解しておいてください。 また、メジャーとディメンションは Looker 上では view ファイル と呼ばれる定義ファイルに定義します。 Looker における権限管理 Looker では、ダッシュボード等への権限管理のため、ユーザーを グループ に格納できます。 部署やチームごとにグループを作成し、ユーザーをその中に配置したら、フォルダにおいてグループ単位で権限を付与するのがベストプラクティスです。運用負荷軽減のため、 個々のユーザーにではなくグループに権限を付与するべき であるという原則は Google Cloud の IAM とも共通しています。 参考 : グループ Analytics Hub Analytics Hub は、異なる組織間でデータを効率よく、セキュアに提供しあうためのプラットフォームです。Analytics Hub は BigQuery や Pub/Sub のデータ交換に対応しています。 自社のデータを、アクセス権限を適切に管理しながら効率よく他社に提供したいシチュエーションでは、Analytics Hub が選択できます。 参考 : Analytics Hub の概要 Colab Enterprise Colab Enterprise は、フルマネージドの Python ノートブックサービスです。BigQuery や Dataproc など他の Google Cloud サービスとも柔軟に連携できます。 Colab Enterprise のノートブック上から Python を使って BigQuery 上のデータを操作したいときなどに、Google アカウントの認証情報を使ってスムーズにデータ連携が可能です。 参考 : Introduction to Colab Enterprise Cloud SQL Cloud SQL に関する問題も若干出題されます。以下の記事を読み、基本を理解してください。 blog.g-gen.co.jp Cloud SQL の高可用性(HA)インスタンスを使うと、簡単な設定で、あるリージョンの中の複数のゾーンに Primary インスタンスと Secondary インスタンスを起動し、高い可用性を実現することができます。また、これに加えて非同期レプリケーションの レプリカ を加えることで、リージョン単位での障害に対応することもできます。 参考 : 障害復旧アーキテクチャ 杉村 勇馬 (記事一覧) 執行役員 CTO 元警察官という経歴を持つ IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。 Follow @y_sugi_it
G-gen の杉村です。2024年10月現在で利用可能な12種類の Cloud Load Balancing のアクセスログは、それぞれフォーマットが異なります。アクセスログをクエリするための Cloud Logging クエリと、アクセスログのサンプルを掲載します。 はじめに アクセスログのフィルタリング フィールドに入る値の違い 公式ドキュメントの記述 Cloud Logging 用クエリ アクセスログのサンプル Global external Application Load Balancer Classic Global external Application Load Balancer Regional external Application Load Balancer Regional internal Application Load Balancer Cross-region internal Application Load Balancer Global external proxy Network Load Balancer Classic Global external proxy Network Load Balancer Regional external proxy Network Load Balancer Regional internal proxy Network Load Balancer Cross-region internal proxy Network Load Balancer Regional external passthrough Network Load Balancer Regional internal passthrough Network Load Balancer はじめに Cloud Load Balancing は、Google Cloud(旧称 GCP)のフルマネージドのロードバランサーです。2024年10月現在、Cloud Load Balancing には用途によって異なる12種類のロードバランサーが存在します。 参考 : Cloud Load Balancing の概要 参考 : ロードバランサを選択する Cloud Load Balancing を構成するコンポーネントの1つである バックエンドサービス でログ出力を有効化することで、アクセスログを Cloud Logging に出力できます。しかし、12種類あるそれぞれのロードバランサーでは、アクセスログのフォーマットが異なります。そのため、監査目的で組織全体のすべてのロードバランサーのアクセスログを Cloud Logging の 集約シンク で集積したいというようなシチュエーションでログを網羅的に収集するには、ログのフィルタリングのためのクエリを適切に設定する必要があります。 当記事では、12種類の Cloud Load Balancing のアクセスログをフィルタリングするための方法や、各ロードバランサーのアクセスログのサンプルを掲載します。 なお、Cloud Logging や集約シンクについては、以下の記事を参照してください。 blog.g-gen.co.jp アクセスログのフィルタリング フィールドに入る値の違い 12種類のロードバランサーについて、アクセスログをフィルタするための Cloud Logging クエリについて検討します。 Cloud Logging で特定の種類のログエントリをフィルタするには、以下のようなフィールドでフィルタすることが考えられます。 resource.type jsonPayload.@type logName 例として、VPC Flow Logs のログエントリを抽出したい場合、以下のようなクエリになります( my-project はプロジェクト ID に置き換えてください。以下同じ)。 logName= " projects/my-project/logs/compute.googleapis.com%2Fvpc_flows " しかし Cloud Load Balancing においては、12種類のロードバランサーでそれぞれこれらのフィールドに入る値が異なっていることがわかります。 同じ Cloud Load Balancing というサービス名がついていても、ロードバランサー種別によって、その内部構造や成り立ち(開発の経緯)が大きく異なっていることが、この違いの根本にあるように思われます。 コピー&ペーストできるよう、同じ表を以下にテキスト形式でも掲載します。 # ロードバランサー名 resource.type jsonPayload.@type logName 1 Global external Application Load Balancer http_load_balancer type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/requests 2 Classic Global external Application Load Balancer http_load_balancer type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/requests 3 Regional external Application Load Balancer http_external_regional_lb_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fexternal_regional_requests 4 Regional internal Application Load Balancer internal_http_lb_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Frequests 5 Cross-region internal Application Load Balancer internal_http_lb_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Frequests 6 Global external proxy Network Load Balancer l4_proxy_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections 7 Classic Global external proxy Network Load Balancer tcp_ssl_proxy_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/requests 8 Regional external proxy Network Load Balancer l4_proxy_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections 9 Regional internal proxy Network Load Balancer l4_proxy_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections 10 Cross-region internal proxy Network Load Balancer l4_proxy_rule type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections 11 Regional external passthrough Network Load Balancer loadbalancing.googleapis.com/ExternalNetworkLoadBalancerRule type.googleapis.com/google.cloud.loadbalancing.type.ExternalNetworkLoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fflows 12 Regional internal passthrough Network Load Balancer loadbalancing.googleapis.com/InternalNetworkLoadBalancerRule type.googleapis.com/google.cloud.loadbalancing.type.InternalNetworkLoadBalancerLogEntry projects/my-project/logs/loadbalancing.googleapis.com%2Fflows 公式ドキュメントの記述 Google Cloud の公式ドキュメントには、ロードバランサーごとに、アクセスログへのクエリ方法が記載されています。 resource.type でのフィルタリング手順が記載されているドキュメントもあれば、 logName が記載されている場合もあります。 参考 : グローバル外部アプリケーション ロードバランサのロギングとモニタリング 参考 : リージョン外部アプリケーション ロードバランサのロギングとモニタリング 参考 : 内部アプリケーション ロードバランサのロギングとモニタリング 参考 : プロキシ ネットワーク ロードバランサのロギングとモニタリング 参考 : 外部パススルー ネットワーク ロードバランサのロギングとモニタリング 参考 : 内部パススルー ネットワーク ロードバランサのロギングとモニタリング Cloud Logging 用クエリ 表に示したように、12種類すべてのロードバランサーが共通して持つ値はありません。ドキュメントの記述に基づいた各ロードバランサーへのクエリを、以下のように OR 条件を用いて繋げることで、すべての種類のアクセスログを抽出することができます(2024年10月の当社環境による検証結果でも確認)。 クエリ1 ( resource . type =( " http_load_balancer " OR " http_external_regional_lb_rule " OR " internal_http_lb_rule " OR " tcp_ssl_proxy_rule " ) OR logName:( " logs/loadbalancing.googleapis.com%2Fconnections " OR " logs/loadbalancing.googleapis.com%2Fflows " ) ) あるいは以下のように、 jsonPayload.@type フィールドに対して、部分一致を示す : 記号でフィルタすることでもアクセスログを抽出できます。 クエリ2 jsonPayload.@ type : " type.googleapis.com/google.cloud.loadbalancing.type " 上記のクエリは短くてシンプルである一方、公式ドキュメントの手順に基づいていません。さらに、 resource.type や logName フィールドの方が、デフォルトでインデックスが作成されているため、より高速です。 参考 : インデックス フィールドの使用 なお部分一致検索は、完全一致での検索に比較してパフォーマンスが悪いとされています。最初のクエリでも、 logName フィールドに対して部分一致( : )で検索しています。もしクエリのパフォーマンスが重要な場合、プロジェクト名を明示してクエリするか、前掲の表に基づいて resource.type フィールドに対してクエリしてください。 参考 : Logging のクエリ言語 クエリ3 ( resource . type =( " http_load_balancer " OR " http_external_regional_lb_rule " OR " internal_http_lb_rule " OR " tcp_ssl_proxy_rule " ) OR logName=( " projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections " OR " projects/my-project/logs/loadbalancing.googleapis.com%2Fflows " ) ) クエリ4 resource . type =( " http_load_balancer " OR " http_external_regional_lb_rule " OR " internal_http_lb_rule " OR " l4_proxy_rule " OR " tcp_ssl_proxy_rule " OR " loadbalancing.googleapis.com/ExternalNetworkLoadBalancerRule " OR " loadbalancing.googleapis.com/InternalNetworkLoadBalancerRule " ) アクセスログのサンプル 以下に、12種類の各ロードバランサーのアクセスログのサンプルを掲載します。設定の条件によって、ログに含まれるフィールドが異なる場合があることをご了承ください。 Global external Application Load Balancer { " insertId ": " 1hl3dx4f9l9kl0 ", " jsonPayload ": { " enforcedSecurityPolicy ": { " rateLimitAction ": { " key ": " 119.173.43.159 ", " outcome ": " RATE_LIMIT_THRESHOLD_CONFORM " } , " outcome ": " ACCEPT ", " priority ": 2147483646 , " name ": " default-security-policy-for-backend-service-be ", " configuredAction ": " THROTTLE " } , " cacheDecision ": [ " RESPONSE_HAS_ETAG ", " RESPONSE_HAS_LAST_MODIFIED ", " RESPONSE_HAS_CONTENT_TYPE ", " CACHE_MODE_USE_ORIGIN_HEADERS " ] , " remoteIp ": " 119.173.43.159 ", " securityPolicyRequestData ": { " remoteIpInfo ": { " regionCode ": " JP " } } , " backendTargetProjectNumber ": " projects/1234567890 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " statusDetails ": " response_sent_by_backend " } , " httpRequest ": { " requestMethod ": " GET ", " requestUrl ": " http://34.128.128.201/ ", " requestSize ": " 78 ", " status ": 200 , " responseSize ": " 251 ", " userAgent ": " curl/7.88.1 ", " remoteIp ": " 119.173.43.159 ", " serverIp ": " 10.146.15.212 ", " latency ": " 0.006456s " } , " resource ": { " type ": " http_load_balancer ", " labels ": { " backend_service_name ": " be ", " zone ": " global ", " forwarding_rule_name ": " fe ", " url_map_name ": " global-external-alb ", " target_proxy_name ": " global-external-alb-target-proxy ", " project_id ": " my-project " } } , " timestamp ": " 2024-10-19T00:25:02.709671Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/requests ", " trace ": " projects/my-project/traces/91b497c4aa913d1adb6cb7239ff7c18f ", " receiveTimestamp ": " 2024-10-19T00:25:03.445640111Z ", " spanId ": " c85d614f1f6380f7 " } Classic Global external Application Load Balancer { " insertId ": " d1hn5ifh5uld4 ", " jsonPayload ": { " remoteIp ": " 34.84.45.157 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " cacheDecision ": [ " RESPONSE_HAS_ETAG ", " RESPONSE_HAS_LAST_MODIFIED ", " RESPONSE_HAS_CONTENT_TYPE ", " CACHE_MODE_USE_ORIGIN_HEADERS " ] , " statusDetails ": " response_sent_by_backend ", " backendTargetProjectNumber ": " projects/1234567890 " } , " httpRequest ": { " requestMethod ": " GET ", " requestUrl ": " http://34.160.74.92/ ", " requestSize ": " 76 ", " status ": 200 , " responseSize ": " 251 ", " userAgent ": " curl/7.74.0 ", " remoteIp ": " 34.84.45.157 ", " serverIp ": " 10.146.15.212 ", " latency ": " 0.004245s " } , " resource ": { " type ": " http_load_balancer ", " labels ": { " target_proxy_name ": " classic-global-external-alb-target-proxy ", " forwarding_rule_name ": " classic-global-external-alb-fe ", " project_id ": " my-project ", " backend_service_name ": " classic-global-external-alb-be ", " url_map_name ": " classic-global-external-alb ", " zone ": " global " } } , " timestamp ": " 2024-10-19T01:26:45.677162Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/requests ", " trace ": " projects/my-project/traces/fd0787f95c1d8c088f56dd3a0fc645a9 ", " receiveTimestamp ": " 2024-10-19T01:26:46.011597715Z ", " spanId ": " 0600c851dd6caba0 " } Regional external Application Load Balancer { " insertId ": " r6zhmhe3tin0 ", " jsonPayload ": { " securityPolicyRequestData ": { " remoteIpInfo ": { " asn ": 9824 , " regionCode ": " JP " } } , " enforcedSecurityPolicy ": { " configuredAction ": " THROTTLE ", " outcome ": " ACCEPT ", " priority ": 2147483646 , " name ": " default-security-policy-for-be-regional-external-alb ", " rateLimitAction ": { " key ": " 119.173.43.159 ", " outcome ": " RATE_LIMIT_THRESHOLD_CONFORM " } } , " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " backendTargetProjectNumber ": " projects/1234567890 " } , " httpRequest ": { " requestMethod ": " GET ", " requestUrl ": " http://34.146.179.192/ ", " requestSize ": " 78 ", " status ": 200 , " responseSize ": " 251 ", " userAgent ": " curl/7.88.1 ", " remoteIp ": " 119.173.43.159:45490 ", " serverIp ": " 10.146.15.212:80 ", " latency ": " 0.006646s ", " protocol ": " HTTP/1.1 " } , " resource ": { " type ": " http_external_regional_lb_rule ", " labels ": { " forwarding_rule_name ": " fe-regional-external-alb ", " network_name ": " default ", " url_map_name ": " regional-external-alb ", " backend_target_type ": " BACKEND_SERVICE ", " target_proxy_name ": " regional-external-alb-target-proxy ", " backend_type ": " INSTANCE_GROUP ", " matched_url_path_rule ": " UNMATCHED ", " backend_name ": " my-unmanaged-instance-group ", " backend_target_name ": " be-regional-external-alb ", " project_id ": " my-project ", " region ": " asia-northeast1 ", " backend_scope ": " asia-northeast1-b ", " backend_scope_type ": " ZONE " } } , " timestamp ": " 2024-10-19T00:26:57.853386Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fexternal_regional_requests ", " receiveTimestamp ": " 2024-10-19T00:27:01.596354155Z " } Regional internal Application Load Balancer { " insertId ": " 1g7z8yle6gehh ", " jsonPayload ": { " backendTargetProjectNumber ": " projects/1234567890 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry " } , " httpRequest ": { " requestMethod ": " GET ", " requestUrl ": " http://10.146.0.6/ ", " requestSize ": " 74 ", " status ": 200 , " responseSize ": " 251 ", " userAgent ": " curl/7.74.0 ", " remoteIp ": " 10.146.15.212:34372 ", " serverIp ": " 10.146.15.212:80 ", " latency ": " 0.005247s ", " protocol ": " HTTP/1.1 " } , " resource ": { " type ": " internal_http_lb_rule ", " labels ": { " project_id ": " my-project ", " matched_url_path_rule ": " UNMATCHED ", " backend_target_type ": " BACKEND_SERVICE ", " backend_scope_type ": " ZONE ", " backend_type ": " INSTANCE_GROUP ", " url_map_name ": " regional-internal-alb ", " target_proxy_name ": " regional-internal-alb-target-proxy ", " forwarding_rule_name ": " regional-internal-alb-fe ", " backend_name ": " my-unmanaged-instance-group ", " backend_scope ": " asia-northeast1-b ", " backend_target_name ": " regional-internal-alb-be ", " network_name ": " default ", " region ": " asia-northeast1 " } } , " timestamp ": " 2024-10-19T01:07:22.171311Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Frequests ", " receiveTimestamp ": " 2024-10-19T01:07:31.591492497Z " } Cross-region internal Application Load Balancer { " insertId ": " nvlidgezxrk5 ", " jsonPayload ": { " backendTargetProjectNumber ": " projects/1234567890 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry " } , " httpRequest ": { " requestMethod ": " GET ", " requestUrl ": " http://10.146.0.7/ ", " requestSize ": " 74 ", " status ": 200 , " responseSize ": " 251 ", " userAgent ": " curl/7.74.0 ", " remoteIp ": " 10.146.15.212:49148 ", " serverIp ": " 10.146.15.212:80 ", " latency ": " 0.000594s ", " protocol ": " HTTP/1.1 " } , " resource ": { " type ": " internal_http_lb_rule ", " labels ": { " target_proxy_name ": " cross-region-internal-alb-target-proxy ", " url_map_name ": " cross-region-internal-alb ", " region ": " asia-northeast1 ", " project_id ": " my-project ", " network_name ": " default ", " backend_target_name ": " cross-region-internal-alb-be ", " backend_type ": " INSTANCE_GROUP ", " backend_scope_type ": " ZONE ", " backend_name ": " my-unmanaged-instance-group ", " backend_scope ": " asia-northeast1-b ", " matched_url_path_rule ": " UNMATCHED ", " forwarding_rule_name ": " cross-region-internal-alb-fe ", " backend_target_type ": " BACKEND_SERVICE " } } , " timestamp ": " 2024-10-19T01:13:07.097824Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Frequests ", " receiveTimestamp ": " 2024-10-19T01:13:11.841628180Z " } Global external proxy Network Load Balancer { " insertId ": " wqqyu5e6bgx2 ", " jsonPayload ": { " endTime ": " 2024-10-19T01:34:01.839237Z ", " startTime ": " 2024-10-19T01:34:01.837981Z ", " connection ": { " clientIp ": " 10.146.15.212 ", " serverPort ": 80 , " protocol ": 6 , " clientPort ": 58774 , " serverIp ": " 10.146.15.233 " } , " serverBytesReceived ": " 77 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " serverBytesSent ": " 258 " } , " resource ": { " type ": " l4_proxy_rule ", " labels ": { " forwarding_rule_name ": " regional-internal-proxy-nlb-fe ", " network_name ": " default ", " protocol ": " TCP ", " region ": " asia-northeast1 ", " backend_scope ": " asia-northeast1-b ", " load_balancing_scheme ": " INTERNAL_MANAGED ", " target_proxy_name ": " regional-internal-proxy-nlb-target-proxy ", " backend_name ": " my-unmanaged-instance-group ", " backend_target_name ": " regional-internal-proxy-nlb ", " backend_scope_type ": " ZONE ", " project_id ": " my-project ", " backend_type ": " INSTANCE_GROUP ", " backend_target_type ": " BACKEND_SERVICE " } } , " timestamp ": " 2024-10-19T01:34:01.837981Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections ", " receiveTimestamp ": " 2024-10-19T01:34:06.969020769Z " } Classic Global external proxy Network Load Balancer { " insertId ": " g5348lf4ffk6m ", " jsonPayload ": { " remoteIp ": " 34.84.45.157 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " statusDetails ": " response_sent_by_backend ", " backendTargetProjectNumber ": " projects/1234567890 " } , " resource ": { " type ": " tcp_ssl_proxy_rule ", " labels ": { " region ": " global ", " backend_scope ": " asia-northeast1-b ", " project_id ": " my-project ", " forwarding_rule_name ": " classic-global-external-proxy-nlb-fe ", " backend_target_name ": " classic-global-external-proxy-nlb ", " backend_name ": " 7592391117369982738 ", " target_proxy_name ": " classic-global-external-proxy-target-proxy ", " backend_scope_type ": " ZONE ", " backend_target_type ": " BACKEND_SERVICE ", " backend_type ": " INSTANCE_GROUP " } } , " timestamp ": " 2024-10-19T01:28:26.522314Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/requests ", " receiveTimestamp ": " 2024-10-19T01:28:28.589962783Z " } Regional external proxy Network Load Balancer { " insertId ": " eqlb0pe8i7nf ", " jsonPayload ": { " startTime ": " 2024-10-19T01:32:45.769843Z ", " endTime ": " 2024-10-19T01:32:45.776569Z ", " serverBytesReceived ": " 76 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " connection ": { " clientIp ": " 34.84.45.157 ", " serverPort ": 80 , " clientPort ": 39902 , " protocol ": 6 , " serverIp ": " 34.85.18.103 " } , " serverBytesSent ": " 258 " } , " resource ": { " type ": " l4_proxy_rule ", " labels ": { " target_proxy_name ": " regional-external-proxy-nlb-target-proxy ", " protocol ": " TCP ", " forwarding_rule_name ": " regional-external-proxy-nlb-fe ", " backend_target_type ": " BACKEND_SERVICE ", " backend_type ": " INSTANCE_GROUP ", " project_id ": " my-project ", " backend_name ": " my-unmanaged-instance-group ", " load_balancing_scheme ": " EXTERNAL_MANAGED ", " backend_target_name ": " regional-external-proxy-nlb ", " region ": " asia-northeast1 ", " network_name ": " default ", " backend_scope_type ": " ZONE ", " backend_scope ": " asia-northeast1-b " } } , " timestamp ": " 2024-10-19T01:32:45.769843Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections ", " receiveTimestamp ": " 2024-10-19T01:32:51.445900382Z " } Regional internal proxy Network Load Balancer { " insertId ": " wqqyu5e6bgx2 ", " jsonPayload ": { " endTime ": " 2024-10-19T01:34:01.839237Z ", " startTime ": " 2024-10-19T01:34:01.837981Z ", " connection ": { " clientIp ": " 10.146.15.212 ", " serverPort ": 80 , " protocol ": 6 , " clientPort ": 58774 , " serverIp ": " 10.146.15.233 " } , " serverBytesReceived ": " 77 ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry ", " serverBytesSent ": " 258 " } , " resource ": { " type ": " l4_proxy_rule ", " labels ": { " forwarding_rule_name ": " regional-internal-proxy-nlb-fe ", " network_name ": " default ", " protocol ": " TCP ", " region ": " asia-northeast1 ", " backend_scope ": " asia-northeast1-b ", " load_balancing_scheme ": " INTERNAL_MANAGED ", " target_proxy_name ": " regional-internal-proxy-nlb-target-proxy ", " backend_name ": " my-unmanaged-instance-group ", " backend_target_name ": " regional-internal-proxy-nlb ", " backend_scope_type ": " ZONE ", " project_id ": " my-project ", " backend_type ": " INSTANCE_GROUP ", " backend_target_type ": " BACKEND_SERVICE " } } , " timestamp ": " 2024-10-19T01:34:01.837981Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections ", " receiveTimestamp ": " 2024-10-19T01:34:06.969020769Z " } Cross-region internal proxy Network Load Balancer { " insertId ": " 1kbs0nme7cs09 ", " jsonPayload ": { " serverBytesSent ": " 258 ", " serverBytesReceived ": " 77 ", " connection ": { " serverIp ": " 10.146.15.234 ", " serverPort ": 80 , " clientPort ": 38876 , " protocol ": 6 , " clientIp ": " 10.146.15.212 " } , " startTime ": " 2024-10-19T01:40:48.650089Z ", " endTime ": " 2024-10-19T01:40:48.656509Z ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry " } , " resource ": { " type ": " l4_proxy_rule ", " labels ": { " protocol ": " TCP ", " load_balancing_scheme ": " INTERNAL_MANAGED ", " network_name ": " default ", " target_proxy_name ": " cross-region-internal-proxy-nl-target-proxy ", " backend_scope_type ": " ZONE ", " forwarding_rule_name ": " cross-region-internal-proxy-nlb-fe ", " backend_target_type ": " BACKEND_SERVICE ", " backend_type ": " INSTANCE_GROUP ", " project_id ": " my-project ", " region ": " asia-northeast1 ", " backend_name ": " my-unmanaged-instance-group ", " backend_scope ": " asia-northeast1-b ", " backend_target_name ": " cross-region-internal-proxy-nlb " } } , " timestamp ": " 2024-10-19T01:40:48.650089Z ", " severity ": " INFO ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fconnections ", " receiveTimestamp ": " 2024-10-19T01:40:51.373191590Z " } Regional external passthrough Network Load Balancer { " insertId ": " 1fcug38f3uvja4 ", " jsonPayload ": { " endTime ": " 2024-10-19T01:59:03.698069641Z ", " startTime ": " 2024-10-19T01:59:03.696967927Z ", " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.ExternalNetworkLoadBalancerLogEntry ", " connection ": { " clientIp ": " 34.84.45.157 ", " serverIp ": " 34.84.37.150 ", " protocol ": 6 , " serverPort ": 80 , " clientPort ": 53720 } , " packetsReceived ": " 8 " } , " resource ": { " type ": " loadbalancing.googleapis.com/ExternalNetworkLoadBalancerRule ", " labels ": { " backend_group_scope ": " ZONE ", " primary_target_pool ": "", " backend_service_name ": " regional-external-passthrough-nlb ", " backend_subnetwork_name ": " default ", " forwarding_rule_name ": " regional-external-passthrough-nlb-fe ", " backend_group_name ": " my-unmanaged-instance-group ", " target_pool ": "", " project_id ": " my-project ", " backend_zone ": " asia-northeast1-b ", " backend_network_name ": " default ", " backend_group_type ": " INSTANCE_GROUP ", " backend_target_type ": " BACKEND_SERVICE ", " region ": " asia-northeast1 " } } , " timestamp ": " 2024-10-19T01:59:03.698069641Z ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fflows ", " receiveTimestamp ": " 2024-10-19T01:59:16.645829810Z " } Regional internal passthrough Network Load Balancer { " insertId ": " 1gq6lrwfwidq51 ", " jsonPayload ": { " packetsSent ": " 2 ", " bytesReceived ": " 154 ", " endTime ": " 2024-10-19T02:03:37.78722619Z ", " packetsReceived ": " 2 ", " startTime ": " 2024-10-19T02:03:37.787098544Z ", " connection ": { " serverPort ": 80 , " clientIp ": " 10.146.15.212 ", " serverIp ": " 10.146.15.236 ", " protocol ": 6 , " clientPort ": 36116 } , " @type ": " type.googleapis.com/google.cloud.loadbalancing.type.InternalNetworkLoadBalancerLogEntry ", " rtt ": " 0.000127646s " } , " resource ": { " type ": " loadbalancing.googleapis.com/InternalNetworkLoadBalancerRule ", " labels ": { " backend_service_name ": " regional-internal-passthrough-nlb ", " region ": " asia-northeast1 ", " project_id ": " my-project ", " backend_subnetwork_name ": " default ", " forwarding_rule_name ": " regional-internal-passthrough-nlb-fe ", " backend_network_name ": " default ", " backend_group_type ": " INSTANCE_GROUP ", " backend_group_scope ": " ZONE ", " backend_group_name ": " my-unmanaged-instance-group " } } , " timestamp ": " 2024-10-19T02:03:37.787226190Z ", " logName ": " projects/my-project/logs/loadbalancing.googleapis.com%2Fflows ", " receiveTimestamp ": " 2024-10-19T02:03:46.610207174Z " } 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it
G-gen の武井です。当記事では、Access Context Manager を使い、Google Cloud コンソール と API へのアクセスを制限する方法について解説します。 はじめに 概要 実現できること アクセスレベル アーキテクチャ 概要 アクセス制限の対象ユーザー 組織外ユーザーに対するアクセス制限 実際にやってみた (デモ) 説明 Admin コンソールでの設定 Cloud コンソールでの設定 動作確認 シリアル番号の収集、登録 Google グループの作成 アクセスレベルの作成 Google グループとアクセスレベルの紐づけ Cloud コンソールへのアクセス #1 の結果 #2 の結果 gcloud CLI の実行 #1 (会社所有のインベントリに登録された端末) の結果 #2 (会社所有のインベントリに登録された端末) の結果 関連記事 はじめに 概要 当記事ではデバイス情報、アカウント情報、接続状況などの 背景情報 (コンテキスト) にもとづき、Google Cloud の Web コンソールや、gcloud コマンドライン・SDK を使った Google Cloud API へのアクセス制限方法について解説します。 実現できること Google Cloud 環境の利用者に対し、接続元 IP アドレスや端末シリアル等のデバイスポリシーといった、 コンテキストベースのルール (アクセスレベル) にもとづくアクセス制御 が行えます。 アクセスレベルで定義した条件に違反する利用者は、コンソール画面にアクセスできないほか、CLI や SDK を使った環境操作もできなくなります。 アクセスレベル アクセスレベルは Access Context Manager で作成できます。 以下の要素を条件として指定できますが、デバイスポリシーを使ってルールを作成する場合は Chrome Enterprise Premium (旧 BeyondCorp Enterprise) のライセンス契約が必要です。 属性 説明 補足 IPアドレス ・IPv4 アドレス ・IPv6 アドレス ・プライベート IP は不可 地域 ・国名コード (JP等) - アクセスレベルの依存関係 ・他のアクセスレベル - プリンシパル ・ユーザーアカウント ・サービスアカウント ・グループは指定不可 ・コンソールからの設定不可 (gcloud / API からのみ設定可) デバイスポリシー ・画面のロック ・管理者の承認 ・会社所有のデバイス (シリアル) ・ストレージの暗号化 ・OS ポリシー ・CEP ライセンスが必要 参考: Access Context Manager の概要 参考: アクセスレベルの属性 参考: Chrome Enterprise Premium アーキテクチャ 概要 設定としては、Access Context Manager で作成したアクセスレベルを、アクセス制限対象のユーザーをグルーピングした Google グループに紐づけます。 これにより、アクセスレベルに合致しないユーザーは、Google Cloud コンソールや Cloud APIs へのアクセスが制限される仕組みです。 必要なコンポーネント 役割 Endpoint Verification (任意) アクセス制限対象ユーザーのデバイス属性情報の収集に使用 Google グループ (必須) アクセス制限対象ユーザーのグルーピングに使用 アクセスレベル (必須) アクセス制限の条件 (ホワイトリスト) として使用 参考: Endpoint Verification の概要 アクセス制限の対象ユーザー 当仕組みにおけるアクセス制限の対象ユーザーは、アクセスレベルを紐づけた Google グループに所属する、同一組織 (上図でいう example.co.jp ) のユーザーのみです。 同一組織であっても当該グループに属さないユーザーや組織外のユーザーに対しては、 当仕組みを用いたアクセス制限の効力が及ばない (無制限でアクセス可能である) という点にご留意ください。 組織外のユーザーは、当該グループに属していたとしても制限の対象とはなりません。 組織外ユーザーに対するアクセス制限 上記の理由から、組織外ユーザーからのアクセスを制限したい場合、別の仕組みによる保護を検討してください。以下はその例です。 組織のポリシー ( constraints/iam.allowedPolicyMemberDomains ) で当該組織上での IAM Policy 設定を禁止する VPC Service Controls で Cloud APIs へのアクセスを禁止する 参考: ドメイン別の ID の制限 参考: VPC Service Controlsを分かりやすく解説 (G-gen Tech Blog) 実際にやってみた (デモ) 説明 このデモでは、 端末のシリアル番号 をアクセスレベルに組み込み、Cloud コンソールや gcloud による Cloud APIs に対するアクセス制限が実現できるかを確認します。 以下の順で設定を行い、最後に動作確認を行います。 Admin コンソールでの設定 シリアル番号の収集、登録 Google グループの作成 Cloud コンソールでの設定 アクセスレベルの作成 Google グループとアクセスレベルの紐づけ (アクセス制限の有効化) 動作確認 Cloud コンソールへのアクセス gcloud CLI の実行 シリアル番号の収集、登録 端末のシリアル番号を使ってアクセス制限を実施する場合、操作端末のシリアル番号を 会社所有のインベントリ に登録します。 操作端末を Google Workspace の管理コンソール上の デバイス として登録 デバイスとして登録された操作端末のシリアル番号を 会社所有のインベントリ に登録 操作端末をデバイスに登録 デバイスに登録した操作端末のシリアル番号を、会社所有のインベントリに登録 手順の詳細は以下の公式ガイドをご参照ください。 参考: パソコンに Endpoint Verification を設定する 参考: 会社で所有しているデバイスをインベントリに追加する Google グループの作成 アクセス制限対象のユーザーをグルーピングするため、 Google グループ を作成します。 アクセス制限対象者をGoogle グループでグルーピング 手順の詳細は以下の公式ガイドをご参照ください。 参考: グループを作成し、グループ設定を選択する アクセスレベルの作成 次に、Cloud コンソールから Access Context Manager を選択し、アクセスレベルを作成します。 今回のデモでは端末のシリアル番号を使ってアクセス制限を行うので、条件に デバイスポリシー (会社所有のデバイスが必要) を選択します。 アクセスレベルの条件にはデバイスポリシーの「会社所有のデバイスが必要」を選択 作成されたアクセスレベル 手順の詳細は以下の公式ガイドをご参照ください。 参考: アクセスレベルを作成する 参考: ベーシック アクセスレベルを作成する 参考: 必要な IAM 権限を付与する Google グループとアクセスレベルの紐づけ Google グループとアクセスレベルを紐づけ、Cloud コンソールや API へのアクセス制御を有効化します。 任意のプロジェクトを選択した状態で、検索バーで chrome enterprise premium と入力してサービスを選択したら、 CLOUD コンソールと API へのアクセスを管理 をクリックします。 その後、 アクセスを管理 > 追加 の順で遷移したら Google グループとアクセスレベルを紐づけます。 アクセスを管理をクリック 追加をクリック グループとアクセスレベルを紐づけて保存する 手順の詳細は以下の公式ガイドをご参照ください。 参考: アクセスレベルを使用してアクセス バインディングを作成する Cloud コンソールへのアクセス 以下の条件で動作確認を行います。 # アカウント (所属グループ) シリアル番号 1 yutakei@example.co.jp (restrict@example.co.jp) 会社所有のインベントリに登録 2 yutakei@example.co.jp (restrict@example.co.jp) 会社所有のインベントリに 未登録 #1 の結果 操作端末のシリアル番号がアクセスレベルに合致する ため、Cloud コンソールへアクセスできました。 アクセスレベルに合致したため、Cloud コンソールにアクセスできた #2 の結果 操作端末のシリアル番号がアクセスレベルに合致しない ため、Cloud コンソールへのアクセスが制限されました。 アクセスレベルに合致しないため、Cloud コンソールへのアクセスが制限された gcloud CLI の実行 前述の条件で動作確認を行います。 #1 (会社所有のインベントリに登録された端末) の結果 操作端末のシリアル番号がアクセスレベルに合致する ため、 gcloud auth login や gcloud auth application-default login コマンドが成功しました。 $ gcloud auth login Your browser has been opened to visit: 中略 You are now logged in as [ yutakei@example.co.jp ] . Your current project is [ cep-demo-prj ] . You can change this setting by running: $ gcloud config set project PROJECT_ID $ gcloud auth application-default login Your browser has been opened to visit: 中略 Credentials saved to file: [ /home/yutakei/.config/gcloud/application_default_credentials.json ] These credentials will be used by any library that requests Application Default Credentials ( ADC ) . Quota project " cep-demo-prj " was added to ADC which can be used by Google client libraries for billing and quota. Note that some services may still bill the project owning the resource. #2 (会社所有のインベントリに登録された端末) の結果 操作端末のシリアル番号がアクセスレベルに合致しない ため、 gcloud auth login や gcloud auth application-default login コマンドが失敗しました。 $ gcloud auth login Your browser has been opened to visit: 中略 ERROR: Access was blocked by Context Aware Access. If you are using gcloud in an SSH session and your organization requires gcloud from a company registered device, please first RDP into your remote machine and log into Chrome. ERROR: ( gcloud.auth.login ) ( access_denied ) Account restricted $ gcloud auth application-default login Your browser has been opened to visit: 中略 ERROR: Access was blocked by Context Aware Access. If you are using gcloud in an SSH session and your organization requires gcloud from a company registered device, please first RDP into your remote machine and log into Chrome. ERROR: ( gcloud.auth.application-default.login ) ( access_denied ) Account restricted 関連記事 blog.g-gen.co.jp 武井 祐介 (記事一覧) クラウドソリューション部所属。G-gen唯一の山梨県在住エンジニア Google Cloud Partner Top Engineer 2024 に選出。IaC や CI/CD 周りのサービスやプロダクトが興味分野です。 趣味はロードバイク、ロードレースやサッカー観戦です。 Follow @ggenyutakei
G-gen の杉村です。当記事では、Google Cloud の IAM(Identity and Access Management)で最小権限の原則を実現するための手段をご紹介します。 IAM と最小権限の原則 過剰な権限を予防する IAM の仕組みを正確に理解する IAM ポリシーを編集できる人を限定する 一時的な特権の管理 適切な IAM ロールを選択する ドキュメントの確認とロール選定 Gemini による支援 すでに付与された権限を整理する IAM Recommender Policy Analyzer Cloud Assets Inventory IAM と最小権限の原則 IAM (Identity and Access Management)は、Google Cloud のリソースに対する権限管理の仕組みです。詳細は以下の記事をご確認ください。 blog.g-gen.co.jp セキュアなクラウド環境を維持するには、IAM で 最小権限の原則 を遵守することが重要です。最小権限の原則とは、「システムの利用主体に持たせるアクセス権限は、必要最小限に抑えるべきである」という設計思想であり、情報セキュリティの大原則です。 しかし Google Cloud でシステムをしばらく運用していると、IAM ロールが過剰に付与されてしまっていることに後から気がつくことが、度々あります。 当記事では、最小権限の原則を守る手段を、「 過剰な権限を予防する 」「 適切な IAM ロールを選択する 」「 すでに付与された権限を整理する 」の3パートにわけて解説します。 過剰な権限を予防する IAM の仕組みを正確に理解する プロジェクトのオーナーや組織の管理者にとって、最小権限を実現するためになにより重要なのは、Google Cloud の IAM の仕組みを正確に理解することです。以下のような概念を正確に理解するべきです。 リソースの持つ IAM ポリシー IAM バインディング 継承 Google Cloud の IAM の仕組みは、Amazon Web Services(AWS)等のそれとは大きく異なります。まずは以下の記事を読み、Google Cloud の IAM の仕組みを正確に理解してください。 blog.g-gen.co.jp IAM ポリシーを編集できる人を限定する 組織やプロジェクト、各 Google Cloud リソースに過剰な権限が付与されてしまう状況を予防するには、IAM ポリシーを編集できる人(主体)を限定することが重要です。 例えば、組織レベルの IAM ポリシーを操作できる権限である resourcemanager.organizations.setIamPolicy は、以下のような IAM ロールに含まれています。 組織の管理者( roles/privilegedaccessmanager.organizationServiceAgent ) セキュリティ管理者( roles/iam.securityAdmin ) また、プロジェクトレベルの IAM ポリシーを操作できる権限である resourcemanager.projects.setIamPolicy は、以下のような IAM ロールに含まれています。 オーナー( roles/owner ) 組織の管理者( roles/privilegedaccessmanager.organizationServiceAgent ) セキュリティ管理者( roles/iam.securityAdmin ) プロジェクト IAM 管理者( roles/resourcemanager.projectIamAdmin ) 組織やフォルダ、プロジェクトの IAM 権限が容易に変更されないよう、これらのロールを付与するアカウントを限定的にすることが望ましいです。 ある権限がどの IAM ロールに含まれているかを調査するには、以下のドキュメントをご参照ください。 参考 : IAM roles and permissions index 一時的な特権の管理 前述のように IAM 権限を操作できる人を限定すると、開発スピードが低下したり、運用性が低下することが考えられます。 必要なときに、必要な間だけ、必要な権限を付与するような運用を実現するには、 Privileged Access Manager (PAM)が利用できます。PAM はフルマネージドの特権管理ワークフローであり「権限の申請」「承認」「所定時間が経過したあとの特権剥奪」といったワークフローを簡単に実装できます。 PAM の申請画面 PAM によって承認フローを含めたジャスト・イン・タイム(JIT)な IAM 特権管理を行うことで、セキュアな最小権限を維持しながら、開発スピードや運用性の低下を防ぐことが可能です。 PAM の詳細は以下の記事をご確認ください。 blog.g-gen.co.jp 適切な IAM ロールを選択する ドキュメントの確認とロール選定 Google アカウントや Google グループなどのプリンシパルに IAM ロールを付与する際は、必要最小限の権限を持つ IAM ロールを選択します。 以下の公式ドキュメントを確認したり、あるいは Google Cloud プロダクトごとの IAM 関連のガイドを確認し、必要最小限の権限を持つ事前定義ロールを選択します。 参考 : IAM roles and permissions index 実行したいアクションに対して、どの権限(permission)が必要なのかは、アクションが呼び出す API メソッドにより決まります。公式ガイドを確認したり、あるいはアクション実行時に出力される Cloud Logging ログ、また権限が足りなかったときに出力されるエラーメッセージなどが参考になります。 Gemini による支援 Google Cloud コンソールには、生成 AI モデル Gemini が、実現したいことに対する適切な IAM ロールを推奨してくれる機能があります。詳細は以下の記事を参照してください。 blog.g-gen.co.jp すでに付与された権限を整理する IAM Recommender IAM Recommender は、最近(デフォルトで90日間)の IAM 利用状況を機械学習で分析し、推奨される対策を提示してくれる機能です。あるプロジェクト(または組織やフォルダ)に付与された過剰な権限を洗い出したいときに利用できます。 IAM Recommender は、組織やフォルダ、プロジェクトの IAM 画面から確認することができます。 IAM 一覧画面に表示される推奨事項 推奨事項の内訳 IAM Recommender は無料で利用できますが、以下の分析や推奨を生成するには、組織レベルで Security Command Center のプレミアムティアに登録している必要があります(一部抜粋)。 基本ロール(オーナー、編集者、閲覧者)以外の推奨事項 組織、フォルダ、プロジェクト以外に付与されたロールに関する推奨事項 無料版だとオーナー、編集者、閲覧者といった基本ロールに対してしか推奨事項が生成されません。 参考 : ロールの推奨事項の概要 Policy Analyzer Policy Analyzer は、Google Cloud リソースの IAM ポリシーに対してクエリを行い、あるプリンシパル(Google アカウント等)やロール、リソースの権限を一覧表示させたいときに使います。 例えば、以下のようなことが実現可能です。 あるプロジェクトにおいて、サービスアカウントの権限を借用できるプリンシパルを一覧化する あるプロジェクトにおいて、ファイアウォールルールを編集できるプリンシパルを一覧化する ある BigQuery データセットを編集または閲覧可能なプリンシパルを一覧化する Policy Analyzer は、Google Cloud コンソールの「IAM と管理」の画面の「ポリシー アナライザ」から利用できます。無料で利用できますが、1日に20回のクエリまでという制限があります。20回を超えるクエリを実行するには、組織レベルでSecurity Command Center のプレミアムティアに登録する必要があります。 任意の検索ができるカスタムクエリに加えて、以下のようなプリセットのクエリが用意されています(スクリーンショットは2024年10月現在)。 プリセットされているクエリ カスタムクエリ作成画面 カスタムクエリの結果画面 参考 : IAM ポリシー用の Policy Analyzer Cloud Assets Inventory Cloud Assets Inventory には、あるプリンシパルがどのリソースに権限を持っているかをリストアップする機能があります。利用料金はありません。 Google Cloud コンソールの Cloud Asset Inventory 画面から、あるいは gcloud コマンド等を使ってクエリをかけることができます。 Cloud Asset Inventory での IAM ポリシークエリ この機能は、以下のようなユースケースで利用できます。 特定のプリンシパルが、どのリソースに対してどのような権限を持っているかをリストアップする 組織全体で、オーナー権限を持っている Google アカウントやグループをリストアップする あるデータセットに対して、誰がアクセス権限(閲覧、編集)を持っているかをリストアップする これらは、Policy Analyzer でできることとほとんど同様です。ただし、Cloud Assets Inventory でのクエリに実行回数制限はないため、利用方法を理解していれば、こちらの機能で代替することができます。 Cloud Assets Inventory の詳細については、以下の記事もご参照ください。 blog.g-gen.co.jp 杉村 勇馬 (記事一覧) 執行役員 CTO 元警察官という経歴を持つ IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。 Follow @y_sugi_it
G-gen の杉村です。2024年10月のイチオシ Google Cloud アップデートをまとめてご紹介します。記載は全て、記事公開当時のものですのでご留意ください。 はじめに Gemini で Dynamic retrieval が GA BigQuery コンソールで Airflow の DAG が管理できるように Google Forms で新しい質問タイプ「Rating」が使えるように BigQuery の SQL で Pipe syntax(パイプ構文)が Preview 公開 Gemini Code Assist に Enterprise プランが登場 Looker から Looker Studio レポートが閲覧・編集可能に(Preview) Cloud Armor で IP Address groups 機能が公開(GA) BigQuery の history-based optimizations は今後デフォルトに BigQuery で Fine-grained DML が申請付き Preview 公開 Google Workspace の Gmail への移行ツールが GA(一般公開) Gemini for Google Workspace のサイドパネルが日本語対応(アルファ版) Google Chat で未読メッセージを生成 AI がサマリ Vertex AI Search で CMEK が利用可能に(US と EU) Associate Google Workspace Administrator 試験(Beta)が受付開始 BigQuery data preparation が Preview 公開 Vertex AI Search でグラウンディングスコアが取得できるように(GA) AlloyDB でメジャーバージョンのインプレイスアップグレード(Preview) Cloud Storage の認証済み URL がデータアクセス監査ログに対応 GitHub Copilot で Gemini モデルが使えるように はじめに 当記事では、毎月の Google Cloud アップデートのうち特に重要なものをまとめます。 また当記事は、Google Cloud に関するある程度の知識を前提に記載されています。前提知識を得るには、ぜひ以下の記事もご参照ください。 blog.g-gen.co.jp リンク先の公式ガイドは、英語版で表示しないと最新情報が反映されていない場合がありますためご注意ください。 Gemini で Dynamic retrieval が GA Generative AI on Vertex AI - Dynamic retrieval (2024-10-01) Gemini で Dynamic retrieval が GA。 Google Search へのグラウンディングが、有効になりそうかの度合い(Prediction score。0〜1の間の小数値)が設定した閾値を超えたときだけ、グラウンディングが行われるようになる。レイテンシや処理コストの調整のため利用できる。 同時に、Vertex AI Agent Builder でも同様の機能が Allowlist 付き Preview 公開。 参考 : Generate grounded answers with RAG BigQuery コンソールで Airflow の DAG が管理できるように Manage Airflow DAGs (2024-10-07) BigQuery コンソールで Airflow の DAG が管理できるようになった(Preview)。 バックエンドは、Cloud Composer 3。Cloud Composer 3 は、フルマネージドの Apache Airflow であり、3 では従来型と異なり、サーバーが見えないフルマネージドになった。 Google Forms で新しい質問タイプ「Rating」が使えるように Ask responders for a rating in Google Forms (2024-10-07) Google Forms で新しい質問タイプ「Rating」が使えるようになる。 満足度(CSAT)の回答などに使える。アイコンは★、いいねマーク、ハートマークがつかえる。10/7から順次ロールアウト。 BigQuery の SQL で Pipe syntax(パイプ構文)が Preview 公開 Pipe syntax (2024-10-08) BigQuery の SQL で Pipe syntax(パイプ構文)が使えるように(Preview)。 クエリをパイプで繋いで結果を渡していくことで直感的にクエリが書ける。探索的なクエリ時やログ検索時などに最適。サブクエリを含む複雑なクエリがシンプルになることも期待できる。 以下の記事も参照。 blog.g-gen.co.jp Gemini Code Assist に Enterprise プランが登場 Introducing AI-powered app dev with code customization from Gemini Code Assist Enterprise (2024-10-10) コーディング補助サービス「Gemini Code Assist」に Enterprise プランが登場。 Standard との違いは、以下とおり。 GitHub や GitLab のコードを読み込ませて、コード生成を最適化する Code Customization Gemini in Databases Gemini in BigQuery Looker から Looker Studio レポートが閲覧・編集可能に(Preview) Using Studio in Looker (2024-10-09) Looker(Google Cloud core)で、Studio in Looker 機能が Preview 公開。 Looker から Looker Studio レポートが閲覧・編集できる。利用には申請が必要。 Cloud Armor で IP Address groups 機能が公開(GA) Address groups overview (2024-10-09) Cloud Armor で IP Address groups 機能が公開(GA)。 複数の IP アドレスをグループにまとめ、複数のポリシーから再利用できる。Cloud NGFW のファイアウォールポリシーからも再利用できるよう設定も可能。 BigQuery の history-based optimizations は今後デフォルトに Get up to 100x query performance improvement with BigQuery history-based optimizations (2024-10-15) 2024年9月30日に GA となった BigQuery の history-based optimizations 機能は、今後数ヶ月をかけて、「全顧客でデフォルトで有効になる」ことが判明。 history-based optimizations とは、類似のクエリを複数回実行していくと、自動的にクエリが最適化されていく機能。バックエンドではさまざまなアルゴリズムで最適化が行われている。 BigQuery で Fine-grained DML が申請付き Preview 公開 Fine-grained DML (2024-10-15) BigQuery で Fine-grained DML が申請付き Preview 公開。 テーブル単位で有効化すると、UPDATE、DELETE、MERGE 文がきめ細かく行われるようになり、スロット消費を抑え、パフォーマンスが最適化される。テーブル単位で有効化する必要あり。 Google Workspace の Gmail への移行ツールが GA(一般公開) Now generally available: Migrate users’ emails from Google Workspace, Gmail and other IMAP enabled mail servers (2024-10-15) Google Workspace の Gmail への移行ツールが GA(一般公開)。 他の Workspace、個人 Gmail、IMAP などからメールを移行できる。差分転送も可能。 Gemini for Google Workspace のサイドパネルが日本語対応(アルファ版) Available in alpha: use Gemini in the side panel of Workspace apps in seven additional languages (2024-10-16) Google Workspace の Gemini サイドパネルの日本語版が、以下の Google Workspace アプリで使えるように(アルファ版公開)。 Google ドキュメント Google スプレッドシート Gmail Google ドライブ 他にもドイツ語、イタリア語、韓国語、ポルトガル語、スペイン語、フランス語が同時対応 アルファ版とは、「アルファ版は、より広範囲へのリリース前に行われる限定的なテスト版です。」「通常、アルファ版への参加は招待制で、一般提供前の利用条件が適用されます。」というもの。 参考 : ソフトウェア テストのフェーズと GA について Google Chat で未読メッセージを生成 AI がサマリ Preview summaries of unread conversations in the Google Chat home view with the help of Gemini (2024-10-21) Google Chat で、未読メッセージを生成 AI がサマリしてくれる機能が登場。要 Gemini アドオンライセンス。 Vertex AI Search で CMEK が利用可能に(US と EU) Customer-managed encryption keys (2024-10-17) Vertex AI Search で CMEK(customer-managed encryption keys)が利用可能に(GA)。 データストアが CMEK で暗号化されるようになり、規制要件等に対応できる。対応ロケーションは US と EU。 Associate Google Workspace Administrator 試験(Beta)が受付開始 Associate Google Workspace Administrator - Beta (2024-10-22) 新しい Google Cloud 認定資格、Associate Google Workspace Administrator 試験(Beta)が2024年10月22日に受付開始。 Google Workspace の日常的な管理業務や、トラブルシューティングに関する知識や技能が問われる。以下記事では、当社エンジニアによる受験 Tips を公開している。 blog.g-gen.co.jp BigQuery data preparation が Preview 公開 Introduction to BigQuery data preparation (2024-10-24) BigQuery data preparation が Preview 公開。 Gemini がデータクレンジングの推奨事項を提示。背後で Dataform が動きデータ変換を実現。BigQuery Studio(コンソール画面)から利用可能。 2024年末ころまでは無料で利用できるが、Gemini in BigQuery は、2024年末ころから以下のいずれかのユーザーのみが使えるように限定される。 BigQuery Enterprise Plus Edition Gemini Code Assist Enterprise Vertex AI Search でグラウンディングスコアが取得できるように(GA) Return grounding support scores (2024-10-25) Vertex AI Search で、グラウンディングスコアが取得できるようになった(GA)。 要約とフォローアップで、生成された文章の各位置のグラウンディングスコア(どれくらいグラウンディングに基づいて生成された文章か)が確認できる。 さらに関連アップデートとして、フィルタリングレベルを設定し、グラウンディングスコアが一定以下の場合は返答を返さないように設定できるになった。詳細は以下の通り。 Show only well-grounded answers AlloyDB でメジャーバージョンのインプレイスアップグレード(Preview) Upgrade a database in-place major version (2024-10-28) AlloyDB for PostgreSQL でメジャーバージョンのインプレイスアップグレードが Preview 公開。 インプレイスアップグレードとは、新規インスタンスを起動することなく、既存インスタンスのバージョンをアップグレードすること。現在利用可能なのは PostgreSQL 14 から 15 へのインプレイスアップグレード。 Cloud Storage の認証済み URL がデータアクセス監査ログに対応 Return grounding support scores (2024-10-29) Cloud Storageの「認証済みブラウザでのダウンロード」がデータアクセス監査ログに対応。 従来はデータアクセス監査ログが有効だと、認証済み URL でのダウンロードがエラーになったが、今後は可能になる。Looker Studio で Cloud Storage 上の画像を IAM 認証で表示するとき等に課題だった。当該の問題については、以下の記事を参照。 blog.g-gen.co.jp GitHub Copilot で Gemini モデルが使えるように Gemini models are coming to GitHub Copilot (2024-10-30) GitHub Copilot で Gemini モデル(Gemini 1.5 Pro)が使えるようになる。コード生成、分析、最適化に Gemini が使われる。 リンク先記事には「開発者は、今後数週間以内に、 github.comの GitHub Copilot Chat、Visual Studio Code、および Visual Studio の Copilot 拡張機能との会話中に、Gemini 1.5 Pro を選択できるようになります。」とある。 杉村 勇馬 (記事一覧) 執行役員 CTO / クラウドソリューション部 部長 元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。 Follow @y_sugi_it