Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

プロジェクトを増減させやすいtfactionプロジェクトを書きたい

私はTerraformを使うときにtfactionを利用しています。tfactionはGitHub ActionsとS3やGCSのような永続層だけでTerraformをいい感じに管理できるのでオススメです。

suzuki-shunsuke.github.io

ところでプロジェクトの増減はTerraformの難しいところだと思っていて、OIDCで認証するサービスアカウントの作り方とか権限の設定とか、新規に作成したプロジェクトの初期化や設定がなかなかTerraform単体では完結できなかったりします。手順書を作っておいて全部手動でやるのもいいのですが、できるだけTerraformで記述する方法を考えてみました。

前提となるプロジェクト構成

以下、特に断りのない場合、Google Cloudを前提としています。

プロジェクトは環境(staging, productionなど)ごとに1つ作成します。例外としてArtifact RegistryやCloud DNSを管理する共有プロジェクト(shared)がひとつあります。これはstagingやproductionのサービスアカウントがsharedのArtifact Registryに対して読み書きする権限が必要ということで、権限管理のキモのひとつです。

flowchart TD
  staging --> shared
  production --> shared
  shared

GitHub ActionsからのログインにはOIDCを利用します。これはWIP(Workflow Identity Pool)が早い段階で必要になることを意味しています。ブランチ戦略に従ってWIP Providerのattribute conditionを設定する必要があるので、たとえばproductionにはmainブランチからしかデプロイしない、といったルールは先に決めておきます。

プロジェクトの作成フローを定めておく

プロジェクト作成の大まかなフローは次のようになります。moduleはこれらの複数のステップに跨ることがないように作成しておくと良さそうです:

  • プロジェクト作成(scaffold workdir
  • 課金の有効化(手動)
  • 各種サービスの有効化(google_project_service
  • terraform plan用サービスアカウント, terraform apply用サービスアカウント, WIPなど作成(モジュール)
  • terraform plan用サービスアカウントとterraform apply用サービスアカウントがGCSバケットにアクセスできるようにする(同上)
  • terraform plan用サービスアカウントとterraform apply用サービスアカウントの切り替え(workdirのtfaction.yaml更新)

ここまではプロジェクトの作成や他のプロジェクトに属するサービスアカウントの作成ができる、強めの権限を持つサービスアカウントを使います。ここからは他のプロジェクトには影響できないサービスアカウントだけで済むはずです。以下のような手順になります:

  • firebaseプロジェクトの作成(手動)
  • サービスアカウントの作成と権限設定(他プロジェクトにあるArtifact Registryなどを含む)
  • リソースの作成
  • notification channelの作成(手動)

サービスアカウントの作成とリソースの作成は同じmoduleでやって大丈夫ですが、他プロジェクトにあるリソースのIAM設定を考えると分けておいても良さそうです。Cloud RunがArtifact Registryに接続できなくてterraform applyに失敗した、とかはよくやります。

workdirの作り方

workdirは少なくとも3種類を持つようにします。これはわかりやすさはもちろん、 terraform apply 等を実行するサービスアカウントの権限を単純にするためにも有効です:

  • プロジェクトやフォルダを作成・管理するworkdir
    • google_folder
    • google_project
    • google_app_engine_application
    • google_project_service
  • 共有プロジェクト内のリソースを管理するworkdir
    • google_artifact_registry_repository
    • google_storage_bucket
    • google_dns_managed_zone
  • 各プロジェクト内のリソースを管理するworkdir

プロジェクトやフォルダを作成・管理するworkdirは main.tf 内に直接処理を書きますが、他のworkdirは適宜moduleに処理を委譲するような書き方が良さそうです。共有プロジェクト内のリソースを管理するworkdirでは、接続を許可するプロジェクトのIDを locals.tf に定義して使うようにすると、プロジェクト増減時の手順が簡素化されます。

moduleの作り方

「各プロジェクト内のリソースを管理するworkdir」の処理はmoduleとして実装しておくことで、workdirに似たような処理を書いて回る手間がなくなります。tfaction 1.3.0からローカルモジュールの扱いがより便利になったので、ローカルモジュールを使う上でのハードルも下がってます。

suzuki-shunsuke.github.io

「各プロジェクト内のリソースを管理するworkdir」が使うモジュールは先ほど整理した作成フローをもとに、以下の種類に分けられます:

  • 各種サービスの有効化(プロジェクトを作成して課金を有効にした後で行う)
  • terraform-plan用サービスアカウント, terraform-apply用サービスアカウント, WIPなど作成(これは他のworkdirでも使える)
  • リソースの作成

ひとつのモジュールが複数のステップを担っても問題ありませんが、前提ステップが終わるまで後続ステップを進めないことを確実にできるように、リソース間依存やvariableを使う必要はありそうです。

まとめ

サービス運用中はこういった観点を意識できてなかったりして、いざ環境を増やすぞというときに苦労するということが自分は多かったので、しばらく今回まとめたmoduleやworkdirの書き方を意識してみます。