GitHub Organization の設定情報取得スクリプトを作成して検証
概要
GitHub Organizationの管理をしていると、多くの設定項目があります。例えば、リポジトリや招待されているメンバーや、Github Appsなどです。それら以外も多数あります。設定変更を主にGUI(管理画面)で行なっていましたが、いつ・どのような設定変更をしたのか、不明確になる可能性があるのが課題と感じています。Terraformなどを利用して管理する方法もあるかもしれませんが、まず最初は、手軽に設定情報をAPIで取得するスクリプトを作成し、設定情報の取得を試みました。
今回は検証目的で、全ての設定情報取得はできていませんが、確認した内容など記載します。
前提
- 今回は、本番環境でなく、テスト環境で検証目的で行なっています
- GitHub Organizationは、無償プランとTeamプランで試しています
- スクリプトは、Claude Code(Opus 4.7)を利用し、Pythonで作成しています。実行はMac端末で行なっています
- 認証は Fine-grained personal access tokens (Read Only)のみで行なっています
- 網羅的な取得はできていません(APIの仕様や、Fine-grained personal access tokensのみ利用している点などの影響もあります)
取得できたデータの概要
Organizationの基本的な情報や、メンバー情報、リポジトリ情報など、概ねは取得できました。
但し、視認性はあまり良くありませんでした。
また、細かい部分ですが、以下の点を注目していたので、確認結果を記載します。
-
Two-factor authentication(2要素認証)の必須化の設定有無:
セキュリティ上重要なため、確認したかった項目ですが、取得できました。(GET /orgs/{org})に、含まれていました。 -
Github Appsの一覧情報が取得できるか:
「いつ」・「誰が」・「どのGithub Appsをインストール(リクエスト)したのか」 が、管理画面のみで把握するのが難しかっため取得できればと思っていました。Org内にインストールされたGithub Appsの一覧は取得できました。/orgs/{org}/installationsで取得できました。しかし、「誰が」という部分は取得できないようでした。(未検証ですが、これを確認するには監査ログの確認が必要な可能性があります)。付与された Permissionsは取得できました。Repository accessについては、対象リポジトリを選択しているか否かはわかるようですが、具体的なリポジトリ名までは取得できていません。 -
Repository -> Rulesets:
タグの保護ルールなどを取得できるか確認しました(無償プランでは有効にできません)。OrganizationレベルのRulesetsは、直接取得はできないようでしたが、リポジトリ単位のRulesetsは取得できて、そちらのデータの方に、OrganizationレベルのRulesetsで反映された内容が取得されました。 -
Repository -> General -> Releases
immutable releasesの設定が取得できるか確認しました。この設定は取得できました。 -
Personal access tokensの設定
Tokens (classic) が無効設定になっているかが、確認したかった項目ですが、この設定は取得できていません。 -
Fine-grained personal access tokensでActiveになっているもの:
取得できれば、Fine-grained PATの棚卸が効率的になると思いましたが、取得できていません。以下によると、「Only GitHub Apps can use this endpoint.」と記載がありました。今回はGitHub Appsではないので、取得できていません。
List fine-grained personal access tokens with access to organization resources
作成したスクリプトについて
今回はClaude Code(Opus 4.7)で作成していますが、最初に与えたプロンプトは以下のようなものです。(途中で何度かバグ修正のやり取りなど行なっていますが、全てのやり取りは残念ながら記録できていません)
gihub organizationを運用しています。
gihub organizationの設定情報の構成管理がしたいです。
そのために、できるだけ多くの情報を取得して、テキストファイルへ出力させるスクリプトを作成してください。
例えば、/orgs/{org} エンドポイントで orgの基本的な情報を取得できますが、それ以外にも多くの情報があるはずなので、取得してください。
また、情報の出力は月次で行い、githubへ保存する予定です。
このため、差分チェックがしやすいようにしてください。データの並び順も同じにしてください。
なお、認証はFine-grained personal access tokenとしますので、必要なパーミッションもリストアップしてください。
スクリプト本体以外に、README.md も同時に作成してください。README.md内に上記パーミッションを記載してください。
Pythonスクリプト、readme、APIエンドポイントの一覧を生成させました。以下の通りとなります。
スクリプトの本体は以下の通りです。
Python スクリプト
#!/usr/bin/env python3
"""GitHub Organization 構成情報エクスポートスクリプト.
差分チェックがしやすい形(キー順固定・要素ソート・揮発フィールド除去)で
組織配下の各種設定情報を JSON テキストファイルへ出力する。
"""
from __future__ import annotations
import argparse
import json
import os
import re
import sys
import time
from pathlib import Path
from typing import Any, Iterable
from urllib.parse import urlencode
import urllib.request
import urllib.error
GITHUB_API = "https://api.github.com"
# API バージョン。`2026-03-10` で /orgs/{org}/settings/immutable-releases 等の
# 比較的新しいエンドポイントが利用可能になる。GitHub の API バージョンは後方互換性が
# 保たれているため、既存エンドポイントは引き続き動作する。
DEFAULT_API_VERSION = "2026-03-10"
USER_AGENT = "github-org-config-exporter"
# 差分ノイズになりやすい揮発的なフィールド(時刻・カウンタなど)。
# トップレベルでもネストでも、このキー名は再帰的に削除する。
VOLATILE_KEYS = frozenset(
{
"updated_at",
"pushed_at",
"last_used",
"last_active_at",
"last_run_at",
"etag",
"node_id",
# 集計値はメンバー追加・PR 件数などで揺れる
"followers",
"following",
"public_repos",
"public_gists",
"total_private_repos",
"owned_private_repos",
"private_gists",
"disk_usage",
"collaborators",
"size",
"network_count",
"subscribers_count",
"forks_count",
"stargazers_count",
"watchers_count",
"open_issues_count",
"open_issues",
"forks",
"watchers",
}
)
# 各リソースのソートキー候補(先に存在したものを採用)。
SORT_KEY_CANDIDATES = ("id", "login", "name", "slug", "key_id", "tag_name")
# ---------------------------------------------------------------------------
# HTTP クライアント
# ---------------------------------------------------------------------------
class GitHubClient:
def __init__(self, token: str, api_version: str = DEFAULT_API_VERSION) -> None:
self.token = token
self.api_version = api_version
def _headers(self) -> dict[str, str]:
return {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {self.token}",
"X-GitHub-Api-Version": self.api_version,
"User-Agent": USER_AGENT,
}
def get(
self,
path: str,
params: dict[str, Any] | None = None,
*,
allow_status: Iterable[int] = (),
) -> tuple[int, Any, dict[str, str]]:
"""単一 GET。`allow_status` に含まれるステータスはエラーにしない。"""
url = f"{GITHUB_API}{path}"
if params:
url = f"{url}?{urlencode(params)}"
req = urllib.request.Request(url, headers=self._headers(), method="GET")
try:
with urllib.request.urlopen(req) as resp:
body = resp.read().decode("utf-8") if resp.length != 0 else ""
data = json.loads(body) if body else None
return resp.status, data, dict(resp.headers)
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", errors="replace")
if e.code in allow_status:
try:
data = json.loads(body) if body else None
except json.JSONDecodeError:
data = body
msg = _extract_message(data) or body[:200]
print(f"[SKIP] GET {path} -> {e.code}: {msg}", file=sys.stderr)
return e.code, data, dict(e.headers)
print(
f"[ERROR] GET {path} -> {e.code}: {body[:300]}",
file=sys.stderr,
)
raise
def paginate(
self,
path: str,
params: dict[str, Any] | None = None,
*,
allow_status: Iterable[int] = (),
) -> list[Any] | None:
"""Link ヘッダ追従でページング取得。404 等で None を返す場合あり。"""
params = dict(params or {})
params.setdefault("per_page", 100)
results: list[Any] = []
url = f"{GITHUB_API}{path}?{urlencode(params)}"
while url:
req = urllib.request.Request(url, headers=self._headers(), method="GET")
try:
with urllib.request.urlopen(req) as resp:
body = resp.read().decode("utf-8") if resp.length != 0 else ""
data = json.loads(body) if body else []
headers = dict(resp.headers)
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", errors="replace")
if e.code in allow_status:
try:
parsed = json.loads(body) if body else None
except json.JSONDecodeError:
parsed = None
msg = _extract_message(parsed) or body[:200]
print(f"[SKIP] GET {path} -> {e.code}: {msg}", file=sys.stderr)
return None
print(
f"[ERROR] GET {url} -> {e.code}: {body[:300]}",
file=sys.stderr,
)
raise
if isinstance(data, list):
results.extend(data)
elif isinstance(data, dict):
# actions/secrets のような {total_count, secrets:[...]} 形式に対応
payload_key = next(
(
k
for k in (
"secrets",
"variables",
"repositories",
"runners",
"runner_groups",
"installations",
"workflows",
)
if k in data and isinstance(data[k], list)
),
None,
)
if payload_key:
results.extend(data[payload_key])
else:
return data # type: ignore[return-value]
url = _next_link(headers.get("Link", ""))
# 簡易レート制御
if int(headers.get("X-RateLimit-Remaining", "1") or 1) < 50:
reset = int(headers.get("X-RateLimit-Reset", "0") or 0)
wait = max(0, reset - int(time.time())) + 1
if wait and wait < 600:
print(
f"[INFO] Rate limit low. sleeping {wait}s",
file=sys.stderr,
)
time.sleep(wait)
return results
def _extract_message(data: Any) -> str | None:
if isinstance(data, dict):
msg = data.get("message")
if isinstance(msg, str):
return msg
return None
def _next_link(link_header: str) -> str | None:
if not link_header:
return None
# 例: <https://api.github.com/...&page=2>; rel="next", <...>; rel="last"
for part in link_header.split(","):
m = re.match(r"\s*<([^>]+)>;\s*rel=\"([^\"]+)\"", part)
if m and m.group(2) == "next":
return m.group(1)
return None
# ---------------------------------------------------------------------------
# 正規化(差分安定化)
# ---------------------------------------------------------------------------
def scrub(value: Any) -> Any:
"""揮発フィールド除去とリストの安定ソート。"""
if isinstance(value, dict):
return {k: scrub(v) for k, v in value.items() if k not in VOLATILE_KEYS}
if isinstance(value, list):
cleaned = [scrub(v) for v in value]
return _sort_list(cleaned)
return value
def _sort_list(items: list[Any]) -> list[Any]:
if not items:
return items
if all(isinstance(i, dict) for i in items):
sort_key = next(
(k for k in SORT_KEY_CANDIDATES if all(k in i for i in items)),
None,
)
if sort_key:
return sorted(items, key=lambda d: (str(d.get(sort_key)),))
# 一意キーが無い場合はシリアライズ文字列で安定ソート
return sorted(items, key=lambda d: json.dumps(d, sort_keys=True, ensure_ascii=False))
if all(isinstance(i, (str, int, float, bool)) or i is None for i in items):
return sorted(items, key=lambda x: (x is None, str(x)))
return items
def write_json(path: Path, data: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
cleaned = scrub(data)
with path.open("w", encoding="utf-8") as f:
json.dump(cleaned, f, indent=2, sort_keys=True, ensure_ascii=False)
f.write("\n")
print(f"[OK] {path}")
# ---------------------------------------------------------------------------
# 取得処理
# ---------------------------------------------------------------------------
# 許容するエラーステータス(取得不能をエラーにせずスキップする)。
# 403: 権限不足 / 404: リソース不在 / 409: 設定上そのデータが存在しない
# (例: allowed_actions=all 下での /actions/permissions/selected-actions)
# 410: Gone / 422: 設定が無効
SKIPPABLE_STATUSES = (403, 404, 409, 410, 422)
def safe_paginate(
client: GitHubClient,
path: str,
params: dict[str, Any] | None = None,
) -> list[Any] | None:
return client.paginate(path, params, allow_status=SKIPPABLE_STATUSES)
def safe_get(client: GitHubClient, path: str) -> Any:
status, data, _ = client.get(path, allow_status=SKIPPABLE_STATUSES)
if status >= 400:
return None
return data
def export_org(client: GitHubClient, org: str, out: Path) -> None:
org_dir = out / "org"
# 基本情報
info = safe_get(client, f"/orgs/{org}")
if info is not None:
write_json(org_dir / "info.json", info)
# 組織設定(immutable releases 等。API バージョン 2026-03-10 以降)
write_optional(
org_dir / "settings_immutable_releases.json",
safe_get(client, f"/orgs/{org}/settings/immutable-releases"),
)
# メンバー / 招待 / 外部コラボレーター
write_optional(org_dir / "members.json", safe_paginate(client, f"/orgs/{org}/members"))
write_optional(
org_dir / "members_admin.json",
safe_paginate(client, f"/orgs/{org}/members", {"role": "admin"}),
)
write_optional(
org_dir / "outside_collaborators.json",
safe_paginate(client, f"/orgs/{org}/outside_collaborators"),
)
write_optional(
org_dir / "pending_invitations.json",
safe_paginate(client, f"/orgs/{org}/invitations"),
)
write_optional(org_dir / "blocks.json", safe_paginate(client, f"/orgs/{org}/blocks"))
write_optional(
org_dir / "security_managers.json",
safe_paginate(client, f"/orgs/{org}/security-managers"),
)
# Webhooks
write_optional(org_dir / "hooks.json", safe_paginate(client, f"/orgs/{org}/hooks"))
# Custom org roles / properties
write_optional(
org_dir / "organization_roles.json",
safe_get(client, f"/orgs/{org}/organization-roles"),
)
write_optional(
org_dir / "custom_properties.json",
safe_paginate(client, f"/orgs/{org}/properties/schema"),
)
# Actions 設定
actions_dir = org_dir / "actions"
write_optional(
actions_dir / "permissions.json",
safe_get(client, f"/orgs/{org}/actions/permissions"),
)
write_optional(
actions_dir / "permissions_selected_actions.json",
safe_get(client, f"/orgs/{org}/actions/permissions/selected-actions"),
)
write_optional(
actions_dir / "permissions_workflow.json",
safe_get(client, f"/orgs/{org}/actions/permissions/workflow"),
)
write_optional(
actions_dir / "selected_repositories.json",
safe_paginate(client, f"/orgs/{org}/actions/permissions/repositories"),
)
write_optional(
actions_dir / "secrets.json",
safe_paginate(client, f"/orgs/{org}/actions/secrets"),
)
write_optional(
actions_dir / "variables.json",
safe_paginate(client, f"/orgs/{org}/actions/variables"),
)
write_optional(
actions_dir / "runners.json",
safe_paginate(client, f"/orgs/{org}/actions/runners"),
)
write_optional(
actions_dir / "runner_groups.json",
safe_paginate(client, f"/orgs/{org}/actions/runner-groups"),
)
# Dependabot / Codespaces シークレット(名前のみ取得)
write_optional(
org_dir / "dependabot_secrets.json",
safe_paginate(client, f"/orgs/{org}/dependabot/secrets"),
)
write_optional(
org_dir / "codespaces_secrets.json",
safe_paginate(client, f"/orgs/{org}/codespaces/secrets"),
)
# 組織レベルの Rulesets。
# /orgs/{org}/rulesets は実装上 Organization administration: Write を要求する
# (ドキュメント記載の Repository administration: Read では 403 となる)。
# Read-only PAT 設計を維持するため、ここでは取得を試みるのみとし、
# 403 でスキップされることを許容する。組織レベル ruleset の内容は各
# リポジトリ側 /repos/{owner}/{repo}/rulesets?includes_parents=true 経由で
# 取得する(export_repos 側)。
org_rulesets = safe_paginate(client, f"/orgs/{org}/rulesets")
if org_rulesets is not None:
write_optional(org_dir / "rulesets.json", org_rulesets)
for rs in org_rulesets:
rs_id = rs.get("id") if isinstance(rs, dict) else None
if rs_id is None:
continue
detail = safe_get(client, f"/orgs/{org}/rulesets/{rs_id}")
if detail:
write_optional(
org_dir / "rulesets_detail" / f"{rs_id}.json",
detail,
)
# Apps installations
write_optional(
org_dir / "installations.json",
safe_paginate(client, f"/orgs/{org}/installations"),
)
# Note: /orgs/{org}/personal-access-tokens(-requests) は GitHub Apps
# (installation token) 専用で Fine-grained PAT では呼び出せない仕様のため
# 取得対象外とする。組織内 PAT の確認は GitHub UI
# (Settings > Personal access tokens) で行うこと。
# Packages(タイプごとに分けて取得)
for pkg_type in ("npm", "maven", "rubygems", "docker", "nuget", "container"):
pkgs = safe_paginate(
client, f"/orgs/{org}/packages", {"package_type": pkg_type}
)
if pkgs:
write_optional(org_dir / "packages" / f"{pkg_type}.json", pkgs)
def export_teams(client: GitHubClient, org: str, out: Path) -> None:
teams = safe_paginate(client, f"/orgs/{org}/teams") or []
write_optional(out / "org" / "teams.json", teams)
if not isinstance(teams, list):
return
for team in teams:
slug = team.get("slug")
if not slug:
continue
team_dir = out / "org" / "teams_detail" / slug
write_optional(team_dir / "info.json", safe_get(client, f"/orgs/{org}/teams/{slug}"))
write_optional(
team_dir / "members.json",
safe_paginate(client, f"/orgs/{org}/teams/{slug}/members"),
)
write_optional(
team_dir / "repos.json",
safe_paginate(client, f"/orgs/{org}/teams/{slug}/repos"),
)
write_optional(
team_dir / "invitations.json",
safe_paginate(client, f"/orgs/{org}/teams/{slug}/invitations"),
)
def export_repos(client: GitHubClient, org: str, out: Path) -> None:
repos = safe_paginate(client, f"/orgs/{org}/repos", {"type": "all"}) or []
repo_index = [
{
"name": r.get("name"),
"private": r.get("private"),
"archived": r.get("archived"),
"fork": r.get("fork"),
"default_branch": r.get("default_branch"),
"visibility": r.get("visibility"),
}
for r in repos
if isinstance(r, dict)
]
write_optional(out / "org" / "repos_index.json", repo_index)
for repo in repos:
if not isinstance(repo, dict):
continue
name = repo.get("name")
if not name:
continue
repo_dir = out / "repos" / name
write_optional(repo_dir / "info.json", repo)
write_optional(
repo_dir / "topics.json",
safe_get(client, f"/repos/{org}/{name}/topics"),
)
write_optional(
repo_dir / "collaborators.json",
safe_paginate(client, f"/repos/{org}/{name}/collaborators"),
)
write_optional(
repo_dir / "teams.json",
safe_paginate(client, f"/repos/{org}/{name}/teams"),
)
write_optional(
repo_dir / "hooks.json",
safe_paginate(client, f"/repos/{org}/{name}/hooks"),
)
write_optional(
repo_dir / "deploy_keys.json",
safe_paginate(client, f"/repos/{org}/{name}/keys"),
)
write_optional(
repo_dir / "actions_permissions.json",
safe_get(client, f"/repos/{org}/{name}/actions/permissions"),
)
write_optional(
repo_dir / "actions_secrets.json",
safe_paginate(client, f"/repos/{org}/{name}/actions/secrets"),
)
write_optional(
repo_dir / "actions_variables.json",
safe_paginate(client, f"/repos/{org}/{name}/actions/variables"),
)
write_optional(
repo_dir / "dependabot_secrets.json",
safe_paginate(client, f"/repos/{org}/{name}/dependabot/secrets"),
)
# リポジトリに適用される ruleset 一覧(組織レベルで定義されたものも
# includes_parents=true により含む)。一覧レスポンスには rules 配列が
# 含まれないため、各 ruleset の詳細も別途取得する。
repo_rulesets = safe_paginate(
client,
f"/repos/{org}/{name}/rulesets",
{"includes_parents": "true"},
)
if repo_rulesets is not None:
write_optional(repo_dir / "rulesets.json", repo_rulesets)
for rs in repo_rulesets:
rs_id = rs.get("id") if isinstance(rs, dict) else None
if rs_id is None:
continue
detail = safe_get(
client, f"/repos/{org}/{name}/rulesets/{rs_id}"
)
if detail:
write_optional(
repo_dir / "rulesets_detail" / f"{rs_id}.json",
detail,
)
write_optional(
repo_dir / "branches.json",
safe_paginate(client, f"/repos/{org}/{name}/branches"),
)
# ブランチ保護はデフォルトブランチのみ取得(全ブランチ取得は重い)。
default_branch = repo.get("default_branch")
if default_branch:
write_optional(
repo_dir / "default_branch_protection.json",
safe_get(
client,
f"/repos/{org}/{name}/branches/{default_branch}/protection",
),
)
# Environments と Pages はあれば取得
envs = safe_get(client, f"/repos/{org}/{name}/environments")
if envs:
write_optional(repo_dir / "environments.json", envs)
pages = safe_get(client, f"/repos/{org}/{name}/pages")
if pages:
write_optional(repo_dir / "pages.json", pages)
def write_optional(path: Path, data: Any) -> None:
if data is None:
return
write_json(path, data)
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main() -> int:
parser = argparse.ArgumentParser(
description="GitHub Organization の構成情報を JSON として出力する。",
)
parser.add_argument("--org", required=True, help="対象 organization のログイン名")
parser.add_argument(
"--out",
default="output",
help="出力ディレクトリ(既定: ./output)",
)
parser.add_argument(
"--token-env",
default="GITHUB_TOKEN",
help="トークンを格納する環境変数名(既定: GITHUB_TOKEN)",
)
parser.add_argument(
"--skip-repos",
action="store_true",
help="リポジトリ単位の取得をスキップ(高速確認用)",
)
args = parser.parse_args()
token = os.environ.get(args.token_env)
if not token:
print(
f"[ERROR] 環境変数 {args.token_env} にトークンが設定されていません。",
file=sys.stderr,
)
return 2
out = Path(args.out).resolve()
out.mkdir(parents=True, exist_ok=True)
client = GitHubClient(token)
print(f"[INFO] Exporting organization '{args.org}' to {out}")
export_org(client, args.org, out)
export_teams(client, args.org, out)
if not args.skip_repos:
export_repos(client, args.org, out)
print("[INFO] Done.")
return 0
if __name__ == "__main__":
sys.exit(main())
readmeは以下のようになります。
readme
# GitHub Organization 構成管理エクスポーター
GitHub Organization の設定情報を REST API から可能な限り取得し、差分チェックがしやすい形式で JSON テキストファイルとして出力するスクリプトです。月次で実行し、出力結果を Git 管理することで構成のスナップショット履歴を残すことを想定しています。
## 主な特徴
- 1 リソース 1 ファイルで分割出力 → 差分が局所化される
- `json.dump(..., sort_keys=True, indent=2)` でキー順を固定
- リスト要素は `id` / `login` / `name` / `slug` 等の一意キーで安定ソート
- `updated_at` `pushed_at` `last_used` 等の揮発フィールドや `*_count` 系の集計値を再帰的に除去
- `403` / `404` を許容して継続(プラン未対応・権限不足のリソースをスキップ)
- ページング(Link ヘッダ)に追従し、レート制限が逼迫したら自動待機
## 取得対象(主なもの)
### Organization レベル
- 基本情報 (`/orgs/{org}`)
- 組織設定(Immutable releases 等、`/orgs/{org}/settings/immutable-releases`)
- メンバー / 管理者メンバー / Outside collaborators / 招待中ユーザー / ブロック済みユーザー / Security managers
- Webhooks
- Custom organization roles / Custom properties schema
- Actions: permissions / selected actions / workflow / 対象リポジトリ / secrets / variables / runners / runner groups
- Dependabot secrets / Codespaces secrets(名前のみ。値は GitHub API で取得不可)
- Rulesets — 組織レベル直接取得は `Organization administration: Write` が必要なため Read-only PAT では `403` スキップされる。**各リポジトリ側で `?includes_parents=true` を付けて取得することで、組織レベル ruleset の内容も `repos/{repo}/rulesets.json` 経由で確認可能**(同 `rulesets_detail/{id}.json` に rules 配列含む詳細も保存)
- Apps installations(※ 誰がインストールしたか / 対象リポジトリのリストは API 仕様上取得不可。`repository_selection` で `all`/`selected` の判別のみ可能)
- Packages(npm / maven / rubygems / docker / nuget / container)※ Fine-grained PAT では取得不可。classic PAT 利用時のみ出力される
### Team レベル
- チーム一覧 + 各チームの詳細 / メンバー / 紐付くリポジトリ / 招待
### Repository レベル
- 基本情報 / Topics / コラボレーター / 紐付くチーム
- Webhooks / Deploy keys
- Actions permissions / secrets / variables
- Dependabot secrets
- Rulesets / Branches / デフォルトブランチの保護設定
- Environments / Pages 設定(存在する場合のみ)
> 注: GitHub API の仕様上、シークレットやデプロイキーの **実値**は取得できません。**名前・ID・作成日**等のメタデータのみ出力されます。
## 必要な権限(Fine-grained Personal Access Token)
トークンは対象 organization をリソースオーナーとし、対象リポジトリは「All repositories」を推奨します。
### Organization permissions(**Access: Read-only**)
GitHub UI 上の項目名(`Organization` プレフィックス込み)で表記しています。
| UI 上の項目名 | 用途 |
| --- | --- |
| Administration | 組織基本情報 / Outside collaborators の参照 |
| Members | メンバー / 招待 / チーム / Security managers |
| Webhooks | Org webhooks |
| Custom organization roles | カスタム組織ロール |
| Custom properties | リポジトリカスタムプロパティのスキーマ |
| Plan | プラン情報 |
| Self-hosted runners | Org のセルフホステッドランナー / ランナーグループ |
| Secrets | Org Actions シークレット名 |
| Variables | Org Actions 変数 |
| Organization dependabot secrets | Org Dependabot シークレット名 |
| Organization codespaces secrets | Org Codespaces シークレット名 |
| Blocked users | ブロックユーザー |
> - `Packages` は **Organization permissions には存在しません**。下記 Repository permissions の `Packages` を Read にしてください。
### Repository permissions(**Access: Read-only**、対象は All repositories 推奨)
| UI 上の項目名 | 用途 |
| --- | --- |
| Metadata | リポジトリ一覧・基本情報(必須・他権限の前提) |
| Administration | コラボレーター / チーム紐付け / Deploy keys / Rulesets |
| Contents | Pages 設定 / 各種ファイル参照 |
| Webhooks | リポジトリ webhook |
| Actions | リポジトリ Actions 権限・workflow 設定 |
| Secrets | リポジトリ Actions シークレット名 |
| Variables | リポジトリ Actions 変数 |
| Dependabot secrets | リポジトリ Dependabot シークレット名 |
| Environments | Environments 一覧 |
| Pages | GitHub Pages 設定 |
| Deployments | Environments / デプロイメント参照 |
> Fine-grained PAT は **すべて Read-only** で問題ありません。書き込み権限は不要です。
> **GitHub Packages について(重要)**
> Fine-grained PAT は **GitHub Packages 関連 API に対応していません**(GitHub の既知の制限)。そのため Organization / Repository のどちらの permissions にも `Packages` 項目は存在しません。
> 本スクリプトは `/orgs/{org}/packages` を呼び出しますが、Fine-grained PAT では `403` が返り **自動的にスキップ** されます(出力ファイルは生成されません)。
> どうしてもパッケージ一覧を構成管理に含めたい場合は、`read:packages` スコープを付けた **classic personal access token** を別途用意する必要があります。
## インストール
Python 3.9 以降であれば標準ライブラリのみで動作します(外部依存なし)。
```bash
git clone <this-repo>
cd github-org_構成管理
chmod +x github_org_export.py
使い方
export GITHUB_TOKEN='github_pat_xxx...' # Fine-grained PAT
python3 github_org_export.py --org your-organization --out output/2026-05
オプション:
| オプション | 説明 |
|---|---|
--org |
(必須)対象 organization のログイン名 |
--out |
出力ディレクトリ(既定: ./output) |
--token-env |
トークンを格納する環境変数名(既定: GITHUB_TOKEN) |
--skip-repos |
リポジトリ単位の取得をスキップ(疎通確認等で利用) |
出力ディレクトリ構造
output/2026-05/
├── org/
│ ├── info.json
│ ├── settings_immutable_releases.json
│ ├── members.json
│ ├── members_admin.json
│ ├── outside_collaborators.json
│ ├── pending_invitations.json
│ ├── blocks.json
│ ├── security_managers.json
│ ├── hooks.json
│ ├── organization_roles.json
│ ├── custom_properties.json
│ ├── installations.json
│ ├── dependabot_secrets.json
│ ├── codespaces_secrets.json
│ ├── rulesets.json
│ ├── teams.json
│ ├── repos_index.json
│ ├── actions/
│ │ ├── permissions.json
│ │ ├── permissions_selected_actions.json
│ │ ├── permissions_workflow.json
│ │ ├── selected_repositories.json
│ │ ├── secrets.json
│ │ ├── variables.json
│ │ ├── runners.json
│ │ └── runner_groups.json
│ ├── packages/
│ │ ├── npm.json
│ │ └── ...
│ ├── rulesets_detail/
│ │ └── {ruleset_id}.json
│ └── teams_detail/
│ └── {team_slug}/
│ ├── info.json
│ ├── members.json
│ ├── repos.json
│ └── invitations.json
└── repos/
└── {repo_name}/
├── info.json
├── topics.json
├── collaborators.json
├── teams.json
├── hooks.json
├── deploy_keys.json
├── actions_permissions.json
├── actions_secrets.json
├── actions_variables.json
├── dependabot_secrets.json
├── rulesets.json
├── rulesets_detail/
│ └── {ruleset_id}.json # rules 配列を含む詳細
├── branches.json
├── default_branch_protection.json
├── environments.json # 存在する場合のみ
└── pages.json # 存在する場合のみ
月次運用フロー(例)
- リポジトリをチェックアウトし、
output/YYYY-MM/に出力 git diffで前月との差分を確認- 差分があれば
git commit && git pushでスナップショットを残す - 想定外の差分が出ていないか PR でレビュー
取得できてない情報
| 知りたい内容 | 状況 |
|---|---|
| Installed GitHub App の インストールしたユーザー | API レスポンスに installed_by 相当のフィールドが無い |
Installed GitHub App の 対象リポジトリ一覧(repository_selection: selected の場合) |
取得には installation access token が必要で、PAT では呼び出し不可。installations.json の repository_selection で all/selected の判別までは可能 |
| PAT ポリシー設定値(Fine-grained / classic の許可・承認要否など) | 設定値を返す REST API が公開されていない(Web UI のみ) |
| 組織内の Fine-grained PAT 一覧 / 承認リクエスト一覧 | /orgs/{org}/personal-access-tokens および /orgs/{org}/personal-access-token-requests は GitHub Apps (installation token) 専用エンドポイント であり、Fine-grained PAT では呼び出し不可(GitHub Docsに「Only GitHub Apps can use this endpoint」と明記)。確認は組織 Settings 画面(Settings > Personal access tokens)で目視するか、GitHub App を別途構築する運用が必要 |
| GitHub Packages の一覧 | Fine-grained PAT 非対応(既述) |
トラブルシューティング
401 Unauthorized: トークンの期限切れ、または--token-envで指定した環境変数が未設定。403のスキップが多発する: Fine-grained PAT の権限が不足している可能性。上記の権限表を見直してください。- 取得時間が長い:
--skip-reposで疎通確認した後、本番取得時のみ全量取得してください。リポジトリが多い大規模 org では数十分〜数時間かかります。 - 差分にノイズが残る: 揮発フィールドの追加が必要な場合は
github_org_export.pyのVOLATILE_KEYSに追加してください。
取得対象のAPIエンドポイントは、以下のようになります。
取得対象のAPIエンドポイント
# GitHub REST API 一覧(本スクリプトが利用するエンドポイント)
`github_org_export.py` が叩いているすべての GitHub REST API のエンドポイント一覧と、各 API の取得状況・出力ファイル・公式ドキュメントへのリンクをまとめた資料です。
## 凡例
| マーク | 意味 |
| --- | --- |
| ◯ | 通常取得可能 |
| △ | 設定や状態によって取得可否が変わる(取得できないことを `[SKIP]` ログで判別) |
| ✕ | 本スクリプトの認証方式(Fine-grained PAT)では構造的に取得不可 |
API バージョンは `2026-03-10` を指定しています(`X-GitHub-Api-Version` ヘッダ)。`/orgs/{org}/settings/immutable-releases` 等の比較的新しいエンドポイントが利用可能です。GitHub の API バージョンは後方互換性が保たれているため、既存エンドポイントは引き続き動作します。
---
## 1. Organization 基本情報・メンバー
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}` | 組織の基本情報(名前 / プラン / 既定権限 / 2FA 強制 等) | `org/info.json` | ◯ | [Get an organization](https://docs.github.com/ja/rest/orgs/orgs#get-an-organization) |
| `GET /orgs/{org}/settings/immutable-releases` | Immutable releases 設定(Org レベルで強制しているか / 個別リポジトリで上書き可か等) | `org/settings_immutable_releases.json` | ◯ | [Get immutable releases settings](https://docs.github.com/ja/rest/orgs/orgs?apiVersion=2026-03-10#get-immutable-releases-settings-for-an-organization) |
| `GET /orgs/{org}/members` | 組織メンバー一覧 | `org/members.json` | ◯ | [List organization members](https://docs.github.com/ja/rest/orgs/members#list-organization-members) |
| `GET /orgs/{org}/members?role=admin` | 組織管理者メンバー一覧 | `org/members_admin.json` | ◯ | 同上(`role=admin` パラメータ) |
| `GET /orgs/{org}/outside_collaborators` | 外部コラボレーター | `org/outside_collaborators.json` | ◯ | [List outside collaborators](https://docs.github.com/ja/rest/orgs/outside-collaborators#list-outside-collaborators-for-an-organization) |
| `GET /orgs/{org}/invitations` | 招待中のユーザー | `org/pending_invitations.json` | ◯ | [List pending invitations](https://docs.github.com/ja/rest/orgs/members#list-pending-organization-invitations) |
| `GET /orgs/{org}/blocks` | ブロック中のユーザー | `org/blocks.json` | ◯ | [List blocked users](https://docs.github.com/ja/rest/orgs/blocking#list-users-blocked-by-an-organization) |
| `GET /orgs/{org}/security-managers` | セキュリティマネージャー | `org/security_managers.json` | ◯ | [List security manager teams](https://docs.github.com/ja/rest/orgs/security-managers#list-security-manager-teams) |
## 2. Organization Webhooks / ロール / プロパティ
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/hooks` | 組織 webhook 一覧 | `org/hooks.json` | ◯ | [List organization webhooks](https://docs.github.com/ja/rest/orgs/webhooks#list-organization-webhooks) |
| `GET /orgs/{org}/organization-roles` | カスタム組織ロール | `org/organization_roles.json` | ◯ | [List organization roles](https://docs.github.com/ja/rest/orgs/organization-roles#list-organization-fine-grained-permissions-for-an-organization) |
| `GET /orgs/{org}/properties/schema` | リポジトリカスタムプロパティのスキーマ | `org/custom_properties.json` | ◯ | [Get all custom properties](https://docs.github.com/ja/rest/orgs/custom-properties#get-all-custom-properties-for-an-organization) |
## 3. Organization GitHub Actions
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/actions/permissions` | Actions 利用可否 / 適用範囲設定 | `org/actions/permissions.json` | ◯ | [Get GitHub Actions permissions](https://docs.github.com/ja/rest/actions/permissions#get-github-actions-permissions-for-an-organization) |
| `GET /orgs/{org}/actions/permissions/selected-actions` | 許可された Actions / Reusable Workflows のリスト | `org/actions/permissions_selected_actions.json` | △ | [Get allowed actions](https://docs.github.com/ja/rest/actions/permissions#get-allowed-actions-and-reusable-workflows-for-an-organization) |
| `GET /orgs/{org}/actions/permissions/workflow` | Workflow デフォルトトークン権限 | `org/actions/permissions_workflow.json` | ◯ | [Get default workflow permissions](https://docs.github.com/ja/rest/actions/permissions#get-default-workflow-permissions-for-an-organization) |
| `GET /orgs/{org}/actions/permissions/repositories` | Actions 利用可能リポジトリ(`enabled_repositories=selected` のとき) | `org/actions/selected_repositories.json` | △ | [List selected repositories](https://docs.github.com/ja/rest/actions/permissions#list-selected-repositories-enabled-for-github-actions-in-an-organization) |
| `GET /orgs/{org}/actions/secrets` | 組織 Actions シークレット名(値は API 仕様上取得不可) | `org/actions/secrets.json` | ◯ | [List organization secrets](https://docs.github.com/ja/rest/actions/secrets#list-organization-secrets) |
| `GET /orgs/{org}/actions/variables` | 組織 Actions 変数 | `org/actions/variables.json` | ◯ | [List organization variables](https://docs.github.com/ja/rest/actions/variables#list-organization-variables) |
| `GET /orgs/{org}/actions/runners` | Self-hosted runners 一覧 | `org/actions/runners.json` | ◯ | [List self-hosted runners](https://docs.github.com/ja/rest/actions/self-hosted-runners#list-self-hosted-runners-for-an-organization) |
| `GET /orgs/{org}/actions/runner-groups` | Runner Group 一覧 | `org/actions/runner_groups.json` | ◯ | [List self-hosted runner groups](https://docs.github.com/ja/rest/actions/self-hosted-runner-groups#list-self-hosted-runner-groups-for-an-organization) |
> △ の SKIP 例: `allowed_actions=all` の組織で `selected-actions` を呼ぶと `409 Conflict`、`enabled_repositories=all` のときに `selected_repositories` を呼ぶと同じく `409` になる。これは設定上そのリソースが存在しない正常状態。
## 4. Organization Secrets / Variables(Actions 以外)
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/dependabot/secrets` | Dependabot シークレット名 | `org/dependabot_secrets.json` | ◯ | [List organization secrets (Dependabot)](https://docs.github.com/ja/rest/dependabot/secrets#list-organization-secrets) |
| `GET /orgs/{org}/codespaces/secrets` | Codespaces シークレット名 | `org/codespaces_secrets.json` | ◯ | [List organization secrets (Codespaces)](https://docs.github.com/ja/rest/codespaces/organization-secrets#list-organization-secrets) |
## 5. Organization Rulesets
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/rulesets` | 組織レベル ruleset 一覧 | `org/rulesets.json` | △ | [List organization repository rulesets](https://docs.github.com/ja/rest/orgs/rules#list-organization-repository-rulesets) |
| `GET /orgs/{org}/rulesets/{id}` | 組織 ruleset 詳細(rules 配列含む) | `org/rulesets_detail/{id}.json` | △ | [Get an organization repository ruleset](https://docs.github.com/ja/rest/orgs/rules#get-an-organization-repository-ruleset) |
> △ の理由: 実装上 **Organization administration: Write** が要求される。Read-only PAT では `403`。代替として、リポジトリ単位の `?includes_parents=true` で組織レベル ruleset の内容も取得している(後述)。
## 6. GitHub Apps Installations
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/installations` | 組織にインストール済みの GitHub App 一覧 | `org/installations.json` | ◯ | [List app installations](https://docs.github.com/ja/rest/orgs/orgs#list-app-installations-for-an-organization) |
> 取得できるのは App 名 / `repository_selection`(`all` / `selected`)等。インストール実行者および `selected` 時の対象リポジトリは Fine-grained PAT では取得不可。
## 7. Packages
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/packages?package_type=...` | 組織に紐付くパッケージ一覧(npm / maven / rubygems / docker / nuget / container) | `org/packages/{type}.json` | ✕ | [List packages](https://docs.github.com/ja/rest/packages/packages#list-packages-for-an-organization) |
> Fine-grained PAT は GitHub Packages API 全般に未対応のため、すべて `403`。classic PAT (`read:packages` スコープ) でしか取れない。
## 8. Teams
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/teams` | チーム一覧 | `org/teams.json` | ◯ | [List teams](https://docs.github.com/ja/rest/teams/teams#list-teams) |
| `GET /orgs/{org}/teams/{slug}` | チーム詳細 | `org/teams_detail/{slug}/info.json` | ◯ | [Get a team by name](https://docs.github.com/ja/rest/teams/teams#get-a-team-by-name) |
| `GET /orgs/{org}/teams/{slug}/members` | チームメンバー | `org/teams_detail/{slug}/members.json` | ◯ | [List team members](https://docs.github.com/ja/rest/teams/members#list-team-members) |
| `GET /orgs/{org}/teams/{slug}/repos` | チームに紐付くリポジトリ | `org/teams_detail/{slug}/repos.json` | ◯ | [List team repositories](https://docs.github.com/ja/rest/teams/teams#list-team-repositories) |
| `GET /orgs/{org}/teams/{slug}/invitations` | チーム招待 | `org/teams_detail/{slug}/invitations.json` | ◯ | [List pending team invitations](https://docs.github.com/ja/rest/teams/members#list-pending-team-invitations) |
## 9. Repository 一覧 / 基本情報
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /orgs/{org}/repos?type=all` | 組織配下の全リポジトリ一覧 | `org/repos_index.json` + 各 `repos/{repo}/info.json` | ◯ | [List organization repositories](https://docs.github.com/ja/rest/repos/repos#list-organization-repositories) |
| `GET /repos/{owner}/{repo}` | リポジトリ詳細 | `repos/{repo}/info.json` | ◯ | [Get a repository](https://docs.github.com/ja/rest/repos/repos#get-a-repository) |
| `GET /repos/{owner}/{repo}/topics` | リポジトリトピック | `repos/{repo}/topics.json` | ◯ | [Get all repository topics](https://docs.github.com/ja/rest/repos/repos#get-all-repository-topics) |
## 10. Repository アクセス制御
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /repos/{owner}/{repo}/collaborators` | コラボレーター一覧 | `repos/{repo}/collaborators.json` | ◯ | [List repository collaborators](https://docs.github.com/ja/rest/collaborators/collaborators#list-repository-collaborators) |
| `GET /repos/{owner}/{repo}/teams` | リポジトリに紐付くチーム | `repos/{repo}/teams.json` | ◯ | [List repository teams](https://docs.github.com/ja/rest/repos/repos#list-repository-teams) |
## 11. Repository Webhooks / Deploy keys
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /repos/{owner}/{repo}/hooks` | リポジトリ webhooks | `repos/{repo}/hooks.json` | ◯ | [List repository webhooks](https://docs.github.com/ja/rest/repos/webhooks#list-repository-webhooks) |
| `GET /repos/{owner}/{repo}/keys` | Deploy keys | `repos/{repo}/deploy_keys.json` | ◯ | [List deploy keys](https://docs.github.com/ja/rest/deploy-keys/deploy-keys#list-deploy-keys) |
## 12. Repository GitHub Actions
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /repos/{owner}/{repo}/actions/permissions` | リポジトリ Actions 権限 | `repos/{repo}/actions_permissions.json` | ◯ | [Get GitHub Actions permissions](https://docs.github.com/ja/rest/actions/permissions#get-github-actions-permissions-for-a-repository) |
| `GET /repos/{owner}/{repo}/actions/secrets` | リポジトリ Actions シークレット名 | `repos/{repo}/actions_secrets.json` | ◯ | [List repository secrets](https://docs.github.com/ja/rest/actions/secrets#list-repository-secrets) |
| `GET /repos/{owner}/{repo}/actions/variables` | リポジトリ Actions 変数 | `repos/{repo}/actions_variables.json` | ◯ | [List repository variables](https://docs.github.com/ja/rest/actions/variables#list-repository-variables) |
## 13. Repository Dependabot
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /repos/{owner}/{repo}/dependabot/secrets` | リポジトリ Dependabot シークレット名 | `repos/{repo}/dependabot_secrets.json` | ◯ | [List repository secrets (Dependabot)](https://docs.github.com/ja/rest/dependabot/secrets#list-repository-secrets) |
## 14. Repository Rulesets / Branches
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /repos/{owner}/{repo}/rulesets?includes_parents=true` | リポジトリに適用される ruleset 一覧(**`includes_parents=true` で組織レベル ruleset も含む**) | `repos/{repo}/rulesets.json` | ◯ | [List repository rulesets](https://docs.github.com/ja/rest/repos/rules#get-all-repository-rulesets) |
| `GET /repos/{owner}/{repo}/rulesets/{id}` | ruleset 詳細(rules 配列含む) | `repos/{repo}/rulesets_detail/{id}.json` | ◯ | [Get a repository ruleset](https://docs.github.com/ja/rest/repos/rules#get-a-repository-ruleset) |
| `GET /repos/{owner}/{repo}/branches` | ブランチ一覧 | `repos/{repo}/branches.json` | ◯ | [List branches](https://docs.github.com/ja/rest/branches/branches#list-branches) |
| `GET /repos/{owner}/{repo}/branches/{branch}/protection` | デフォルトブランチの保護設定 | `repos/{repo}/default_branch_protection.json` | △ | [Get branch protection](https://docs.github.com/ja/rest/branches/branch-protection#get-branch-protection) |
> △ の SKIP 例: ブランチ保護未設定なら `404 Branch not protected`、対象ブランチ未作成(空リポジトリ等)なら `404 Branch not found`。どちらも正常状態。
## 15. Repository Environments / Pages
| API | 説明 | 出力ファイル | 状態 | 公式ドキュメント |
| --- | --- | --- | --- | --- |
| `GET /repos/{owner}/{repo}/environments` | Environments 一覧 | `repos/{repo}/environments.json` | ◯ | [List environments](https://docs.github.com/ja/rest/deployments/environments#list-environments) |
| `GET /repos/{owner}/{repo}/pages` | GitHub Pages 設定 | `repos/{repo}/pages.json` | △ | [Get a GitHub Pages site](https://docs.github.com/ja/rest/pages/pages#get-a-apiname-pages-site) |
> △ の SKIP 例: Pages 未設定なら `404 Not Found`。
---
## 取得不可の情報(再掲)
本スクリプトでは取得しないが、関連リソースとして頻出する情報のまとめ。
| 情報 | 取得不可の理由 | 代替手段 |
| --- | --- | --- |
| 組織内 Fine-grained PAT 一覧 / 承認リクエスト | `/orgs/{org}/personal-access-tokens` および `/personal-access-token-requests` は GitHub Apps 専用エンドポイント([公式ドキュメント](https://docs.github.com/ja/rest/orgs/personal-access-tokens) に "Only GitHub Apps can use this endpoint" 記載) | 組織 `Settings > Personal access tokens` で目視 / GitHub App を構築 |
| GitHub App をインストールしたユーザー | API レスポンスに該当フィールドなし | 組織 Audit log(Enterprise Cloud) |
| GitHub App の対象リポジトリ(`repository_selection=selected` のとき) | `GET /installation/repositories` は installation access token(App private key で署名した JWT)が必要 | 組織 `Settings > Installations > 各 App` で目視 / 自社製 App なら App 認証を別途構築 |
| PAT ポリシー設定値 | 設定値を返す REST API が公開されていない | Web UI のみ |
| GitHub Packages 一覧 | Fine-grained PAT が Packages API 全般に未対応 | classic PAT(`read:packages` スコープ) |
---
実行結果
-
前準備
実行前には、対象のOrgでFine-grained personal access tokensを許可します。
今回はOwner権限で検証しています。具体的に必要な権限は上記のreadmeに記載しています。(Organization permissionsとRepository permissions両方必要ですが、全て Read権限です) -
スクリプトの実行
上記スクリプトは以下のようにして実行できます。
export GITHUB_TOKEN='github_pat_xxx...'
python3 github_org_export.py --org <Organization名>
実行すると、以下のように表示されていきます。
$ python3 github_org_export.py --org test-org
[INFO] Exporting organization 'test-org' to /work/output
[OK] /work/output/org/info.json
[OK] /work/output/org/settings_immutable_releases.json
[OK] /work/output/org/members.json
[OK] /work/output/org/members_admin.json
[OK] /work/output/org/outside_collaborators.json
・・・・
Outputディレクトリ以下に、Org単位と、リポジトリ単位の情報が出力されます。

データ量が多いため、1つだけ例を記載します。
Organizationの基本的な情報が出力された info.json は、以下例のようになっていました。
{
"advanced_security_enabled_for_new_repositories": false,
"archived_at": null,
"avatar_url": "https://avatars.githubusercontent.com/u/232077537?v=4",
"billing_email": "xxx@xxx.jp",
"created_at": "2025-09-13T14:42:43Z",
"default_repository_branch": "main",
"default_repository_permission": "read",
"dependabot_alerts_enabled_for_new_repositories": false,
"dependabot_security_updates_enabled_for_new_repositories": false,
"dependency_graph_enabled_for_new_repositories": false,
"deploy_keys_enabled_for_repositories": false,
"description": null,
"display_commenter_full_name_setting_enabled": false,
"events_url": "https://api.github.com/orgs/test-org/events",
"has_organization_projects": true,
"has_repository_projects": true,
"hooks_url": "https://api.github.com/orgs/test-org/hooks",
"html_url": "https://github.com/test-org",
"id": 232077537,
"is_verified": false,
"issues_url": "https://api.github.com/orgs/test-org/issues",
"login": "test-org",
"members_allowed_repository_creation_type": "all",
"members_can_change_repo_visibility": true,
"members_can_create_internal_repositories": false,
"members_can_create_pages": true,
"members_can_create_private_pages": true,
"members_can_create_private_repositories": true,
"members_can_create_public_pages": true,
"members_can_create_public_repositories": true,
"members_can_create_repositories": true,
"members_can_create_teams": true,
"members_can_delete_issues": false,
"members_can_delete_repositories": true,
"members_can_fork_private_repositories": false,
"members_can_invite_outside_collaborators": true,
"members_can_view_dependency_insights": true,
"members_url": "https://api.github.com/orgs/test-org/members{/member}",
"plan": {
"filled_seats": 1,
"name": "team",
"private_repos": 999999,
"seats": 1,
"space": 976562499
},
"public_members_url": "https://api.github.com/orgs/test-org/public_members{/member}",
"readers_can_create_discussions": true,
"repos_url": "https://api.github.com/orgs/test-org/repos",
"secret_scanning_enabled_for_new_repositories": false,
"secret_scanning_push_protection_custom_link": null,
"secret_scanning_push_protection_enabled_for_new_repositories": false,
"secret_scanning_validity_checks_enabled": false,
"two_factor_requirement_enabled": true,
"type": "Organization",
"url": "https://api.github.com/orgs/test-org",
"web_commit_signoff_required": false
}
冒頭に記載した、Two-factor authentication(2要素認証)の必須化の設定は、このファイルの下から5行目の「"two_factor_requirement_enabled": true」部分に記述されていました。
課題と今後に向けて
仮に現状のスクリプトで運用を行った場合、月次でデータを取得して、githubで管理すると、設定情報の履歴が残せるのと、差分のチェック、重要な設定項目のチェックなどはできそうです。但し、以下のような課題があるため、今後検討していきたいと考えています。
-
全ての設定情報が網羅されてない:設定情報の網羅性については、APIを調査してスクリプトの改善を検討していくか、またはTerraformなど別手段で改善されるかの調査を考えています。
-
視認性が良くない:現状のスクリプトだと大幅な改善は難しいですが、Terraformなどの別手段だと改善されると考えています。このため、Terraformなどの別手段の方で、仮に網羅性も向上する場合には、別手段への移行で改善を検討します。
おわりに
GitHub Organization の設定情報取得スクリプトを作成して検証し、その際の内容を記載しました。この記事が皆様のお役に立てば幸いです。
クラスメソッドオペレーションズ株式会社について
クラスメソッドグループのオペレーション企業です。
運用・保守開発・サポート・情シス・バックオフィスの専門チームが、IT・AIをフル活用した「しくみ」を通じて、お客様の業務代行から課題解決や高付加価値サービスまでを提供するエキスパート集団です。
当社は様々な職種でメンバーを募集しています。
「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、クラスメソッドオペレーションズ株式会社 コーポレートサイト をぜひご覧ください。
※2026年1月 アノテーション㈱から社名変更しました








