Cloud RunのIAP認証をTerraformで設定する手順

Cloud Run のアプリの手前に IAP の関門があり、許可したユーザーだけが通れる様子を表したイラスト Google Cloud
Cloud Run に IAP 認証をかけ、許可したユーザーだけがアプリを開ける構成のイメージ

はじめに

Cloud Run で動かすアプリを、特定のグループのメンバーだけがブラウザで開ける状態にしたいと考えました。そこで、ロードバランサーを使わずに Cloud Run へ直接 IAP を設定し、Terraform で構成しました。

IAP(Identity-Aware Proxy)は、アプリの手前で Google アカウントのログインとアクセス権を確認する Google Cloud の機能です。本記事では、Cloud Run に IAP 認証をかける Terraform の書き方と、実際に試した動作確認の結果をまとめます。

本記事のまとめ

  • Cloud Run Service に iap_enabled = true を付け、2 つの IAM を付与するだけで、ロードバランサーなしに IAP 認証をかけられる。
  • 組織配下のプロジェクトなら、OAuth クライアントを自分で作らなくても Google 管理の OAuth クライアントで動くことを実機で確認した。
  • 許可していないユーザーは、Google のログインは通ったうえで IAP の拒否ページになる。
  • roles/owner を持っていても IAP 経由では開けない。閲覧させたい人には roles/iap.httpsResourceAccessor を明示的に付ける。

Cloud Run の IAP 認証とは(ロードバランサー不要)

Cloud Run の IAP 認証は、ロードバランサーを立てずに Cloud Run Service へ直接設定できます。直接 IAP が使えるようになる前は、ロードバランサーを経由して IAP を設定する必要がありました。

Cloud Run に直接 IAP を設定する機能は、Cloud Run のリリースノートで 2026 年 3 月 13 日に一般提供(GA)になったと告知されています。設定方法は公式ドキュメント「Configure IAP for Cloud Run」にまとまっています。

直接 IAP の構成で、利用者がアプリを開くまでの流れは次のとおりです。

  • 利用者がブラウザで Cloud Run の URL を開くと、IAP が Google アカウントのログインを求める。
  • ログイン後、IAP は IAM でアクセス権を確認する。権限が無ければ拒否ページを返す。
  • 権限があれば、IAP が利用者に代わって Cloud Run を呼び出し、アプリの画面が表示される。

なお、IAP でログインに使える ID の種類(認証ソース)は、1 つのサービスにつき 1 つだけです。Google アカウントと、Microsoft Entra ID などの外部 ID を、1 つのサービスで両方は許可できません。本記事は Google アカウントを使う構成を扱います。

外部 ID を使う場合は IAP の設定 API で Cloud Run 用のリソースを指定しますが、この部分は 2026 年 9 月の検証時点でまだプレビュー版の扱いでした。Google アカウントだけで使う本記事の構成では、この API は使いません。

設定の前提(OAuth クライアント・権限・provider)

設定の前に、OAuth クライアント・デプロイする人の権限・Terraform の provider の 3 点を確認します。組織配下のプロジェクトであれば、OAuth クライアントを自分で用意する必要はありません。

OAuth クライアントは Google 管理のもので足りる

OAuth クライアントは、IAP が Google のログイン画面を出すときに使う「アプリの登録情報」です。IAP を有効にすると、既定では Google が管理する OAuth クライアントが使われます。

ただし公式ドキュメントによると、Google 管理の OAuth クライアントで開けるのは、リソースと同じ組織に属するユーザーだけです。組織外のユーザーにも見せる場合は、独自の OAuth クライアントの設定が必要になります。

私の検証環境は Google Cloud の組織配下にあるプロジェクトで、OAuth クライアントを 1 つも作らずに IAP 認証が動きました。組織に属していないプロジェクトについては、今回は検証していません。

デプロイする人に必要なロール

公式ドキュメントでは、IAP を有効化する人に次のロールが必要とされています。Terraform を CI で実行するなら、そのサービスアカウントに付けておきます。

ロール用途
roles/run.adminCloud Run Service の作成・更新
roles/iap.adminIAP のアクセス権(IAM)の設定
roles/iap.settingsAdminIAP の設定
roles/oauthconfig.editorOAuth の構成
roles/iam.serviceAccountUserCloud Run に実行用サービスアカウントを割り当てる
roles/artifactregistry.readerコンテナイメージの読み取り

Terraform は google-beta provider を指定する

私の環境では google provider が 5.x 系に固定されていました。そこで google-beta provider を 7.x 系で追加し、IAP 関連のリソースにだけ provider = google-beta を付けて動かしました。この方法なら terraform validate が通り、google provider のメジャーバージョンを上げずに済みます。

provider の定義は次のように、googlegoogle-beta を並べて書きます。

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 7.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

provider "google-beta" {
  project = var.project_id
  region  = var.region
}

google-beta で作りたいリソースには、次の節のコードのように provider = google-beta を 1 行足します。書いていないリソースは、これまでどおり google provider で作られます。provider を追加・変更したら、terraform init -upgrade でロックファイル(.terraform.lock.hcl)を更新してから plan してください。

Terraform で Cloud Run に IAP 認証を設定する手順

必要なリソースは、Cloud Run Service、IAP サービスエージェントの実行権限、閲覧者のアクセス権の 3 つです。以下は私が書いた定義をもとにした最小構成の例で、プロジェクト ID やグループのアドレスはご自身の環境に置き換えてください。

Cloud Run Service で iap_enabled を有効にする

Cloud Run Service に iap_enabled = true を書くと、その Service で IAP が有効になります。動作確認には Google 提供のサンプルイメージを使いました。実行用のサービスアカウント(google_service_account.app)の定義は省略しているので、別途作成してください。

resource "google_cloud_run_v2_service" "app" {
  provider    = google-beta
  project     = var.project_id
  name        = "cloudrun-iap-sample"
  location    = "asia-northeast1"
  ingress     = "INGRESS_TRAFFIC_ALL"
  iap_enabled = true

  template {
    service_account = google_service_account.app.email

    scaling {
      min_instance_count = 0
    }

    containers {
      image = "us-docker.pkg.dev/cloudrun/container/hello"
    }
  }
}

ingress は外部からのアクセスを受け付ける INGRESS_TRAFFIC_ALL にしています。アクセスを絞るのは IAP の役割なので、allUsersroles/run.invoker付けていません

IAP サービスエージェントに Cloud Run の実行権限を付ける

サービスエージェントは、Google Cloud のサービスが利用者のプロジェクトで処理を行うために使う、Google 管理のサービスアカウントです。IAP のサービスエージェントは service-プロジェクト番号@gcp-sa-iap.iam.gserviceaccount.com です。これに roles/run.invoker を付けると、IAP が Cloud Run を呼び出せるようになります。

resource "google_project_service_identity" "iap_service_agent" {
  provider = google-beta
  project  = var.project_id
  service  = "iap.googleapis.com"
}

resource "google_cloud_run_v2_service_iam_member" "iap_invoker" {
  project  = var.project_id
  location = google_cloud_run_v2_service.app.location
  name     = google_cloud_run_v2_service.app.name
  role     = "roles/run.invoker"
  member   = "serviceAccount:${google_project_service_identity.iap_service_agent.email}"
}

サービスエージェントのメールアドレスは、プロジェクト番号を文字列でつなげて組み立てず、google_project_service_identityemail を参照しています。

閲覧させるグループに roles/iap.httpsResourceAccessor を付ける

アプリを開ける人は、roles/iap.httpsResourceAccessor で決まります。私の環境では、IAM は個人ではなくグループ単位で付ける方針です。そのため Google グループに付与しました。

resource "google_iap_web_cloud_run_service_iam_binding" "iap_access" {
  provider               = google-beta
  project                = var.project_id
  location               = google_cloud_run_v2_service.app.location
  cloud_run_service_name = google_cloud_run_v2_service.app.name
  role                   = "roles/iap.httpsResourceAccessor"

  members = [
    "group:app-users@example.com",
  ]
}

_iam_binding は、そのロールを持つメンバーを Terraform の定義どおりに上書きします。コンソールで手動追加したメンバーは apply で消えるので、plan で既存のメンバーを必ず確認してください。ほかの付与と共存させたい場合は _iam_member を使います。

plan で差分を確認して apply する

定義を書いたら、フォーマット・構文・差分の順に確認します。追加だけで、削除や置き換えが出ていないことを見てから apply します。

terraform fmt -check -diff
terraform validate
terraform plan

動作確認:許可したユーザーだけが開ける

apply 後、許可したグループのユーザーと、グループ外のユーザーの 2 人で Cloud Run の URL を開きました。グループのユーザーだけがアプリを開け、グループ外のユーザーは IAP の拒否ページで止まりました。

グループのユーザーで開くと、Google のログインを経てサンプルページが表示されます。サンプルイメージは IAP が渡した ID を画面に出すため、次のようにどのアカウントで入ったかが分かります。

You are authenticated as accounts.google.com:<ログインしたアカウント>

この表示があれば、IAP の認証を通ってアプリに届いたことを確かめられます。疎通確認のためだけに自前のアプリを用意する必要はありません。

一方、グループに入っていないユーザーで同じ URL を開くと、IAP の「You don’t have access」というページが表示され、アプリには届きません。

確認した内容をまとめると次のとおりです。

確認したこと結果
許可したグループのユーザーが開ける開けた
グループ外のユーザーは拒否されるIAP の拒否ページになった
OAuth クライアントを作らずに動く動いた(Google 管理の OAuth クライアントのみ)
asia-northeast1 で動く動いた

リージョンについては、公式ドキュメントに直接 IAP のリージョン制限の記述が見つかりませんでした。東京リージョン(asia-northeast1)で動くことは、今回の検証で確認しています。

つまずきやすい点

検証中に判断を誤りかけた点が 2 つありました。とくに「owner なら開けるはず」という思い込みは、権限の中身を確かめると誤りだと分かります。

拒否ページはログインの失敗ではない

拒否ページには、ログインしたユーザーのアカウントが表示されていました。つまり、Google のログイン(認証)は成功しており、そのうえで IAP がアクセス権(認可)を確認して拒否しています。

拒否ページが出たら、ログインをやり直すのではなく、そのユーザーに roles/iap.httpsResourceAccessor が付いているかを確認します。

roles/owner では IAP 経由で開けない

検証に使ったグループのユーザーはプロジェクトの roles/owner も持っていたため、「アクセス権を付けなくても開けたのでは」と疑いました。そこでロールに含まれる権限を調べました。

gcloud iam roles describe roles/owner --format="value(includedPermissions)" \
  | tr ';' '\n' | grep accessViaIAP

roles/owner に含まれる accessViaIAP 系の権限は、次の 2 つだけでした。どちらも TCP 転送(VM への接続など)用で、Web アプリを開くための権限ではありません。

iap.tunnelDestGroups.accessViaIAP
iap.tunnelInstances.accessViaIAP

IAP 経由で Web アプリを開くには iap.webServiceVersions.accessViaIAP が必要です。roles/iap.httpsResourceAccessor の中身は、この 1 権限だけです。プロジェクトの owner でも、このロールを付けなければ開けません。

なお、--format="value(includedPermissions)" の出力は ; 区切りです。, で分割すると権限が 1 つも見つからず、誤った結論になるので注意してください。

まとめ

Cloud Run に直接 IAP を設定し、ロードバランサーなしで Google アカウントの IAP 認証をかけられることを確認しました。必要なのは iap_enabled = true と、IAP サービスエージェントへの実行権限、閲覧者へのアクセス権の 3 つです。

  • 組織配下のプロジェクトなら、OAuth クライアントは Google 管理のもので足りる。ただし開けるのは組織内のユーザーだけになる。
  • 閲覧させたい人には roles/iap.httpsResourceAccessor を付ける。roles/owner では開けない。
  • 拒否ページが出たら、ログインではなくアクセス権を確認する。

まずはサンプルイメージで IAP 認証を通し、アクセス権の付け方を確かめてから自分のアプリに切り替えるのがおすすめです。Terraform の各リソースの引数は、Terraform Registry の google_cloud_run_v2_service で確認できます。