A composable Terraform module suite for managing a whole GitHub organization as code — members, teams, repositories, GitHub Actions (variables, secrets and environments), organization rulesets, and organization roles.
Use the whole thing, or just one piece:
# Everything — the root module manages the entire organization.
module "github" {
source = "pelotech/orgkit/github"
version = "~> 0.3"
users = [/* ... */]
teams = [/* ... */]
repositories = [/* ... */]
}
# Or adopt a single submodule.
module "repos" {
source = "pelotech/orgkit/github//modules/repository"
version = "~> 0.2"
teams = { developers = { id = "123", slug = "developers" } }
repositories = [/* ... */]
}The well-known community modules for GitHub-as-code are all years-stale. orgkit is a maintained, modern alternative that also does more than any of them on the organization and Actions axes:
- GitHub Actions — repository and environment variables, repository and
environment secrets, and organization-level secrets/variables with
all/private/selectedvisibility. - Environments — reviewers, wait timers, and deployment branch policies.
- Organization & repository rulesets — a generic ruleset interface with opt-in presets (PR reviews, signed commits, deletion protection, force-push and linear-history), the modern replacement for per-repo branch protection.
- Organization roles — assign predefined org roles (e.g.
security_manager) to teams. - Opinionated defaults, no hardcoded policy — every organization-specific choice (who is an owner, the all-members team, ruleset tuning) is an input, not a baked-in constant.
See docs/DESIGN.md for the design rationale, a comparison with the
existing ecosystem modules, and the feature roadmap.
Three submodules composed by the root as a one-directional pipeline (no cycles):
modules/organization → modules/teams → modules/repository
| Submodule | Manages |
|---|---|
organization |
Org membership, owner/admin assignment, org rulesets (generic interface + presets). |
teams |
Teams, team memberships, the optional all-members team, org-role assignments. |
repository |
Repositories, team↔repo grants, per-repo rulesets, repo/environment Actions variables & secrets, environments. |
The root additionally manages organization-level Actions secrets and variables (they need both secret values and repository ids, so they live at the composition root).
examples/complete— manage an entire organization.examples/repository— use only the repository submodule.
orgkit is pre-1.0 and the input interface may still change between minor
releases. Pin a version (version = "~> 0.2") and review the changelog before
upgrading. Breaking input changes so far, since the initial release:
unsafe_mergesremoved — useallow_squash_merge/allow_rebase_merge(and the other granular merge controls) instead.baseline_rulesetandrequire_signed_commitsremoved — useenabled_presets(defaults reproduce the previous behavior) andorganization_rulesets; per-repo rules viarepositories[].rulesets.- Default merge strategy is now squash-only (
allow_squash_merge = true,allow_merge_commit = false). Set theallow_*fields per repository to change it.
| Name | Version |
|---|---|
| terraform | >= 1.9 |
| github | ~> 6.0 |
| Name | Version |
|---|---|
| github | ~> 6.0 |
| Name | Source | Version |
|---|---|---|
| organization | ./modules/organization | n/a |
| repository | ./modules/repository | n/a |
| teams | ./modules/teams | n/a |
| Name | Type |
|---|---|
| github_actions_organization_secret.internal | resource |
| github_actions_organization_variable.internal | resource |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| all_members_team_name | Name of the all-members team. | string |
"everyone" |
no |
| create_all_members_team | Create an all-members team that grants default read access to non-restricted repositories. | bool |
true |
no |
| enabled_presets | Names of built-in ruleset presets to enable. See modules/organization/main.tf local.presets. | list(string) |
[ |
no |
| organization_rulesets | Organization rulesets, keyed by name. Merged with (and overridden by name by) the presets selected via enabled_presets. bypass_actors uses raw numeric actor_id (OrganizationAdmin needs none); team-name bypass is per-repo only. |
map(object({ |
{} |
no |
| organization_secrets | Visibility config for org-level shared secrets. Values come from var.secrets.org. | map(object({ |
{} |
no |
| organization_variables | Org-level shared Actions variables (plaintext value + visibility). | map(object({ |
{} |
no |
| owners | Explicit set of owner usernames (mapped to GitHub's admin role). Ignored when owners_team is set. |
set(string) |
[] |
no |
| owners_team | Convenience: derive organization owners from the members of this team, by name. null (default) means use the explicit owners set instead. There isno hardcoded owners team — the consumer chooses. |
string |
null |
no |
| repositories | Repositories and which teams may access them. See the repository submodule for the full object schema and validations. | list(object({ |
[] |
no |
| secrets | Decrypted secret values keyed by scope. Supplied already-decrypted by the caller; this module never performs decryption. | object({ |
{ |
no |
| teams | Teams and their membership. A team may hold predefined organization-level GitHub roles via org_roles. Repository access is granted per repository viareaders/writers/admins, not through org roles. |
list(object({ |
[] |
no |
| users | Organization members. fullname/email are informational; membership is keyed on username. |
list(object({ |
[] |
no |
| Name | Description |
|---|---|
| all_members_team_id | ID of the all-members team, or null when create_all_members_team = false. |
| members | Map of organization member username => managed membership username. |
| repository_ids | Map of repository name => numeric repo_id. |
| teams | Map of team name => { id, slug }. |