🗒️
Firebase ProjectをTerraformを使って管理
以前こちらで紹介したリポジトリの管理方法のterraformを利用した環境構築をコードベースで紹介しようと思います。
.
├── .envrc
├── .gitignore
├── _modules # <-- 全プロジェクト共通のmodule (現在は廃止)
│ ├── module_1
│ └── module_2
├── Makefile
├── project_1
│ ├── .envrc
│ ├── env_1
│ │ ├── .envrc
│ │ ├── Makefile
│ │ ├── main.tf
│ │ └── variables.tf
│ └── env_2
│ ├── .envrc
│ ├── Makefile
│ ├── main.tf
│ └── variables.tf
├── project_2
│ ├── .envrc
│ ├── env_1
│ │ ├── modules # <-- プロジェクト固有のmodule
│ │ │ ├── module_1
│ │ │ └── module_2
│ │ ├── .envrc
│ │ ├── Makefile
│ │ ├── main.tf
│ │ └── variables.tf
│ └── env_2
│ ├── .envrc
│ ├── Makefile
│ ├── main.tf
│ └── variables.tf
└── project_3
├── .envrc
├── env_1
│ ├── .envrc
│ ├── Makefile
│ ├── main.tf
│ └── variables.tf
└── env_2
├── .envrc
├── Makefile
├── main.tf
└── variables.tf
こんな構造をしています。
全体の流れ
- Makefileでgcloudコマンドを実行し最低限の状態にする
- terraformのmain.tfファイルの用意
- terraform cloudにworkspaceの作成
- 環境変数の設定
- terraformの実行(Run)
以上です
事前準備周り
gcloudコマンドが使えるように(brewでもinstallできます)
configuration を使用した project の切り替え(オプション)
direnvを利用して作業時のディレクトリ内で使用する環境変数を管理します(オプション)
Makefileでgcloudコマンドを実行し最低限の状態にする
Workload Identity連携を利用するなどすれば作成段階も全てterraformで賄えそうですが、そこまでする必要はないかなと思い、作成と支払いアカウントの紐付け、terraformで実行する用のサービスアカウントの作成。
このあたりはgcloudコマンドで行う方針にしました。
一応理由
- 請求先アカウントに紐付けられるプロジェクトはデフォルト5件まで(申請すれば50件くらいまで上げられる)なので、プロジェクト毎に請求先アカウントを作成する運用にしているため、請求先アカウントの作成と紐付けを考えるとあまり恩恵を感じなかった
- 普段の状態管理と作成周りを一緒のworkspaceにしてしまうとstateの変化があった場合の影響範囲が未知で不安だった(別workspaceにするのもなんか違う気がした)
- 正直面倒そうだった(Workload Identityの発行や管理が)
といった結構偏見を交えつつ個人的精神バランスを取った結果、プロジェクトの作成と最低限の処理をgcloudコマンドに任せることにしました。
手順
.envrcファイルで環境変数を設定
/.envrc
export CLOUDSDK_ACTIVE_CONFIG_NAME=xxx
export ORG_ID=01234567890
/project_1/.envrc
source_up
export PROJECT_PREFIX=project_1
export BILLING_ACCOUNT=xxxxxx-xxxxxx-xxxxxx
export DIR_NAME=${PROJECT_PREFIX}
/project_1/env_1/.envrc
source_up
export CONFIGURATION={env_name} #ex: prd, stg
export PROJECT_SUFFIX={identification_number} #ex: 001
export DIR_NAME=${DIR_NAME}/${CONFIGURATION}
export PROJECT_NAME=${PROJECT_PREFIX}-${CONFIGURATION}
変数の説明
変数 | 内容 |
---|---|
CLOUDSDK_ACTIVE_CONFIG_NAME | gcloud configurationで設定された設定切り替え名 |
ORG_ID | GCPの組織ID |
PROJECT_PREFIX | これがプロジェクトのベースとなる名前 |
BILLING_ACCOUNT | 請求アカウントID |
CONFIGURATION | prg, stgなど環境レベルを表す値 |
PROJECT_SUFFIX | 作り直しなどをする可能性を考えたり、同じ名前のプロジェクトが存在している可能性を考慮し001など一意性が保たれるような値を入れ、その値をproject_idに設定する |
DIR_NAME |
source_up によって上位階層のDIR_NAMEの値を継承しつつ、現在のDIR_NAMEを更新する。この値を利用して最終的にサービスアカウントのjsonファイルを吐き出し先を指定しています。 |
PROJECT_NAME | PROJECT_PREFIXとCONFIGURATIONを繋げてプロジェクト名にする |
Makefileの用意
/Makefile
## 定数
PROJECT_ID = ${PROJECT_NAME}-${PROJECT_SUFFIX}
SERVICE_ACCOUNT_KET_DIR = ./${DIR_NAME}/service-accounts/${PROJECT_ID}
SERVICE_ACCOUNT = terraform@${PROJECT_ID}.iam.gserviceaccount.com
#-----------------------------------------------------------------------------------------------------------------------
iam/add:
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:${SERVICE_ACCOUNT} --role=roles/${ROLE}
project/create:
gcloud projects create ${PROJECT_ID} --name=${PROJECT_NAME} --organization=${ORG_ID}
billing/link:
gcloud beta billing projects link ${PROJECT_ID} --billing-account=${BILLING_ACCOUNT}
budgets/enable/api:
gcloud services enable billingbudgets.googleapis.com --project=${PROJECT_ID}
budgets/create:
gcloud alpha billing budgets create \
--project=${PROJECT_ID} \
--billing-project=${PROJECT_ID} \
--billing-account=${BILLING_ACCOUNT} \
--filter-projects=projects/${PROJECT_ID} \
--display-name=${PROJECT_ID} \
--budget-amount=10000 \ # 適宜修正
--threshold-rule=percent=1.00 \ # 適宜修正
--threshold-rule=percent=0.90 \
--threshold-rule=percent=0.50 \
--threshold-rule=percent=0.30 \
--threshold-rule=percent=0.10 \
--quiet
service_account/create/terraform:
gcloud iam service-accounts create terraform \
--display-name="terraform" \
--project=${PROJECT_ID}
service_account/compact_output:
cat ${SERVICE_ACCOUNT_KET_DIR}/service-account.json | jq -c > ${SERVICE_ACCOUNT_KET_DIR}/service-account.compact-output.json
service_account/create/key:
mkdir -p ${SERVICE_ACCOUNT_KET_DIR} \
&& gcloud iam service-accounts keys create ${SERVICE_ACCOUNT_KET_DIR}/service-account.json --iam-account=${SERVICE_ACCOUNT} \
&& make service_account/compact_output
#-----------------------------------------------------------------------------------------------------------------------
iam/add/all:
@make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=serviceusage.serviceUsageAdmin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=servicemanagement.admin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=iam.securityAdmin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=firebase.admin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=appengine.appAdmin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=iam.serviceAccountUser \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=cloudbuild.builds.editor \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=storage.objectAdmin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=appengine.appCreator \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=storage.admin \
&& make iam/add SERVICE_ACCOUNT=${SERVICE_ACCOUNT} ROLE=iam.workloadIdentityPoolAdmin
init:
@make project/create \
&& make billing/link \
&& make budgets/enable/api \
&& make budgets/create \
&& make service_account/create/terraform \
&& make iam/add/all \
&& make service_account/create/key
/project_1/env_1/Makefile
init:
cd ../.. && make init
main.tf と variables.tfの用意
/project_1/env_1/main.tf
terraform {
required_version = "~> 1.7.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.12.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 5.12.0"
}
}
}
locals {
api_services = ["cloudtasks.googleapis.com"]
organization_id = var.organization_id
project_id = var.project_id
region = "asia-northeast1"
editors = var.editors
hosting_names = var.hosting_names
firestore_backup_buckets = [{
bucket_name = "firestore-backups"
export_platform = "cloud_run"
}]
storage_buckets = [
{ bucket_name = "user-icons" },
]
}
provider "google-beta" {
project = local.project_id
billing_project = local.project_id
user_project_override = true
region = local.region
}
provider "google" {
project = local.project_id
region = local.region
}
module "initial" {
source = "cilly-yllic/firebase-project-factory/google"
version = "1.1.0"
api_services = local.api_services
editors = local.editors
firestore_backup_buckets = local.firestore_backup_buckets
hosting_names = local.hosting_names
organization_id = local.organization_id
project_id = local.project_id
region = local.region
storage_buckets = local.storage_buckets
}
variable "organization_id" {
description = "GCP organizationId."
type = string
}
variable "project_id" {
description = "Firebase project id"
type = string
}
variable "editors" {
description = "Firebase project Development member's emails."
type = list(string)
default = []
}
variable "hosting_names" {
description = "Firebase project Hosting names."
type = list(string)
default = []
}
全プロジェクト用modulesディレクトリの廃止理由
- modules内の処理を修正したら全てのプロジェクトのpaln(&apply)処理が走ってしまう。
- terraform registryであればバージョンで管理しているので、プロジェクト毎にバージョン指定を変更すれば影響を受けない
自作moduleの内容
気になる方はREADME.mdを見ていただくと大体わかるとは思いますが、まだ未熟なため、READMEの内容が完全ではないです。。。
なので興味を持っていただける方がいらっしゃるかもしれないので、一応説明だけさせていただきます。
- 必要なAPIの有効化
-
api_services
に追加することでデフォルトで有効化される処理に追加され同様に有効化されます。
-
デフォルトで有効化されるもの
- cloudbilling.googleapis.com
- cloudresourcemanager.googleapis.com
- identitytoolkit.googleapis.com
- firebase.googleapis.com
- appengine.googleapis.com
- firebasestorage.googleapis.com
- firestore.googleapis.com
- cloudfunctions.googleapis.com
- cloudbuild.googleapis.com
- artifactregistry.googleapis.com
- eventarc.googleapis.com
- cloudscheduler.googleapis.com
- run.googleapis.com
- Firestore(データベース)を作成します
- デフォルトで作成されるStorage Bucketを作成します
-
"${var.project}.appspot.com"
という感じで作成
-
- 編集者の追加
Firebase Consoleを利用したり、実際に開発する人に以下の権限を付与
- roles/editor
- roles/cloudfunctions.admin
- roles/artifactregistry.reader
下二つはfunctionのデプロイ時にこれがないとエラーがでる
- Web用のアプリの追加とHostingのサイトを作成
- Firestoreのデータをバックアップする用のStorage Bucketを作成します
- 上記Storage Bucket以外のバケットを作成します。
Terraform CloudにWorkspaceを作成し環境変数を設定しRunする。
- Terraform Working Directoryの設定を
{project}/{env}
(対象のmain.tfファイルまでのパス)を設定 - Version Controlの
VCS Triggers
でパターン登録
- Workspace variablesの登録
最低限以上の設定をしてRunをすれば無事plan & applyが通るかと思います。無事Firebase Consoleでプロジェクトが確認できれば完了です。
まとめ
以上一旦基盤を作ってしまえば10分かかるかかからないかくらいでFirebase Consoleのページが開けるようになるので、productionとstaging環境が必要になった場合にそこまでストレスなく作成できるかと思います。
Firebase Consoleで作成するのが一番早いかもしれないですが、同じサービスで環境毎に本当に同じ状況が整っているか不安になるリスクを考えればTerraformなどのlaCを利用してコードベースで管理できる方が安心感があるかと思います。
Discussion