📘

Terraform cloud から OIDC 経由で GCP の Credentials を取得する

2023/11/02に公開

Terraform cloud の文脈でいうと、2023年の1月に「Dynamic Provider Credentials」という機能がリリースされ、特定の Provider に対して動的に Credentials の情報が取得できるようになりました
https://www.hashicorp.com/blog/terraform-cloud-adds-dynamic-provider-credentials-vault-official-cloud-providers

2023/11現在でサポートされてる Provider は以下になります

  • Vault
  • AWS
  • GCP
  • Azure

GCP からの動的な Credentials の取得は、 OIDC (OpenID Connect) 経由での取得になります。

ここでは、設定に必要な手順について簡易的に記載をします。

一番伝えたいこと

「Dynamic Provider Credentials の設定には Variable Set は使えない」
ハマりがちなポイントなので、最初に記載をしておきます。

公式ドキュメント

https://developer.hashicorp.com/terraform/cloud-docs/workspaces/dynamic-provider-credentials/gcp-configuration

公式ドキュメントに、一通りの設定手順について記載されています。
ただ、この記事では後述の learn-terraform-dynamic-credentials をベースに説明していきます。

learn-terraform-dynamic-credentials

Dynamic Provider Credentials の設定については Hashicorp 公式のリポジトリにサンプルの設定集がおいてあります。こちらを倣って設定していくのがおそらく一番楽な手順になると思います。
https://github.com/hashicorp-education/learn-terraform-dynamic-credentials

GCP への設定

OIDC フェデレーション用の IAM の設定を行います。
https://cloud.google.com/iam/docs/workload-identity-federation?hl=ja

以下がおおまかな手順になります。

Workload Identity プールの作成

OIDC 関連の信頼ポリシーの設定を、GCP では「Workload Identity プール」を作成することで実施します。
https://cloud.google.com/iam/docs/manage-workload-identity-pools-providers?hl=ja

設定内容については、 learn-terraform-dynamic-credentials の設定を参考にするほうがわかりやすいと思うので引用します。

resource "google_iam_workload_identity_pool" "tfc_pool" {
  workload_identity_pool_id = "my-tfc-pool"
}

resource "google_iam_workload_identity_pool_provider" "tfc_provider" {
  workload_identity_pool_id          = google_iam_workload_identity_pool.tfc_pool.workload_identity_pool_id
  workload_identity_pool_provider_id = "my-tfc-provider-id"

  attribute_mapping = {
    "google.subject"                        = "assertion.sub",
    "attribute.aud"                         = "assertion.aud",
    "attribute.terraform_run_phase"         = "assertion.terraform_run_phase",
    "attribute.terraform_project_id"        = "assertion.terraform_project_id",
    "attribute.terraform_project_name"      = "assertion.terraform_project_name",
    "attribute.terraform_workspace_id"      = "assertion.terraform_workspace_id",
    "attribute.terraform_workspace_name"    = "assertion.terraform_workspace_name",
    "attribute.terraform_organization_id"   = "assertion.terraform_organization_id",
    "attribute.terraform_organization_name" = "assertion.terraform_organization_name",
    "attribute.terraform_run_id"            = "assertion.terraform_run_id",
    "attribute.terraform_full_workspace"    = "assertion.terraform_full_workspace",
  }
  oidc {
    issuer_uri = "https://${var.tfc_hostname}"
    # The default audience format used by TFC is of the form:
    # //iam.googleapis.com/projects/{project number}/locations/global/workloadIdentityPools/{pool ID}/providers/{provider ID}
    # which matches with the default accepted audience format on GCP.
    #
    # Uncomment the line below if you are specifying a custom value for the audience instead of using the default audience.
    # allowed_audiences = [var.tfc_gcp_audience]
  }
  attribute_condition = "assertion.sub.startsWith(\"organization:${var.tfc_organization_name}:project:${var.tfc_project_name}:workspace:${var.tfc_workspace_name}\")"
}

issue_url (発行元URL) は基本的には https://app.terraform.io 固定になります。
信頼条件については、環境によって設定が変わってくると思います。

attribute_condition (属性条件) に、接続元の Terraform cloud を信頼する条件について記載をします。

"assertion.sub.startsWith(\"organization:${var.tfc_organization_name}:project:${var.tfc_project_name}:workspace:${var.tfc_workspace_name}\")"
item value
tfc_organization_name Terraform cloud の organization の名前
tfc_project_name Terraform cloud の project の名前
tfc_workspace_name Terraform cloud の workspace の名前

それぞれの値は、それぞれ固定の値を入れて完全一致で指定することもできます。
どの条件でも許可する場合は、該当の item について記述を削除すればワイルドカード扱いになります。

上記の設定の結果作成される Workload Identity Pool の値は以下のような形になります。この値を Terraform cloud 側に設定することになるので、控えておきます

projects/{project number}/locations/global/workloadIdentityPools/{pool ID}/providers/{provider ID}

Service Account の作成

続いて、権限を付与する Service Account を作成します。
すでに該当のものが存在する場合はそちらを使用します。

権限については、terraform apply を行う場合は、基本ロールを用いるとしたら roles/editor を付与することになると思います。
terraform plan のみを実行する場合は roles/viewer を付与する形でも良いかも知れません。

resource "google_service_account" "tfc_service_account" {
  account_id   = "tfc-service-account"
  display_name = "Terraform Cloud Service Account"
}

resource "google_project_iam_member" "tfc_project_member" {
  project = var.gcp_project_id
  role    = "roles/editor"
  member  = "serviceAccount:${google_service_account.tfc_service_account.email}"
}

Service Account と Workload Identity プールの関連付けについては、learn-terraform-dynamic-credentials の定義をここでは引用します。
role については roles/iam.workloadIdentityUserを指定します。

resource "google_service_account_iam_member" "tfc_service_account_member" {
  service_account_id = google_service_account.tfc_service_account.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.tfc_pool.name}/*"
}

Terraform cloud への設定

GCP 側に作成した IAM Role の情報を、 Terraform cloud の variables に設定をします。

item value
TFC_GCP_PROVIDER_AUTH true
TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL Service Account の値
TFC_GCP_WORKLOAD_PROVIDER_NAME 作成した Workload Identity プールの値

上記の値を Variables に設定すれば基本的には OK、です。

見落としがちな大事なハマりポイントとして、 「Dynamic Provider Credentials の設定には Variables Set は使えない(ように見える)」 という点があります。
公式ドキュメントでは variable set も使えると書いてあるのですが、私の環境では何度試してもダメでした。Workspace variables に同じ値を設定すると、問題なく動作しました。

You can set these as workspace variables, or if you’d like to share one GCP service account across multiple workspaces, you can use a variable set.

OIDC 関連の設定は複数のプロジェクト/ワークスペースで共有したいので Variable set に指定したくなりますが、このような制約があるため、Variable set に正しい値を設定してもおそらく正しく動作してくれません。ご注意ください。

Discussion