This repository helps administrators manage cost centers in a GitHub Enterprise Account. It provides steps and scripts to add users to cost centers, associate organizations and repositories, and streamline enterprise billing.
- Python 3.x installed on your machine
- Enterprise Owner or Billing Manager permissions on GitHub
- GitHub Classic token with admin, read, and write permissions
- Clone the repository:
git clone https://github.com/CanarysPlayground/Cost-Center.git cd Cost-Center - (Optional) Install dependencies:
If there are Python dependencies, install them:pip install -r requirements.txt
- Navigate to Your Enterprises and select your enterprise account.
- Ensure you have the necessary permissions (Enterprise Owner or Billing Manager).
- Go to Billing and Licensing > Cost Centers and create a new cost center.
- Edit the cost center to add users as needed.
- Generate a Classic Personal Access Token on GitHub with admin, read, and write permissions.
- Use the appropriate REST API endpoints for enterprise billing as described in GitHub's documentation.
- Example
curlcommand:
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR-TOKEN>" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/enterprises/ENTERPRISE/settings/billing/cost-centers/COST_CENTER_ID/resource \
-d '{"users":["monalisa"]}'
Replace placeholders with your details.
Make sure both cost-center.py and users.csv are in the same directory.
To successfully run the cost-center.py script, you must provide the following inputs:
-
GitHub Token
- A Classic Personal Access Token with
admin,read, andwritepermissions. - Replace the value of
GITHUB_TOKENincost-center.pywith your actual token, or modify the script to read from an environment variable for better security.
- A Classic Personal Access Token with
-
Enterprise Slug Name
- The slug (short name) of your GitHub Enterprise (e.g.,
my-enterprise). - Set the
ENTERPRISEvariable in the script.
- The slug (short name) of your GitHub Enterprise (e.g.,
-
Cost Center ID
- The unique identifier for the cost center you wish to add users to.
- Set the
COST_CENTER_IDvariable in the script.
-
CSV File of Users
- Prepare a
users.csvfile in the same directory as the script. - The first row should be a header (e.g.,
username), and each subsequent row should contain a GitHub username. - Example:
username johndoe janedoe anotheruser
- Prepare a
How to Set Inputs:
- You can either edit the variables directly in
cost-center.py, or for improved security and flexibility, modify the script to read these values from environment variables or prompt for them at runtime.
Example Variable Assignment in Script:
GITHUB_TOKEN = "your_personal_access_token"
ENTERPRISE = "your_enterprise_slug"
COST_CENTER_ID = "cost_center_id"- Run the script:
python cost-center.py
- The script will process the CSV and add users to the specified cost center.
The new_sync.py script (run automatically by the GitHub Actions workflow) syncs one or more user sources to their corresponding cost centers.
Important – GitHub exclusive membership: A user can only belong to one cost center at a time. Adding a user to a new cost center automatically removes them from their previous one. The multi-mapping mode accounts for this.
Set the following repository secrets and run the workflow as before:
| Secret | Description |
|---|---|
ENTERPRISE |
Enterprise slug (e.g. canarys) |
TEAM_SLUG |
Enterprise team slug whose members to sync |
COST_CENTER_ID |
UUID of the target cost center |
TOKEN |
Personal access token |
Use the COST_CENTER_MAPPINGS secret (a JSON array) instead of TEAM_SLUG / COST_CENTER_ID.
Each entry maps a user source to a cost center and must contain:
| Field | Required | Description |
|---|---|---|
cost_center_id |
Always | UUID of the cost center to populate |
team_slug |
One of | Enterprise team slug – members are fetched from the GitHub API |
users |
One of | Explicit list of GitHub login names – no enterprise team needed |
You must provide either
team_slugorusers(not both, but at least one).
Important – use plain ASCII double-quotes only.
When entering the JSON value in the GitHub secrets textarea, always use straight ASCII double-quotes (").
Do not use curly/smart quotes ("") that word processors and some browser autocorrect features insert automatically.
Smart quotes are invisible in most UIs but cause a JSON parse error (Expecting property name enclosed in double quotes).
How it works:
- Mappings are processed in order.
- Once a user is assigned to a cost center, they are skipped for all subsequent mappings — preventing GitHub's exclusive-membership behaviour from moving them back.
- List higher-priority cost centers first (e.g.
PR1beforeMarchCC).
MarchCChas 8 users (managed via themarch-teamenterprise team).- 3 of those users should be in
PR1(budget $50 for additional premium requests). - The remaining 5 stay in
MarchCC(budget $0 — no additional premium requests). - You do not need to create a PR1 enterprise team.
Step 1 – Create the two cost centers in GitHub (Billing and licensing → Cost centers):
| Cost center | Budget |
|---|---|
PR1 |
$50 |
MarchCC |
$0 |
Step 2 – Set the COST_CENTER_MAPPINGS secret (Settings → Secrets and variables → Actions):
[
{
"users": ["user1", "user2", "user3"],
"cost_center_id": "<PR1-cost-center-uuid>"
},
{
"team_slug": "march-team",
"cost_center_id": "<MarchCC-cost-center-uuid>"
}
]Replace user1, user2, user3 with the actual GitHub logins of the 3 users who should receive premium requests, and replace the UUIDs with the IDs shown in the cost center URL on GitHub.
Because PR1 is listed first, those 3 users are claimed by PR1 and will not be re-added to MarchCC when the march-team mapping runs.
Step 3 – Leave TEAM_SLUG and COST_CENTER_ID secrets empty (or remove them). When COST_CENTER_MAPPINGS is set it takes priority.
Step 4 – Run the workflow (Actions → Sync_EntTeam_Cost_Center → Run workflow).
You can also use team_slug for every entry (requires an enterprise team per cost center):
[
{"team_slug": "pr1-team", "cost_center_id": "<PR1-cost-center-uuid>"},
{"team_slug": "march-team", "cost_center_id": "<MarchCC-cost-center-uuid>"}
]To preview changes without applying them, set the DRY_RUN environment variable to true in the workflow step or locally:
DRY_RUN=true COST_CENTER_MAPPINGS='[...]' python new_sync.pyAfter each run the workflow uploads synced_users.csv as an artifact. The CSV includes source (team slug or (direct)) and cost_center columns so you can see exactly which mapping each user was processed under.