🐙

Google Cloud プロジェクト登録を Google Forms & Terraform によって自動化

2023/06/13に公開

はじめに

こんにちは。クラウドエース株式会社で SRE をしている間瀬です。

今回は Google Forms で入力した内容に沿って Google Cloud のプロジェクト払い出し運用を自動化する際の設計ポイントについて紹介したいと思います。

プロジェクト登録の自動化について

Google Cloud 上でのリソース管理単位となるプロジェクトですが、サービスや機能単位でプロジェクトを分割するケースが多いかと思います。しかし、会社組織内でクラウド利用が拡大することに伴い、プロジェクトの登録作業や権限の管理、組織リソース内でのプロジェクト作成を強制するといった運用やセキュリティ観点での課題が出てきます。

本記事ではこれら課題を解決するために Google Forms , Google App Script , GitHub Actions , Terraform を利用したプロジェクトの登録プロセスを自動化する構成について紹介します。また、今回の構成の前提として共有 VPC (※) を利用しているケースを仮定して、ホストプロジェクトが登録されている状態に対してプロジェクトの登録運用の際に VPC ネットワークサブネットの払い出しを行いサービスプロジェクトとして登録する構成にしています。

(※)共有 VPC は一つのプロジェクト上の VPC リソースを複数プロジェクトで共有することができるサービスです。Cloud Innterconnect によるオンプレミスとの通信を複数プロジェクトで行いたいケースや、ネットワークの管理をプロジェクト個別で行うのではなく、ネットワーク担当で統合管理したいといったケースにおいて利用することが多いです。また、共有 VPC の文脈では、 VPC を持つプロジェクトをホストプロジェクトと呼び、ネットワークを共有される側のプロジェクトをサービスプロジェクトと呼びます。

以下は紹介する構成の概要図になります。

architecture

プロジェクト登録までの流れ

  1. Google Forms へプロジェクト情報を入力

    • プロジェクト ID 、担当部署、プロジェクト担当者といった情報を入力します。
  2. Google Apps Scriptがフォーム投稿をトリガーに起動されてフォームの入力情報を GitHub Actions へ連携

  3. GitHub Actions にてプロジェクトを登録するための変数ファイルを作成して Pull Request(以下、PR) を作成

  4. ネットワーク担当者がサブネットの設計を行い、変数ファイルへサブネットを反映

    • 自動でサブネットを払い出すことも不可能ではないですが、プロジェクトによって必要な IP アドレス数、セカンダリ範囲の要否などが異なるため個別に設定する前提としています。
    • 今回は 3.にて作成された PR を確認してマージを行うものとしています。
    • 図中では表現していないですが、 PR のマージによって更に GitHub Actions がトリガーされ新たなPRが作成されます。
  5. プロジェクトリソースの管理者にて PR をマージ

    • PR マージをトリガーに GitHub Actions が起動され Terraform によって Google Cloud へ反映が行われます。

パイプライン詳細

上図のパイプラインの詳細について解説させていただきます。
パイプラインのプロセスは以下の通りです。

pipeline

設計ポイント

プロジェクトファイル、インプットファイルのマージについて

  • 一見、インプットファイルのみにしてプロジェクト情報を追加していく運用も問題なさそうに見えますが、 landing ブランチへのマージ前に複数のプロジェクト作成依頼を受けた際に一つのファイルを複数ブランチにて編集してしまうとコンフリクトが発生するため、プロジェクト別にファイルを分けて landing ブランチへのマージ時にインプットファイルをマージするようにしています。
  • インプットファイルを一つにしているのは Terraform に変数として渡す際に一つの変数に対してプロジェクト情報を JSON 配列で渡すことが理由です。
    また、ファイルをわざわざ作成している理由は担当者が手動で修正を行いたい際に作業端末からも Terraform コマンドが実行しやすいように作成しています。GitHub Actions で Terraform コマンドを実行する際に各ファイルを集約して変数として与えることでパイプラインはもっとシンプルにはなりますが、手動運用が必要な際に扱いづらくなるデメリットがあります。

GitHub Actions から Google Cloud リソースを編集する権限について

  • GitHub Actions から Google Cloud リソースを編集するためには GitHub Actions に対して権限を与える必要があります。従来、Google Cloud にてサービスアカウントキーを作成して GitHub に与える必要がありましたが、Workload Identity という Google Cloud の機能を利用することで GitHub のような外部のリソースに対してキーを与えることなく権限を利用させることが可能なのでこの方法を採用します。
    本機能の詳細については弊社から別の記事を公開しているため、参照してください。

https://zenn.dev/cloud_ace/articles/7fe428ac4f25c8

Google Cloud の組織階層

  • 今回紹介する構成では以下の図のような組織階層を前提としています。ホストプロジェクト含めて Terraform で管理しており、本パイプラインによる更新によって部署別ファルダ内のプロジェクトが追加されるようになっています。

hierarchy

Terraform のディレクトリ構成

  • 今回紹介する構成では以下の通りとしています。組織階層に合わせて dev/prd/parent(terraform-state) を単位に state ファイルを分けるように構成しています。terraform-state で管理することは必須ではありませんが、GitHub Actions が利用するサービスアカウントや Workload Identity を当プロジェクトの state にて管理しています。
  • hierarchy フォルダにはプロジェクト、部署別のファイルが格納されており、これらをマージしたインプットファイルを shared フォルダに input.json という名称で格納しています。
project-sharedvpc-auto-pb
  .github/
   - (GitHub Actions を定義したyamlファイル)
  hierarchy/
   - departsments/
     - (部署別フォルダ情報を定義したjsonファイル)
   - projects/
     - (プロジェクト別情報を定義したjsonファイル)
  terraform/
   - environments/
     - dev/
       - (.tfファイル)
     - prd/ (請求先アカウントに紐づけられる数が枯渇するので、今回の検証では作成対象外としています。)
     - parent/
       - (.tfファイル)
     - shared/
       - input.json
       - template/
         - (input.jsonのテンプレートファイル)

動作確認

Google Forms からの作成依頼

今回は以下のようなフォームを用意していますが、利用するケースによって項目を追加いただくことも可能です。例えば今回のように共有 VPC 構成でネットワーク設計を行う必要がある場合は必要なアドレス数やセカンダリ範囲の必要性も記入できるようにすると良いかもしれません。

googleform

Google Apps Script

Google Forms の投稿をトリガーに Google Apps Script が起動されるようにトリガーを設定しておきます。以下の図のように Google Forms 編集画面の右上のメニュからスクリプトエディタを開いて、スクリプトエディタ画面の左のメニュからトリガーを選択してトリガー作成時にイベントソースを「フォームから」としてイベントの種類を「フォーム送信時」とすることで設定が可能です。

  • Google Apps Script のコード内容は公開しているので参照してください。
    Google Apps Script
  • Google Apps Script から GitHub Actions を起動するためには GitHub にて Apps を作成して該当リポジトリに対してインストールする必要や秘密鍵を作成する必要があります。この辺りの情報を Google Apps Script のスクリプトプロパティとして設定します。

https://docs.github.com/ja/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps

https://docs.github.com/ja/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps

  • Google Forms の編集画面
    googleform-apps

  • Google Apps Script のトリガー作成画面
    apps-script

GitHub Actions ( create-hierarchy )

GitHub Actions が起動することによって、以下のようなプロジェクトファイルが作成されPRが作成されます。

GitHub Actions

{
  "name": "mase-t-web-taky",
  "project_id": "mase-t-web-taky",
  "department": "infosys",
  "host_project_name": "masem-cmn-nw",
  "folder_name": "mase-t-web",
  "assign_roles_members": [
    {
      "role": "roles/owner",
      "members": [
        "user:xxxxx@example.com"
      ]
    }
  ],
  "billing_account": "",
  "labels": {
    "managed-by": "autoprocess"
  },
  "network": {
    "subnet_name": "mase-t-web-taky",
    "subnet_cidr_range": "",
    "secondary_ip_ranges": [],
    "assign_nw_user_members": [
      "user:xxxxx@example.com"
    ]
  }
}

GitHub Actions ( merge-hierarchy )

GitHub Actions が起動することによって、以下のような input.json が作成され PR が作成されます。

GitHub Actions

{
  "departments": [
      <!-- 部署別のフォルダを定義するJSON 記載割愛 -->
  ],
  "service_projects": [
    {
      <!-- その他プロジェクトを定義するJSON 記載割愛 -->
    },
    {
      "name": "mase-t-web-xxxx",
      "project_id": "mase-t-web-xxxx",
      "department": "infosys",
      "host_project_name": "masem-cmn-xxxx",
      "folder_name": "mase-t-web",
      "assign_roles_members": [
        {
          "role": "roles/owner",
          "members": [
            "user:xxxxx@example.com"
          ]
        }
      ],
      "billing_account": "xxxxxxxxx",
      "labels": {
        "managed-by": "autoprocess"
      },
      "network": {
        "subnet_name": "mase-t-web-xxxx",
        "subnet_cidr_range": "192.168.8.0/24",
        "secondary_ip_ranges": [],
        "assign_nw_user_members": [
          "user:xxxxx@example.com"
        ]
      }
    },
    {
      <!-- その他プロジェクトを定義するJSON 記載割愛 -->
    }
  ]
}

GitHub Actions ( deploy-hierarchy )

GitHub Actions が起動することによって、Terraform Apply を行なわれ Google Cloud にリソースが作成されます。
余談ですが、 setup-terraform を利用することでPRリクエストをマージする前に terraform plan の結果を確認できるようにしています。

https://github.com/hashicorp/setup-terraform

GitHub Actions

 <!-- 一部記載割愛 -->

Terraform will perform the following actions:

  # module.service_projects["mase-t-web-xxxx-develop"].google_compute_subnetwork.main will be created
  + resource "google_compute_subnetwork" "main" {
      + creation_timestamp         = (known after apply)
      + external_ipv6_prefix       = (known after apply)
      + fingerprint                = (known after apply)
      + gateway_address            = (known after apply)
      + id                         = (known after apply)
      + ip_cidr_range              = "192.168.8.0/24"
      + ipv6_cidr_range            = (known after apply)
      + name                       = "mase-t-web-xxxx"
      + network                    = "projects/xxxxxxxxx/global/networks/shared-network"
      + private_ip_google_access   = true
      + private_ipv6_google_access = (known after apply)
      + project                    = "masem-cmn-xxxx-develop"
      + purpose                    = (known after apply)
      + region                     = "asia-northeast1"
      + secondary_ip_range         = (known after apply)
      + self_link                  = (known after apply)
      + stack_type                 = (known after apply)
    }

  # module.service_projects["mase-t-web-xxxx-develop"].google_compute_subnetwork_iam_binding.main will be created
  + resource "google_compute_subnetwork_iam_binding" "main" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + members    = [
          + "user:xxxxx@example.com",
        ]
      + project    = "mase-t-web-xxxx-develop"
      + region     = "asia-northeast1"
      + role       = "roles/compute.networkUser"
      + subnetwork = "mase-t-web-xxxx"
    }

  # module.service_projects["mase-t-web-xxxx-develop"].google_folder.main will be created
  + resource "google_folder" "main" {
      + create_time     = (known after apply)
      + display_name    = "mase-t-web"
      + folder_id       = (known after apply)
      + id              = (known after apply)
      + lifecycle_state = (known after apply)
      + name            = (known after apply)
      + parent          = "folders/xxxxxxxx"
    }

  # module.service_projects["mase-t-web-xxxx-develop"].google_folder_iam_binding.main["0"] will be created
  + resource "google_folder_iam_binding" "main" {
      + etag    = (known after apply)
      + folder  = (known after apply)
      + id      = (known after apply)
      + members = [
          + "user:xxxxx@example.com",
        ]
      + role    = "roles/owner"
    }

  # module.service_projects["mase-t-web-xxxx-develop"].google_project.main will be created
  + resource "google_project" "main" {
      + auto_create_network = false
      + billing_account     = "xxxxxxxxx"
      + folder_id           = (known after apply)
      + id                  = (known after apply)
      + labels              = {
          + "managed-by" = "autoprocess"
        }
      + name                = "mase-t-web-xxxx-develop"
      + number              = (known after apply)
      + project_id          = "mase-t-web-xxxx-develop"
      + skip_delete         = true
    }

  # module.shared_vpc["masem-cmn-nw-develop"].google_compute_shared_vpc_service_project.main["mase-t-web-xxxx-develop"] will be created
  + resource "google_compute_shared_vpc_service_project" "main" {
      + host_project    = "masem-cmn-xxxx-develop"
      + id              = (known after apply)
      + service_project = "mase-t-web-xxxx-develop"
    }

Plan: 6 to add, 0 to change, 0 to destroy.

<!-- 一部記載割愛 -->

今回検証した範囲以外で考慮すべきこと

組織ポリシー、VPC Service Controll の管理

プロジェクトの増加に伴って利用することの多い、組織ポリシーや VPC Service Controll も Terraform で管理することは可能なので、併せて管理の検討をしてください。

サービスプロジェクト別のリソース管理方法

今回紹介している Terraform を管理する担当者は組織の管理者やネットワーク管理者を想定しています。個別のサービスプロジェクトのリソースについてはプロジェクト別の担当者が管理することになるため、リポジトリや state ファイルは分けて管理することになるかと思います。

請求先アカウントの割り当て方法、タイミング

運用される組織によって請求先アカウントとプロジェクトの管理は異なってくるかと思います。管理ルールに従って自動化プロセスに取り込むか管理者がIDを付与して push するかといった整理が必要になるかと思います。

その他、プロジェクトの自動登録方法について

  • Terraform 主体での構成を紹介しましたが、他には Config Controller による登録を行うことも可能です。Google Kubernetes Engine クラスタを使用してリソース別のフォーマットに従って yaml ファイルを作成する必要がありますが、 GitOps となるためパイプラインは本構成と比較して非常にシンプルになります。
  • 今回の構成を検討する上でも参考とさせていただいた記事が公開されているため以下に共有させていただきます。

https://medium.com/google-cloud-jp/configcontroller-selfdeploymentsample-part1-f76117c1f6c3

まとめ

今回は利用者が Google Forms から 申請を行うと PR が自動作成されて、プロジェクト別に必要なネットワーク設計、承認を行うことでプロジェクトが自動作成される仕組み、設計時のポイントとなる点を紹介させていただきました。
もちろん、運用プロセスのあるべき姿はこれといった正解があるわけではなく、組織によって異なってきます。今回紹介したケースを例に運用を検討されている方の参考になれば幸いです。
今回検証する際に使用したコードは以下のリポジトリにて公開しています。記事を閲覧いただきありがとうございました。

GitHub Repository

Discussion