One enforced, secure-by-default baseline for an entire GitHub organization β billing & profile metadata, least-privilege member privileges, no-accidental-public repo/Pages creation, and an always-on security baseline (GHAS, Dependabot, secret scanning + push protection, web commit sign-off) for every new repository β now extended with optional org-governance posture controls: security-manager team designation and organization user blocks. Built for integrations/github v6.x.
This module manages the single github_organization_settings resource β the org-wide policy object that governs the organization your provider points at (owner / GITHUB_OWNER) β and now optionally layers two additional org-governance posture controls behind the same boundary. There is exactly one settings object per organization; the two new controls are opt-in for_each collections that default to {}.
- π§Ύ Profile & billing β sets the required
billing_emailplus optional public profile fields (name,description,company,blog,email,twitter_username,location). - π Least-privilege member privileges β base member permission defaults to
read; public repository and public Pages creation default to off; private-repo forking defaults to off. - π‘οΈ Security baseline for new repos β GHAS, Dependabot alerts + security updates, dependency graph, secret scanning, and secret-scanning push protection all default on, plus org-wide web commit sign-off.
- π’ Enterprise-aware β
members_can_create_internal_repositoriesdefaults tonullso the module applies cleanly on Free / Team orgs and is only managed when you opt in on Enterprise Cloud. - π§β
βοΈ Security-manager designation (optional) β designate one or more teams as organization security managers viasecurity_managers(default{}). Note: the underlying resource is deprecated β see Architecture Notes for the recommended org-role path. - π« Organization user blocks (optional) β block abusive/spam accounts org-wide via
blocked_users(default{}), authoritative per username. - π€ Audit-friendly outputs β emits the org settings
id, a typedsecurity_for_new_repositoriessummary, and the newsecurity_manager_ids/blocked_user_idsmaps for compliance dashboards.
π‘ Why it matters: this is the org-level guardrail that decides what every member can do and what security every new repository inherits. Setting it once, in code, replaces dozens of clicks and removes the single biggest source of accidental public exposure at a regulated institution.
βΉοΈ Backward compatible: the two new collections default to
{}. A caller that sets only org settings creates neither new resource and sees an identical plan/apply to before the module was extended.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- β Star this repository to help others discover this Terraform module.
- π€ Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
This module is part of the Org Governance family. It is the only governance module that consumes a sibling output: a team slug from terraform-github-team to designate security managers. The keystone terraform-github-repository (GitHub-dark) inherits this module's new-repo security baseline.
flowchart LR
team["terraform-github-team"]
subgraph govern["Org Governance family"]
orgset["terraform-github-organization-settings<br/>β
THIS"]
roles["terraform-github-organization-roles"]
membership["terraform-github-membership"]
orgrule["terraform-github-organization-ruleset"]
end
repo["terraform-github-repository<br/>keystone"]
team -->|team slug| orgset
roles -.->|modern security-manager role lives here| orgset
orgset -.->|new-repo security baseline| repo
style orgset fill:#8957E5,color:#fff
style repo fill:#24292F,color:#fff
Consumes: a team
slug(fromterraform-github-team) for security managers; usernames for blocks. Emits:id, governance flags, and thesecurity_manager_ids/blocked_user_idsmaps. See the Cross-Module Contract.
github_organization_settings.this is the primary resource (always created). The two optional for_each collections extend the module into a small org-governance aggregation; both default to {}, so a settings-only caller creates neither.
flowchart TD
subgraph mod["terraform-github-organization-settings β one provider/org boundary"]
settings["github_organization_settings Β· this<br/>PRIMARY β org-wide singleton<br/>profile Β· member privileges Β· new-repo security baseline"]
sm["github_organization_security_manager Β· security_managers<br/>OPTIONAL Β· for_each map(object) Β· default {}"]
block["github_organization_block Β· blocked_users<br/>OPTIONAL Β· for_each map(object) Β· default {}"]
end
org["GitHub Organization<br/>provider owner / GITHUB_OWNER"]
settings -->|governs| org
sm -->|designates security-manager teams in| org
block -->|blocks users from| org
style settings fill:#8957E5,color:#fff
style sm fill:#6E40C9,color:#fff
style block fill:#6E40C9,color:#fff
style org fill:#24292F,color:#fff
Resource inventory
github_organization_settings.thisβ PRIMARY / keystone. The org-wide settings singleton: billing/profile, member privileges, feature toggles, and the new-repo security baseline. Always created.github_organization_security_manager.security_managersβ OPTIONALfor_eachcollection overmap(object({ team_slug })), default{}. Designates teams as organization security managers.β οΈ Deprecated provider resource β prefer the built-insecurity_managerorg role (see Architecture Notes).github_organization_block.blocked_usersβ OPTIONALfor_eachcollection overmap(object({ username })), default{}. Blocks users from the organization; authoritative per username.
- Terraform
>= 1.12.0 - Provider
integrations/github~> 6.0(validated against 6.12.1) β never the deprecatedhashicorp/github. - Schema note:
github_organization_security_manageris deprecated in the 6.x provider in favour ofgithub_organization_role_team(the built-insecurity_managerorganization role). It still applies βterraform validateemits a Deprecated Resource warning, not an error. - Edition note:
members_can_create_internal_repositoriesis Enterprise Cloud only; the advanced-security / secret-scanning*_for_new_repositoriesflags require a GHAS license for private/internal repos.
terraform-github-organization-settings/
βββ providers.tf # github provider requirement (~> 6.0); no provider {} block
βββ variables.tf # billing_email (required) + profile + privileges + security baseline + optional posture controls
βββ main.tf # github_organization_settings.this (primary) + 2 optional for_each collections
βββ outputs.tf # id + governance/audit passthroughs + security_manager_ids / blocked_user_ids
βββ README.md # this file
βββ SCOPE.md # token scopes, prerequisites, consumes/emits tables, gotchas
βΉοΈ Still four
.tffiles β the module grew from one resource to three (one primary + two optional collections) without adding files.
The minimal call needs only billing_email; every other field inherits a secure default, and both posture-control collections default to {}.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
}βΉοΈ The organization itself is the provider's
owner/GITHUB_OWNERβ it is not a module variable. Point the provider at the org you intend to govern before applying.
Sourced from SCOPE.md. The module now has a real Consumes dependency (a team slug for security managers); everything else it Emits.
Consumes
| Input | Type | Source |
|---|---|---|
security_managers[*].team_slug |
string | terraform-github-team β slug (wire module.<team>.slug) |
blocked_users keys (and optional username) |
string | Caller-supplied GitHub usernames (public account metadata) |
Emits
| Output | Type | Typically consumed by |
|---|---|---|
id |
string | Governance dashboards / audit tooling; a stable reference to the org settings object |
billing_email |
string | Compliance reporting (non-sensitive org metadata) |
default_repository_permission |
string | Access-review tooling auditing org-wide base permission |
members_can_create_public_repositories |
bool | Public-exposure audit checks / policy assertions |
web_commit_signoff_required |
bool | Commit-policy compliance reporting |
security_for_new_repositories |
object | Compliance dashboards verifying the new-repo security baseline |
security_manager_ids |
map(string) | Access-review tooling auditing which teams hold org security-manager rights |
blocked_user_ids |
map(string) | Moderation/audit tooling tracking org-level blocks |
βΉοΈ This is still an org-governance module β it emits no repository name. Repository/team modules wire into the org, not into this resource (except the team
slugthis module consumes).
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
name = "Casey Wood"
description = "Technology & operations for the regulated financial."
company = "Casey Wood, Inc."
blog = "https://www.financialpartners.com"
email = "opensource@financialpartners.com"
location = "Agawam, MA"
twitter_username = "FarmCreditcasey"
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
# Members get the narrowest base access and cannot create any repository.
default_repository_permission = "none"
members_can_create_repositories = false
# Pages fully disabled.
members_can_create_pages = false
# Project boards off where unused.
has_organization_projects = false
has_repository_projects = false
# Security baseline left at its secure-on defaults (GHAS licensed).
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
default_repository_permission = "read"
members_can_create_repositories = true
members_can_create_public_repositories = false # secure default, made explicit
members_can_create_private_repositories = true
members_can_fork_private_repositories = false # secure default, made explicit
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
# Opt in ONLY after security review of the open-source program.
members_can_create_public_repositories = true
members_can_create_public_pages = true
}
β οΈ Turning on public repository/Pages creation widens internet exposure. Document the review that approved it before applying at a regulated institution.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
# ENTERPRISE-ONLY: valid only on a GitHub Enterprise Cloud org.
members_can_create_internal_repositories = true
}π Leave
members_can_create_internal_repositoriesat itsnulldefault on Free/Team orgs β sending a value to a non-Enterprise org fails the apply.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
web_commit_signoff_required = true
advanced_security_enabled_for_new_repositories = true
dependabot_alerts_enabled_for_new_repositories = true
dependabot_security_updates_enabled_for_new_repositories = true
dependency_graph_enabled_for_new_repositories = true
secret_scanning_enabled_for_new_repositories = true
secret_scanning_push_protection_enabled_for_new_repositories = true
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
# GHAS not licensed β these MUST be false on private/internal orgs or apply fails.
advanced_security_enabled_for_new_repositories = false
secret_scanning_enabled_for_new_repositories = false
secret_scanning_push_protection_enabled_for_new_repositories = false
# Dependabot + dependency graph stay on β they are free.
dependabot_alerts_enabled_for_new_repositories = true
dependabot_security_updates_enabled_for_new_repositories = true
dependency_graph_enabled_for_new_repositories = true
}π‘ Dependabot alerts/updates and the dependency graph are available without GHAS; only advanced security and secret scanning on private/internal repos require the license.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
web_commit_signoff_required = false # relax org-wide; record the approving change
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
members_can_create_pages = true
members_can_create_public_pages = false
members_can_create_private_pages = true
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
has_organization_projects = false
has_repository_projects = false
}variable "orgs" {
type = map(object({
billing_email = string
members_can_create_public_repositories = optional(bool, false)
members_can_create_internal_repositories = optional(bool) # null unless Enterprise
}))
default = {
prod = {
billing_email = "github-billing@financialpartners.com"
}
sandbox = {
billing_email = "github-sandbox@financialpartners.com"
members_can_create_public_repositories = true
}
}
}
# One provider alias per org (configured in the root), one module call per org.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
for_each = var.orgs
providers = { github = github.by_org[each.key] }
billing_email = each.value.billing_email
members_can_create_public_repositories = each.value.members_can_create_public_repositories
members_can_create_internal_repositories = each.value.members_can_create_internal_repositories
}βΉοΈ The org is a provider concern, so multi-org governance is done with one provider alias per org, not by passing an org name into the module.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
}
# Feed the typed baseline into a compliance assertion / dashboard.
output "new_repo_security_baseline" {
value = module.org_settings.security_for_new_repositories
}
check "secret_scanning_on" {
assert {
condition = module.org_settings.security_for_new_repositories.secret_scanning_push_protection
error_message = "Secret-scanning push protection must be enabled for new repositories."
}
}module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
members_can_create_public_repositories = false
default_repository_permission = "read"
}
# Repositories created after the org baseline inherit its new-repo security defaults.
module "repository" {
source = "git::https://github.com/microsoftexpert/terraform-github-repository?ref=v1.0.0"
name = "loan-origination-svc"
visibility = "private"
depends_on = [module.org_settings] # ensure the baseline is applied first
}π‘ Apply org settings before the first repository in a fresh org so new repos inherit the intended security baseline.
depends_onmakes the ordering explicit.
15 Β· Settings-only β behavior unchanged after the upgrade
# The two new collections default to {}. A caller that sets only org settings
# creates NEITHER github_organization_security_manager NOR github_organization_block
# β an identical plan/apply to before this module was extended.
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
}π‘ Zero change for existing callers: omitting
security_managersandblocked_usersleaves them{}, so no extra resources are planned.
16 Β· Designate a security-manager team (wired from terraform-github-team)
module "appsec_team" {
source = "git::https://github.com/microsoftexpert/terraform-github-team?ref=v1.0.0"
name = "application-security"
#...team configuration...
}
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
security_managers = {
appsec = {
team_slug = module.appsec_team.slug # wire the SLUG, not the display name
}
}
}
β οΈ Deprecated resource.github_organization_security_manageris deprecated by the provider in favour of the built-insecurity_managerorganization role assigned viagithub_organization_role_team(seeterraform-github-organization-roles). Prefer that for new work; this collection remains for back-compat. See Architecture Notes.
17 Β· Block users from the organization (user moderation)
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
# Keyed by username β the map key IS the GitHub login.
blocked_users = {
"spammer-account" = {}
"abusive-user-123" = {}
}
}π Authoritative per username. Each entry fully owns the block for that login; removing an entry unblocks the user on the next apply. Manage the complete set of org blocks here, or not at all.
18 Β· ποΈ End-to-end composition β team β security managers + settings + blocks
module "appsec_team" {
source = "git::https://github.com/microsoftexpert/terraform-github-team?ref=v1.0.0"
name = "application-security"
}
module "org_settings" {
source = "git::https://github.com/microsoftexpert/terraform-github-organization-settings?ref=v1.0.0"
billing_email = "github-billing@financialpartners.com"
# Org-wide secure baseline (secure defaults shown explicitly).
default_repository_permission = "read"
members_can_create_public_repositories = false
# Designate the security team (deprecated resource; see Architecture Notes).
security_managers = {
appsec = { team_slug = module.appsec_team.slug }
}
# Moderate abusive accounts.
blocked_users = {
"spammer-account" = {}
}
}
output "security_manager_team_ids" {
value = module.org_settings.security_manager_ids
}π‘ The full suite wired via outputs β inputs: a team's
slugflows intosecurity_managers, the org baseline applies to every new repository, and abusive accounts are blocked β all under one provider/org boundary.
Required
billing_emailβ the organization billing email (validated as an email). The one field the provider cannot infer.
Organization profile (all optional, default null = leave unchanged)
name,description,company,blog,email,twitter_username,location
Member privileges (least-privilege secure defaults)
default_repository_permission(none/read/write/admin, defaultread)members_can_create_repositories(defaulttrue)members_can_create_public_repositories(defaultfalse)members_can_create_private_repositories(defaulttrue)members_can_create_internal_repositories(Enterprise-only, defaultnull)members_can_fork_private_repositories(defaultfalse)members_can_create_pages(defaulttrue),members_can_create_public_pages(defaultfalse),members_can_create_private_pages(defaulttrue)
Organization feature toggles
has_organization_projects(defaulttrue),has_repository_projects(defaulttrue)
Commit policy & new-repo security baseline (secure-by-default ON)
web_commit_signoff_required(defaulttrue)advanced_security_enabled_for_new_repositories(defaulttrue, needs GHAS)dependabot_alerts_enabled_for_new_repositories(defaulttrue)dependabot_security_updates_enabled_for_new_repositories(defaulttrue)dependency_graph_enabled_for_new_repositories(defaulttrue)secret_scanning_enabled_for_new_repositories(defaulttrue, needs GHAS for private/internal)secret_scanning_push_protection_enabled_for_new_repositories(defaulttrue, needs GHAS for private/internal)
Optional org-governance posture controls (both default {} = no change)
security_managersβmap(object)keyed by a stable handle; designates teams as org security managers.β οΈ deprecated resource β see Architecture Notes.blocked_usersβmap(object)keyed by username; blocks users from the org (authoritative per username).
Full object schemas for the nested map inputs
# security_managers β designate teams as organization security managers (DEPRECATED resource)
variable "security_managers" {
type = map(object({
team_slug = string # slug of the team to designate β wire module.<team>.slug (NOT the display name)
}))
default = {}
}
# blocked_users β block users from the organization (authoritative per username)
variable "blocked_users" {
type = map(object({
username = optional(string) # defaults to the map key; override only if the key must differ from the login
}))
default = {}
}| Output | Description | Sensitive |
|---|---|---|
id |
The organization settings ID (the organization's GitHub ID). Primary cross-module reference. | β |
billing_email |
The org billing email currently applied (non-sensitive metadata). | β |
default_repository_permission |
The base permission every member has on all org repos (none/read/write/admin). |
β |
members_can_create_public_repositories |
Whether members may create public repos β the key public-exposure flag for audit. | β |
web_commit_signoff_required |
Whether web-based commits org-wide require a sign-off. | β |
security_for_new_repositories |
Typed object summarizing the new-repo security baseline (advanced security, Dependabot alerts/updates, dependency graph, secret scanning, push protection). | β |
security_manager_ids |
Map keyed by the same handle as security_managers β the designated team's GitHub team ID. {} when none designated. |
β |
blocked_user_ids |
Map keyed the same as blocked_users (by username) β the block resource's ID. {} when no users blocked. |
β |
βΉοΈ No output is
sensitiveβ this resource holds no secret values and emits no member PII. Team slugs and GitHub usernames are public metadata.
idis the organization's GitHub ID.github_organization_settingsexposes onlyidas a computed attribute β there is nonode_id,slug,repo_id,full_name, orhtml_urlon this resource (unlike repositories or teams). The remaining settings outputs are governance flags read back off the applied resource, not separate identifiers. To import an existing settings object, use the numeric organization ID:terraform import module.org_settings.github_organization_settings.this <org_id>.- Flat primary resource β no nested blocks. Every
github_organization_settingsargument is a direct typed scalar, so it contributes nodynamicblocks. The two optional collections are rendered withfor_eachover amap(object(...))keyed by a stable caller string β nevercountβ so adding/removing one entry never re-indexes the others. - Singleton, not ForceNew. There is one settings object per org; the module mutates it in place. The org it targets is fixed by the provider's
owner, so pointing the provider at a different org and re-applying governs a different organization β there is no immutablenamefield on this resource to recreate. nullmeans "leave unmanaged." Profile fields andmembers_can_create_internal_repositoriesdefault tonullso an unset value is left untouched rather than blanked. This is what lets the same code run on Free/Team and Enterprise orgs.- Enterprise-only field.
members_can_create_internal_repositoriesis valid only on GitHub Enterprise Cloud orgs. Passing a non-null value to a Free/Team org causes an apply error β keep itnullunless the org is part of an enterprise. - GHAS-gated fields.
advanced_security_enabled_for_new_repositories,secret_scanning_enabled_for_new_repositories, andsecret_scanning_push_protection_enabled_for_new_repositoriesrequire a GitHub Advanced Security license to enable on private/internal repos. Without GHAS licensed these must befalseor the apply fails (they are free on public repos). - New-repo baseline only. The
*_for_new_repositoriessettings apply to repositories created after this setting takes effect; existing repositories are unaffected. Remediate existing repos with the repository / Dependabot / Actions modules. - Org settings β rulesets. This module sets org-wide member privileges and the new-repo security baseline. Branch-level controls (required reviews, status checks, no force-push) belong to
terraform-github-organization-ruleset/terraform-github-repository-rulesetβ prefers rulesets over legacygithub_branch_protectionfor those. The two are complementary, not alternatives. - No secrets handled. This module holds no secret values, so there is no
plaintext_value/encrypted_valueconcern and no output issensitiveβbilling_emailis org billing metadata, not member PII; all other outputs are non-secret governance flags, team slugs, or public usernames. - π Security managers: deprecated resource β prefer the built-in org role. The prompt3 resolve-first check confirmed (and
terraform validatewarns) thatgithub_organization_security_manageris deprecated in the integrations/github 6.x provider. The modern path is to assign the built-insecurity_managerorganization role to a team viagithub_organization_role_team, which the provider documents directly:
data "github_organization_roles" "all" {}
locals {
security_manager_id = one([for r in data.github_organization_roles.all.roles: r.role_id if r.name == "security_manager"])
}
resource "github_organization_role_team" "security_managers" {
role_id = local.security_manager_id
team_slug = "application-security"
}That resource belongs in terraform-github-organization-roles, and should prefer it for new work. This module keeps security_managers (the deprecated resource) only for back-compat / existing state; migrate when convenient. Either way, wire the team slug (from terraform-github-team), never the display name.
- User blocks are authoritative per username. Each
blocked_usersentry fully owns the org-level block for that login. Removing an entry unblocks the user on the next apply. Manage the complete set of org blocks here, or leave the collection{}and manage blocks elsewhere β do not split ownership of a single username across modules. The map key is the username (the optionalusernamefield overrides it only when the resource-address key must differ from the actual login, viacoalesce(each.value.username, each.key)). - Both collections default
{}β zero-change upgrade. A settings-only caller plans/applies identically to the pre-extension module; neithergithub_organization_security_managernorgithub_organization_blockis created until you populate the corresponding map. - Small
for_each, modest API cost. These collections are typically a handful of entries, so they do not carry the secondary-rate-limit risk that large bulk modules (membership, collaborators) do β but keep org-governance applies in their own small plan to avoid colliding with large bulk applies.
- π Private by default β public repository and public Pages creation default off; private-repo forking defaults off. Public exposure is always an explicit, reviewed opt-in.
- πͺͺ Least privilege β base member permission defaults to
read, neverwrite/admin. Broaden access per-repo via collaborators/teams, not org-wide. Security-manager designation gives a team org-wide security visibility without org-owner rights. - π‘οΈ Security on by default β GHAS, Dependabot, dependency graph, secret scanning + push protection, and web commit sign-off default on so every new repo inherits the baseline; each opt-out is documented inline.
- π« Safe by omission β the optional posture controls default to
{}. The module never blocks a user or designates a security manager unless you explicitly say so; removing an entry reverses the action (unblock / undesignate) on the next apply. - π’ Edition-safe β Enterprise-only and GHAS-gated fields default to
null/are clearly flagged so the module applies cleanly on Free/Team orgs. - π§© Composable β no
provider {}block, noowner/token/app_authvariables; the caller configures auth and the target org. Sibling wiring is byslug/id, never by name. - π§Ύ Auditable β emits the exact governance flags compliance dashboards need (including who holds security-manager rights and who is blocked), with zero secrets in outputs.
terraform init -backend=false
terraform validate
terraform fmt -check
terraform plan
terraform apply
terraform output
β οΈ Always pin the module to a tag β?ref=v1.0.0β never a branch. Org-wide settings affect every member and every new repository; a movingrefis unacceptable for a governance baseline.
The offline proof gate (no GitHub credentials required):
terraform fmt -check # zero formatting differences
terraform validate # valid config (a Deprecated Resource WARNING on
# github_organization_security_manager is expected, not an error)
tflint # core rules β no dedicated GitHub ruleset existsβΉοΈ
terraform validatesucceeds with the deprecation warning present β that warning is the provider confirming the recommended migration to the built-insecurity_managerorg role.
A sample terraform output after applying the end-to-end composition (Example 18):
billing_email = "github-billing@financialpartners.com"
blocked_user_ids = {
"spammer-account" = "12345678"
}
default_repository_permission = "read"
id = "1234567"
members_can_create_public_repositories = false
security_for_new_repositories = {
"advanced_security" = true
"dependabot_alerts" = true
"dependabot_security_updates" = true
"dependency_graph" = true
"secret_scanning" = true
"secret_scanning_push_protection" = true
}
security_manager_ids = {
"appsec" = "7654321"
}
web_commit_signoff_required = true
| Symptom | Cause | Resolution |
|---|---|---|
403 / Resource not accessible on apply |
Provider identity lacks org-admin rights | The token/App needs the admin:org classic scope (or Organization administration: write fine-grained). See SCOPE.md. |
Apply fails enabling members_can_create_internal_repositories |
Org is Free/Team, not Enterprise Cloud | Leave the variable null on non-Enterprise orgs. |
| Apply fails enabling advanced security / secret scanning | GHAS not licensed for private/internal repos | Set the GHAS-gated flags to false, or license GitHub Advanced Security. |
| New repos don't inherit the security baseline | Repos were created before the settings applied | The *_for_new_repositories flags affect only new repos; remediate existing repos with the repository/Dependabot modules. |
| Settings drift back after a manual UI change | Someone edited settings in the GitHub UI | Re-run terraform apply β Terraform is the source of truth for org governance. |
billing_email must be a valid email address |
billing_email failed the validation regex |
Supply a syntactically valid address (e.g. billing@example.com). |
Warning: Deprecated Resource on github_organization_security_manager |
The provider deprecated this resource | Expected β it is a warning, not an error. Migrate to the built-in security_manager org role via github_organization_role_team (terraform-github-organization-roles) when convenient. |
Security-manager designation fails / team not found |
Wrong value passed β display name instead of slug, or team not yet created | Pass the team slug (wire module.<team>.slug); ensure the team exists first (depends_on / output wiring). |
Each blocked_users entry must resolve to a valid GitHub username⦠|
A key/username isn't a valid GitHub login |
Use a valid login: 1β39 chars, alphanumeric with single internal hyphens, no leading/trailing hyphen. |
| A previously blocked user can suddenly interact again | Their blocked_users entry was removed |
Blocks are authoritative per username β removing an entry unblocks on apply. Re-add the entry to re-block. |
| Secondary rate limit during a large combined apply | Org-governance apply bundled with a big bulk for_each run |
Keep org-settings in its own small plan/apply, separate from bulk membership/collaborator applies. |
- integrations/github provider β Organization Settings resource reference
- integrations/github provider β Organization Security Manager resource (deprecated) and Organization Role Team resource (the replacement)
- integrations/github provider β Organization Block resource reference
- integrations/github provider β Organization data source (read-back attributes)
- module β terraform-github-organization-roles (org roles; the built-in
security_managerrole β recommended path) - module β terraform-github-organization-ruleset (org-wide branch/tag rules)
- module β terraform-github-repository (keystone; inherits the new-repo baseline)
- module β terraform-github-team (emits the
slugconsumed for security managers) - module β terraform-github-membership (org membership & roles)
- GitHub Docs β Managing organization settings, GitHub Advanced Security, security managers, blocking users, and the edition feature matrix