😊

GitHub ActionsでのTerraformのPlanとApplyで異なるサービスアカウントを使用する

2024/09/22に公開

What

GitHub ActionsでTerraformのCI/CDを構成すると、Terraform PlanはPRのOpen時に、Terraform ApplyはPRのMerge時に発火するようにすることが多いと思います。

この時にTerraformのPlanとApplyで同じサービスアカウントを使用すると、任意のブランチから強力な権限を使用でき、リスクを孕んだ構成となってしまいます。

そのため、TerraformのPlan時には読み取り権限のみ付与したサービスアカウントを使用し、TerraformのApply時のみ更新権限を付与したサービスアカウントを使用するのが望ましいと言えます。

GCPのWorkload Identityを使用して、GitHub ActionsでTerraformのPlanとApplyで異なるサービスアカウントを使用する方法を書きます。

Workload Identity Poolの作成

resource "google_iam_workload_identity_pool" "github_actions" {
  project                   = var.project_id
  workload_identity_pool_id = "gh-oidc-pool"
  display_name              = "GitHub Actions OIDC Pool"
  description               = "Workload Identity Pool for GitHub Actions"
}
  • GitHub Actions用のWorkload Identity Poolを作成します

Workload Identity Pool Providerの設定

resource "google_iam_workload_identity_pool_provider" "github_actions" {
  project                            = var.project_id
  workload_identity_pool_id          = google_iam_workload_identity_pool.github_actions.workload_identity_pool_id
  workload_identity_pool_provider_id = "github-actions"
  display_name                       = "GitHub Actions Provider"
  description                        = "OIDC identity pool provider for GitHub Actions"
  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.repository" = "assertion.repository"
    "attribute.ref"        = "assertion.ref"
    "attribute.full_ref"   = "assertion.repository + \"/\" + assertion.ref"
  }
  attribute_condition = "attribute.repository.startsWith(\"${var.github_organization}/\")"
  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}
  • attribute_mappingでGitHubからの情報をGCPの属性にマッピングします
  • attribute.full_refでリポジトリとブランチの条件を結合したattributeをマッピングします
    • これにより特定のリポジトリの特定のブランチからの接続を条件として書けるようにしています
  • attribute_conditionで特定のOrganizationのリポジトリからの接続のみを許可しています

Plan用サービスアカウントの設定

resource "google_service_account" "github_actions_plan" {
  project      = var.project_id
  account_id   = "github-actions-plan"
  display_name = "Service account for GitHub Actions Plan"
}

resource "google_service_account_iam_member" "github_actions_plan" {
  service_account_id = google_service_account.github_actions_plan.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/attribute.repository/${var.github_organization}/${var.github_repository}"
}
  • Plan用のサービスアカウントを作成し、Workload Identity Poolと紐付けます
  • 別途roles/viewerなどの読み取り権限のみを付与するようにします
  • attribute.repositoryをプリンシパルとしているので、どのブランチからでもPlan用のサービスアカウントを使用できます

Apply用サービスアカウントの設定

resource "google_service_account" "github_actions_apply" {
  project      = var.project_id
  account_id   = "github-actions-apply"
  display_name = "Service account for GitHub Actions Apply"
}

resource "google_service_account_iam_member" "github_actions_apply" {
  service_account_id = google_service_account.github_actions_apply.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/attribute.full_ref/${var.github_organization}/${var.github_repository}/refs/heads/main"
}
  • Apply用のサービスアカウントを作成し、Workload Identity Poolと紐付けています
  • 別途roles/ownerなどの更新権限のみを付与するようにします
  • attribute.full_refをプリンシパルとしており、main(レビュー済みのコード)からしかApply用のサービスアカウントを使用できないようにしています

Discussion