GitHubの権限とチームを整理してTerraform化した
こんにちは。SRE 兼 Development Infrastructure の komtaki です。
IVRy では、2024 年 4Q から開発者体験の整備の「Development Infrastructure」が発足しました。組織とプロダクトが急成長する成長する中で、CI/CD や各種開発基盤の整備は今後のプロダクト開発に大きく影響してきます。まだまだ人数が少ないので全員 SRE と兼務なのですが、CICD 以外にも AI コードエディターの Cursor の試験導入など、いろいろな改善が進んでいます。
その一環として GitHub の整理をしたので紹介します。
導入の背景
IVRy では、エンジニアの数も 30 人を超えてどんどん増えていて、目まぐるしい速さで組織が大きくなっています。
とはいえ去年まではまだまだ人数も少なく、各種開発ツールのアカウントの発行はエンジニア全体でやる運用でした。
その結果、対応する人によって微妙に付与される権限が違うなどの問題が発生しており、GitHub の運用フローをバッサリ作り直しました。
GitHub 運用の課題
1. owner たくさんいて権限バラバラ
GitHub のアカウントの付与や削除も気づいた人が対応するという運用だったため、エンジニアは一番強い権限を持っていました。
暗黙のルールはあるもののオペレーションが明文化されていなかったので、招待した人によって権限に差があり、下記のような特殊ケースが生まれていました。
- エンジニアなのに member になっている人
- 全員がはいるチームに入っていないけど、owner だから全リポジトリが見れている人
- 業務委託で Organization に入ってる人と Collaborator の入っていない人がいる
- 正社員から諸事情で業務委託になったから owner のままの人
結果、人によって見れるリポジトリが微妙に異なり、「同僚のエンジニアは見れるのに、なんで自分はこのリポジトリが見れないのか?」という相談が時々発生していました。
2. GitHub のアカウントが会社の誰かわからない
現時点では、GitHub Teams で契約しているため、個人の GitHub アカウントを使っています。そのため、ニックネームと本名がパッと見でわからない人もいます。
その結果、下記のような課題も出ていました。
- 誰かが退職した時に、そのアカウントがすぐにわからず困る。
- 付与されているアカウントや権限が適切なものかわからない。
GitHub 運用の 3 つの大方針
そこで運用を改善するため、3 つの方針をたてて整理を進めていきました。
- EM, SRE 以外、owner を剥がして member にする。owner がついている人は Organization の全てのリポジトリに対して admin 権限を持っているので、権限管理がなくなる。
- Terraform 化することで、レビューをして誰でもリポジトリの作成やメンバーの追加作業ができるようにする。
- 原則として、業務委託のメンバーのリポジトリごとの細かい権限管理はしない
方針決めで検討したポイント
業務委託のメンバーの権限
業務委託の仲間の権限をどうするか考えた時に、まず「Organization にいれるかどうか」が大きな分岐点で、会社によって判断が変わってくると思います。
Organization に入れると、チームに所属させるなど一括した管理ができます。入れない場合、collaborators としてリポジトリごとに権限をつける必要があり、人数が増えた時に権限管理が大変になります。
弊社の場合、認証情報などの機密情報がリポジトリに入っていないことが確認できたこと。また、社内ツールを参考に情シスに相談したら契約形態ではなく、業務内容で権限を管理しているものが多いことがわかりました。そのため、1 つ 1 つのリポジトリに権限をつけるのではなく、Organizaion に入れてチームで管理する方針にしました。
Terraform
Terraform は、弊社ですでに AWS, GCP などのインフラの構成管理として全面的に活用しています。GitHub のプロバイダーも、Hashicorp の公式ではないですがコミュニティから提供されています。
owner を外すと、アカウントの追加削除は当然ながら、組織内にリポジトリが自由に作れなくなります。これらの作業を誰でも PR を作ればできるようにするために、Terraform 化を決めました。
Member privileges
「Member privileges」機能で、owner 以外でもリポジトリを作成可能にできるのですが、契約プランによって制限に違いがあります。弊社の場合、team プランであるため Public リポジトリも作成可能になってしまうため、採用を見送りました。
- Team プラン
- public, private の両方が作成可能
- Enterprise プラン
- private だけに限定可能
チームとリポジトリの設計
まず、全員のチームを解体し職種ごとのチームを作成しました。
ロール名 | メンバー |
---|---|
engineers | 正社員エンジニア |
designers | 正社員デザイナー |
product-managers | 正社員プロダクトマネージャー |
collaborators | 業務委託のメンバー |
ベースのアクセス権限はチームに沿って、オーガニゼーションロールで一括で付与します。
しかし下記のようなケースでは、リポジトリに直接権限をつける運用にしています。
- 業務委託で特定のリポジトリの管理を任せたい場合
- code owner のチームをつける
- 正社員はダイレクトに権限はつけない。原則チームを作成して対応する。
公式ベースの Terraform の実装
公式のチュートリアルを参考に実装しました。なので、csv にユーザーを 1 行追加するだけで、terraform を書かずに作業が完了します。
ただ 2 つはまりポイントがあったので共有します。
1. GitHub App を使用する場合、環境変数で認証情報がわたせない
CI で動かす場合、GitHub の認証は Personal Token か GitHub App になります。Personal Token だと退職リスクや漏洩リスクがあるので、GitHub App がおすすめです。
GitHub App の認証では、公式に下記のように環境変数が使えると書いてあるのですが、実際に実行すると terraform の validation でエラーになります。
provider "github" {
owner = var.github_organization
app_auth {} # When using `GITHUB_APP_XXX` environment variables
}
issueはあるのですが、修正される雰囲気はありません。そのため環境変数で渡したい場合はTF_github_app_id
などで定義して variables 経由で渡しましょう。
variable "github_app_id" {
description = "github app id"
type = string
ephemeral = true
}
variable "github_app_installation_id" {
description = "github app installation id"
type = string
ephemeral = true
}
variable "github_app_pem_file" {
description = "github app pem file"
type = string
ephemeral = true
}
provider "github" {
owner = local.organization_name
app_auth {
id = var.github_app_id
installation_id = var.github_app_installation_id
pem_file = var.github_app_pem_file
}
}
2. Organization role が owner のユーザーは、チームの権限が必ず maintainer になる
GitHub の仕様として、Organization role が owner のユーザーは、チームの権限が必ず maintainer になります。なので、うっかり member で定義してもバリデーションエラー等にはならず terraform apply は成功するのですが、毎回差分が出る挙動になります。
そのため、対象者の権限を見て、自動でチームの権限を決めるようなリファクタリングを追加しました。
resource "github_membership" "all" {
for_each = {
for member in csvdecode(file("${local.members_path}.csv")) :
member.username => member
}
downgrade_on_destroy = false
username = each.value.username
role = each.value.role
}
resource "github_team_membership" "members" {
for_each = { for tm in local.team_members : tm.name => tm }
team_id = github_team.all[each.value.team_slug].id
username = each.value.username
// チーム権限のroleはOrganization roleがadminの場合はmaintainerにする。それ以外はmemberにする。
role = lookup(lookup(github_membership.all, each.value.username, {}), "role", "member") == "admin" ? "maintainer" : "member"
}
実際に運用してみて
実際の GitHub アカウントの付与のオペレーションは下記の流れに変わりました。
- エンジニアやデザイナーが PR を作成
- SRE がレビュー
- SRE がシートを購入
- SRE が PR をマージ
Terraform では決済を伴う作業ができないので、terraform の apply の前にシートの購入が必要になので少し手間です。
422 You must purchase at least one more seat to add this user as a member. [{Resource: Field:user Code:no_seat Message:}]
それでもレビューができて履歴が残ること、権限を絞りつつ誰でも自由に変更提案ができるので、恩恵を感じています。
またリポジトリを作成するときに、よく使う共通設定をいれた状態でリポジトリを新規作成できるのは便利です。まだまだ調整中ですが、ブランチプロテクションルールや、PR マージの時のブランチの削除など一括で共通の設定が入れれば、設定の漏れが減ります。
まとめ
一番大変だったのは、IVRy としてどういう方針で権限を整理することが最適なのかコーポレイト IT や EM と議論しつつ会社として方針を決めることでした。方針が決まれば公式のサンプルに沿って、実装はスムーズに進めることができました。
さらに Terraform 化した日に、GitHub アカウントの追加の PR を作ってくれた同僚がいて、「この会社は、みんな積極的で本当にいい会社だな!」と思いました。
事業の成長に合わせて人数が増えていますが開発速度を落とさないように、より効率的な開発体験を求めて改善を進めています。他にも最近、負荷試験環境の整備や、Devin の試験導入が進んでいて、どんどんエンジニアの働き方が進化しています。
ぜひ興味がある方は、カジュアル面談でお話ししましょう。
Discussion