Terraformでfirebase deploy用のサービスアカウントを作る

GitHub ActionsなどCI/CD環境からfirebaseのデプロイをするために、認証トークンではなく、サービスアカウントを使うとした時に、GCP Consoleでぽちぽちするのも面倒だなということで、Terraformで作ってみました。

ざっと探してもそのままな例も見つからなかったので、こちらに残しておきます。

## サービスアカウント

サービスアカウントは、`google_service_account`で作れるので、firebaseのプロジェクトID、accountId(メアド形式のユーザ名部分に相当)、(必要に応じて)アカウントの表示名を指定しておきます。

```terraform
resource "google_service_account" "service_account" {
  project      = var.project_id
  account_id   = "deploy"
  display_name = "Deploy user"
}
```

## 権限の付与

この辺りは、いろいろ先人の知恵もあり`google_project_iam_member`を使うのが良いらしいということで、あとは必要な権限を見繕って付与していきます。
Hostingのみを使う場合と、HostingとFunctionsの双方を使う場合の例を示します。

### Hostingのみ

```Terraform
resource "google_project_iam_member" "service_account_user" {
  project = var.project_id
  role    = "roles/iam.serviceAccountUser"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "apikeys_viewer" {
  project = var.project_id
  role    = "roles/serviceusage.apiKeysViewer"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "firebaserules_system" {
  project = var.project_id
  role    = "roles/firebaserules.system"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "firebasehosting_admin" {
  project = var.project_id
  role    = "roles/firebasehosting.admin"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}
```

### Hosting + Functions
Hostingに加えて、`roles/cloudfunctions.developer`と`roles/secretmanager.viewer`(SecretManagerを使う場合)を指定します。

```Terraform
resource "google_project_iam_member" "service_account_user" {
  project = var.project_id
  role    = "roles/iam.serviceAccountUser"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "apikeys_viewer" {
  project = var.project_id
  role    = "roles/serviceusage.apiKeysViewer"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "firebaserules_system" {
  project = var.project_id
  role    = "roles/firebaserules.system"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "firebasehosting_admin" {
  project = var.project_id
  role    = "roles/firebasehosting.admin"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "cloudfunctions_developer" {
  project = var.project_id
  role    = "roles/cloudfunctions.developer"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_project_iam_member" "secretmanager_viewer" {
  project = var.project_id
  role    = "roles/secretmanager.viewer"
  member  = "serviceAccount:${google_service_account.service_account.email}"
}
```

## キーの保存

GitHub ActionsのSecretsにキーを保存することを考えてローカルファイルに落としておきます。ローカルに落とさずに、[github providerのgithub_actions_secret](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_secret)に渡して一気に登録までしちゃうのが楽でいいですね。

```Terraform
resource "google_service_account_key" "deployuser-key" {
  service_account_id = google_service_account.service_account.name
}

resource "local_file" "deployuser-key" {
  filename             = "./output/secrets/deployuser-key"
  content              = google_service_account_key.deployuser-key.private_key
  file_permission      = "0600"
  directory_permission = "0755"
}
```

実際にはtfstateに生でキーが残っちゃうのがちょっとキモい(というかかなりキモい)。なので、OIDCで接続するようにしよう。そうしよう。