Terraform+CSV+GitHub ActionsでGitHub Organizationのメンバー管理をやってみた-①概要・利用方法編

Terraform+CSV+GitHub ActionsでGitHub Organizationのメンバー管理をやってみた-①概要・利用方法編

Clock Icon2025.03.24

こんにちは!クラウド事業本部の吉田です。

皆さん、GitHub Organization利用していますか?

GitHub Organizationを運用するにあたって、課題の1つとなってくることはメンバー管理だと思います。

GitHub Organizationでのメンバー管理に関するトラブルとして以下が考えられます。

  • Onwer権限を持つユーザーが少なく、メンバー管理の負荷が特定の人に集中する
    • 余裕がないタイミングで作業をするとミスをする可能性が高くなる
  • Owner権限を持つユーザーが大量にあり各々がメンバーを追加するためメンバー管理が難しくなる
    • 誰のものがわからないGitHubユーザーがある
    • Organizationに招待していいユーザーなのか精査できていない
  • 全ユーザーの権限管理がおざなりになる

このような問題に対応するためにTerraformによるIaC化がよく活用されています。

他社の事例:

当記事はTerraform+CSV+GitHub Actionsを利用して、GitHub Organizationのメンバー管理をする方法を紹介させていただきます。

内容が多いため、記事を分割しております。
当記事は仕組みの概要と利用方法を解説しております。
この仕組みを導入する際は、当記事で大まかな内容を把握した後、初期構築の記事を参照し構築してください。
また、詳細な仕組みに関しても別記事でまとめます。

記事リンク

サンプルコード

サンプルコードは、下記のリポジトリで公開しております。
https://github.com/yoshida-takeshi-classmethod/sample-gh-org

OrganizationにOrganization管理用のリポジトリを作成し、サンプルコードを展開してください。

READMEに記載しています通り、GitHub Appsトークン生成スクリプトとGitHub Actionsワークフロー上のGitHub Appsトークン生成に関するステップは下記の記事から引用しております。

GitHub Appsトークン解体新書:GitHub ActionsからPATを駆逐する技術

サンプルコードのディレクトリ/ファイル構成

.
├── .github/
│   └── workflows/ #GitHub Actionsのワークフローファイルの格納フォルダ
│       ├── apply.yml # Pull Requestマージ時に実行されるterraform apply用ワークフローファイル
│       └── plan.yml # Pull Request時に実行されるterraform plan用ワークフローファイル
├── shell/
│   └── script.sh # GitHub Appsトークン生成用シェルスクリプト
├── terraform/ #tfファイル格納用フォルダ
│   ├── module/
│   │   └── terraform-github-organization/ # Organizationのメンバー管理用モジュールフォルダ
│   │       ├── main.tf # モジュールのメインtfファイル
│   │       └── variables.tf # モジュールの変数用tfファイル
│   ├── .terraform.lock.hcl # terraform init時に自動生成されるプロバイダロックファイル
│   ├── organization.tf # モジュールに渡す引数を定義するtfファイル。チーム追加時などに更新するファイル
│   ├── README.md # organization.tfの内容を説明したREADMEファイル
│   └── versions.tf # terraform version、プロパイダー、バックエンドを指定するファイル
├── users/ #メンバー管理用のCSVファイルを格納するフォルダ
│   └── *.csv
├── .gitignore
└── README.md

概要・機能

GitHubリソースを管理するためのGitHub Providerがあります。
このGitHub Providerを利用して、Organization・チームのメンバー管理を行います。
Organization・チームのメンバー情報はCSV、チーム情報はtfファイルで管理します。
CSVにはGitHubのアカウント名と合わせてメールアドレスを記載することで、GitHubアカウントの個人を特定しやすくします。
主要なファイルの関連性は以下の通りです。

処理の流れ.png

新しくチームを作成したりメンバーを追加する際に、gitのブランチを切ってファイルを更新した後Pull Requestを作成します。
GitHub ActionsによってPull Request作成時にterraform plan、Pull Requestマージ時にterraform applyが実行されます。
terraform planterraform apply実行時はGitHub Appsトークンの権限を利用します。
(詳細はTerraform+CSV+GitHub ActionsでGitHub Organizationのメンバー管理をやってみた-③仕組み編で説明します。)
つまり、実際のOrganization・チームのメンバー管理の処理はGitHub Appsトークンの権限を利用するので、Organization管理用のリポジトリへのwrite権限があればOwner以外のメンバーもメンバー管理作業(CSVファイル更新など)ができます。
例えば、チームリーダー用のチームを作成し、そのチームにリポジトリのwrite権限を渡すことで各チームのメンバー管理作業をチームリーダーに委任することができます。
管理者は承認作業に徹することができるので、管理者のメンバー管理の負担を軽減することができます。

プルリクマージの流れ.png

この仕組みを利用することで、実現できる機能としては以下の通りです。

  • Organizationのメンバー管理
    • ロール管理(Onwer/Member)
  • チームの新規作成
  • チームのメンバー管理
    • ロール管理(Memtainer/Member)
    • 手動作成の既存チームのメンバー管理も可能
  • Pull Requestによる承認フロー

全体の流れ

  • トピックブランチを作成
# main へ switch
$ git switch main
# リモートの main ブランチをローカルに取り込み
$ git pull
# トピックブランチを作成
$ git switch -c <ブランチ名>
# 変更をステージングに追加
$ git add .
# 変更をコミット
$ git commit -m "<Commit message>"
# ローカルブランチをリモートへ Push
$ git push origin <ブランチ名>
  • GitHub上でPull Requestを作成
  • (随時)Pull Requestのコメントにterraform validateによるエラーが出力されたら、内容を確認してterraformコードを修正
  • Pull Requestのコメントに出力されるterraform planの実行結果を確認し意図せぬ変更がないか確認。詳細な内容は、後述のPull Request時のterraform plan出力内容を参照。
  • レビュアーがGitHub上でPull Requestをレビュー&マージ
    • 下記のファイルが更新されている可能性があります
      • .terraform.lock.hcl
        • ワークフロー上のterraform initによる更新
      • organization.tf
        • ワークフロー上のterraform fmtによるフォーマット
  • Pull Requestのコメントに出力されるterraform applyの実行結果を確認しエラーがないか確認。詳細な内容はPull Requestマージ時のterraform apply出力内容を参照。

TerraformコードまたはCSVファイルの編集箇所

各パターンで、ファイルの更新する箇所が変わります。
更新する主要なファイルは下記の通りです。

  • userフォルダ配下のCSVファイル
    • 新規チーム作成時などは新規CSVファイルを作成してください
  • terraformフォルダ配下のorganization.tf

Organization

Ownerロールのメンバー管理

  • Owner用のCSVファイルをusersフォルダ配下に作成
    • CSVファイルの名前にこだわりがなければ、サンプルコードと同じowners.csvを利用してください。
    • CSVファイルの1行目は必ず「username,email」としてください。
username,email
owner-user,owner-user@owner.jp
  • terraform/organization.tf
    • 変数名・CSVファイルの名前にこだわりがなければ更新不要です。更新する場合は、下記の箇所を更新してください。
      • localsブロック
      • moduleブロックのownersキー
locals {
 ------更新-------
  <Owner用変数> = csvdecode(file("../users/Owner用CSV名"))
  ----------------
}

module "organization" {
(省略)
  ------更新-------
  owners  = local.<Owner用変数>[*].username
  ----------------
}

Memberロールのメンバー管理

  • Member用のCSVファイルをusersフォルダ配下に作成
    • CSVファイルの名前にこだわりがなければ、サンプルコードと同じmembers.csvを利用してください。
    • CSVファイルの1行目は必ず「username,email」としてください。
username,email
member-user,member-user@member.jp
  • terraform/organization.tf
    • 変数名・CSVファイルの名前にこだわりがなければ更新不要です。更新する場合は、下記の箇所を更新してください。
      • localsブロック
      • moduleブロックのmembersキー
locals {
 ------更新-------
  <Member用変数> = csvdecode(file("../users/Member用CSV名"))
  ----------------
}

module "organization" {
(省略)
  ------更新-------
  members  = local.<Member用変数>[*].username
  ----------------
}

チーム

新規チーム作成

  • 新規チーム用のCSVファイルをusersフォルダ配下に作成
    • CSVファイルの1行目は必ず「username,email」としてください。
username,email
test-user,test-user@test.jp
  • terraform/organization.tfの下記の箇所に既存チーム用の設定を追記
    • localsブロック
    • moduleブロックのteamsオブジェクト
      • 新規チーム作成時のみnameキーの値に「大文字・スペース」が利用できます。(例:Test Team)
locals {
 ------追加-------
  <新規チーム用変数> = csvdecode(file("../users/<新規チーム用CSV名>"))
  ----------------
}

module "organization" {
(省略)
  teams = [
    ------追加-------
    {
      name    = "<新規チーム名>"
      members = local.<新規チーム用変数>[*].username
    }
    ----------------
  ]
}

手動で作成した既存チームのメンバー管理

  • 手動で作成した既存チーム用のCSVファイルをusersフォルダ配下に作成
    • CSVファイルの1行目は必ず「username,email」としてください
    • Teamに既に参加しているメンバーの情報をCSVを記載することで、terraformによる管理対象にすることができます
username,email
exsit-user,exsit-user@exsit.jp
  • terraform/organization.tfの下記の箇所に既存チーム用の設定を追記・更新
    • localsブロック
    • moduleブロックのexisting_teamsキー
      • 手動で作成した既存チーム情報を取得するために必ずチーム名は slug形式 で指定してください
      • slug形式のチーム名は、チームのURLから確認してください
    • moduleブロックのteamsオブジェクト
      • こちらのnameキーの値も、 slug形式 のチーム名で指定してください
locals {
 ------追加-------
  <既存チーム用変数> = csvdecode(file("../users/<既存チーム用CSV名>"))
  ----------------
}

module "organization" {
(省略)
  ------更新-------
  existing_teams = ["<既存チーム名>"]
  ----------------

  teams = [
    ------追加-------
    {
      name    = "<既存チーム名>"
      members = local.<既存チーム用変数>[*].username
    }
    ----------------
  ]
}

メンバーの追加・削除

メンバーを追加する場合は、CSVに追加するメンバーの情報を追加してください。
メンバーを削除する場合は、CSVから削除するメンバーの情報を削除してください。

チーム削除(Terraformで作成したチームのみ)

terraform/organization.tfから削除対象のチームに関する設定を削除してください。
また、削除対象のチーム用のCSVファイルも削除してください。
手動で作成した既存チームに関しては、terraform/organization.tfから設定を削除しても、削除されません

留意事項

  • GitHubアカウントの個人を特定するために、CSVにユーザー名だけでなくメールアドレスも記載するようにしております。
  • terraform/organization.tfのmoduleブロックのteamsオブジェクトでは、membersキー以外にもmaintainersキーを利用することでMaintainer(チームロール)を割り当てたいユーザーを指定できます。ただし、チームメンバー管理はterraformで管理するため、基本的にはmaintainersキーは利用しなくても問題ありません。
    • ただしOwner(組織ロール)権限のメンバーをmembersキーで指定しますと、チーム追加時に自動的にMaintainer(チームロール)が付与されます。チームロールの差分が発生するため、Owner(組織ロール)権限のメンバーをTeamに参加させる場合はmaintainersキーを利用してください。

Pull Request

ファイル更新後はPull Requestを作成します。
下記のキャプチャのように、GitHub Actionsのワークフローによってterraform planterraform applyが実行された際、botが出力内容をコメントします。

スクリーンショット 2025-03-06 18.34.39.png

Pull Request時のterraform plan出力内容

Pull Requestを作成後、Pull Requestにterraform planの出力内容がコメントされます。
出力される内容は主に下記の通りです。

  • チーム作成・削除時
    • module.organization. github_team .main["チーム名"] will be created/destroyed
  • チームにメンバーを追加・削除時
    • module.organization. github_team_membership .main["チーム名" "アカウント名"] will be created/destroyed

出力例

  • 「Test Team」作成
  # module.organization.github_team.main["Test Team"] will be created
  + resource "github_team" "main" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + name    = "Test Team"
      + node_id = (known after apply)
      + privacy = "closed"
      + slug    = (known after apply)
    }
  • 「Test Team」に「test-user」を追加
  # module.organization.github_team_membership.main["Test Team test-user"] will be created
  + resource "github_team_membership" "main" {
      + etag     = (known after apply)
      + id       = (known after apply)
      + role     = "member"
      + team_id  = (known after apply)
      + username = "test-user"
    }

terraform planの内容を確認し、問題がなければマージしてください。

Pull Requestマージ時のterraform apply出力内容

Pull Requestマージ後、Pull Requestにterraform applyの出力内容がコメントされます。
コメントの最後に「Apply complete!」と出力されていることを確認してください。

出力例

Terraform will perform the following actions:

  # module.organization.github_team_membership.main["Test Team test-user"] will be created
  + resource "github_team_membership" "main" {
      + etag     = (known after apply)
      + id       = (known after apply)
      + role     = "member"
      + team_id  = (known after apply)
      + username = "test-user"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
module.organization.github_team_membership.main["Test Team test-user"]: Creating...
module.organization.github_team_membership.main["Test Team test-user"]: Creation complete after 2s [id=12118962:test-user]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

最後に

当記事で紹介した仕組みは、私が所属する事業部のGitHub Organizationのメンバー管理の仕組みを元としており、いくつかのアップデートを加えた形となります。
元の仕組みを構築していただいた方に感謝の気持ちでいっぱいです。

記事はまだ続きますので、続きの記事も読んでいただけると嬉しいです!

以上、クラウド事業本部の吉田でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.