Parent
#1359
Problem Statement
Firestartr needs to manage GitHub Actions organization-level variables declaratively. Currently there is no TFM module that provisions github_actions_organization_variable resources. Consumers need a ghaps-compatible module that accepts a single config input, supports all three visibility modes (all, private, selected), resolves repository references by full name, and exposes which variables are managed so Firestartr can track adoption.
Solution
A new module github-org-variables-section that accepts a variables map keyed by variable name, creates one github_actions_organization_variable per entry via for_each, resolves selectedRepositoryIds from repo full names using data.github_repository, and outputs the set of managed variable names and their resource IDs.
User Stories
- As a Firestartr operator, I want to define organization-level GitHub Actions variables declaratively in
terraform.tfvars.json, so that they are provisioned consistently across all CRs.
- As a Firestartr operator, I want variables with
all visibility to be accessible to every repository in the organization, so that common CI configuration is shared automatically.
- As a Firestartr operator, I want variables with
selected visibility scoped to specific repositories, so that sensitive values are only exposed to intended repos.
- As a Firestartr operator, I want variables with
private visibility to be accessible only to private repositories, so that internal configuration is not exposed to public repos.
- As a Firestartr operator, I want to reference repositories by human-readable full names (e.g.
my-org/my-repo) rather than numeric IDs, so that configuration is self-documenting.
- As a Firestartr operator, I want variable names validated at plan time against GitHub Actions naming rules, so that I catch errors before apply.
- As a Firestartr operator, I want
visibility validated against the allowed enum (all, private, selected), so that typos are caught early.
- As a Firestartr operator, I want to know which variable names are managed by this module after apply, so that Firestartr can compute adoption/drift.
- As a Firestartr operator, I want a map of variable names to resource IDs in the output, so that downstream tooling can verify state.
- As a Firestartr operator, I want removing a variable from the config to cleanly destroy only that variable, so that cleanup is predictable and safe.
- As a module consumer, I want the config shape to use camelCase field names consistent with other TFM github modules, so that the contract is familiar.
Implementation Decisions
-
Module name: github-org-variables-section — follows the github-repo-secrets-section naming convention for a section that manages a map of named resources via for_each.
-
Config shape: The config variable is a single object({ variables = map(object({...})) }). The map keys are the variable names. This guarantees uniqueness by construction and matches the github-repo-secrets-section pattern.
Config type contract:
object({
variables = map(object({
value = string
visibility = string
selectedRepositoryIds = optional(list(string))
}))
})
-
Visibility enum: Validated to all, private, selected — complete coverage of the provider enum.
-
Repository ID resolution: When visibility is selected, selectedRepositoryIds accepts repo full names (strings, e.g. my-org/my-repo). The module resolves them to numeric IDs via data.github_repository data sources, matching the provider documentation example.
-
selectedRepositoryIds omission: When visibility is not selected, the selected_repository_ids argument is omitted entirely from the resource block (not passed as null). This follows the provider schema where it is optional.
-
Variable name validation: A validation block ensures all map keys match ^[A-Za-z0-9_]+$, matching GitHub Actions naming rules and the pattern from github-repo-secrets-section.
-
managed_variables output: A simple list(string) output containing the keys of github_actions_organization_variable.this. No accumulator input — the gh-provisioner handles diffing externally. This matches the github-repo-secrets-section output pattern.
-
variable_ids output: A map(string) of variable_name → resource id, providing stable identifiers for Firestartr reconciliation.
-
No org field: The config does not include an org field. The GitHub provider knows the org from its configuration, and repo full names already encode the org. This matches github-org-settings and github-org-rulesets.
-
value not marked sensitive: The value field is treated as plaintext. GitHub Actions variables, while potentially containing non-public strings, are not encrypted secrets in the provider model and are not marked sensitive.
-
Config field naming: All config fields use camelCase (selectedRepositoryIds, visibility, value) to match the JSON input origin and the established TFM convention across all github modules.
-
Delete behavior: Safe. Removing a variable entry from the map destroys only that github_actions_organization_variable. No broader org impact. Dependent CI workflows may fail at runtime but no infrastructure mutation occurs.
-
Resource address for import: github_actions_organization_variable.this["<variable_name>"]. Import ID is the variable name itself.
-
Provider version: github >= ~> 6.0, Terraform >= 1.5 — consistent with all existing TFM github modules.
Testing Decisions
- Tests should validate that
terraform init and terraform validate pass with a representative _examples/basic/ config.
- The example config should cover all three visibility modes: one
all, one private, one selected with repo references.
- Test seam is the
_examples/basic/ directory using the same jsondecode(file(...)).config pattern as other modules.
- Prior art: follow the
github-org-settings/_examples/basic/ pattern — a main.tf calling the module and a config.json with the config shape.
Out of Scope
- Encrypted secrets (these are variables, not secrets; use
github-repo-secrets-section for encrypted values).
managed_variables accumulator input (the provisioner handles diffing externally).
- Repository-to-org-variable assignment beyond the
selected visibility list (the GitHub API does not support per-variable repo assignments outside this mechanism).
- Migration or import tooling — standard
terraform import documentation is sufficient.
- Multi-org support (one module = one provider-configured org).
Further Notes
- The module follows the
ghaps contract: single variable "config", one CR, one state, one terraform.tfvars.json.
- After implementation, run
terraform-docs . from the module directory to regenerate README.md.
- Changelog will be updated by Release Please automation.
- The
data.github_repository lookups for selectedRepositoryIds will cause the module to fail at plan time if a referenced repo does not exist — this is intentional for early feedback.
Parent
#1359
Problem Statement
Firestartr needs to manage GitHub Actions organization-level variables declaratively. Currently there is no TFM module that provisions
github_actions_organization_variableresources. Consumers need aghaps-compatible module that accepts a singleconfiginput, supports all three visibility modes (all,private,selected), resolves repository references by full name, and exposes which variables are managed so Firestartr can track adoption.Solution
A new module
github-org-variables-sectionthat accepts avariablesmap keyed by variable name, creates onegithub_actions_organization_variableper entry viafor_each, resolvesselectedRepositoryIdsfrom repo full names usingdata.github_repository, and outputs the set of managed variable names and their resource IDs.User Stories
terraform.tfvars.json, so that they are provisioned consistently across all CRs.allvisibility to be accessible to every repository in the organization, so that common CI configuration is shared automatically.selectedvisibility scoped to specific repositories, so that sensitive values are only exposed to intended repos.privatevisibility to be accessible only to private repositories, so that internal configuration is not exposed to public repos.my-org/my-repo) rather than numeric IDs, so that configuration is self-documenting.visibilityvalidated against the allowed enum (all,private,selected), so that typos are caught early.Implementation Decisions
Module name:
github-org-variables-section— follows thegithub-repo-secrets-sectionnaming convention for a section that manages a map of named resources viafor_each.Config shape: The
configvariable is a singleobject({ variables = map(object({...})) }). The map keys are the variable names. This guarantees uniqueness by construction and matches thegithub-repo-secrets-sectionpattern.Config type contract:
Visibility enum: Validated to
all,private,selected— complete coverage of the provider enum.Repository ID resolution: When
visibilityisselected,selectedRepositoryIdsaccepts repo full names (strings, e.g.my-org/my-repo). The module resolves them to numeric IDs viadata.github_repositorydata sources, matching the provider documentation example.selectedRepositoryIds omission: When
visibilityis notselected, theselected_repository_idsargument is omitted entirely from the resource block (not passed asnull). This follows the provider schema where it is optional.Variable name validation: A
validationblock ensures all map keys match^[A-Za-z0-9_]+$, matching GitHub Actions naming rules and the pattern fromgithub-repo-secrets-section.managed_variables output: A simple
list(string)output containing the keys ofgithub_actions_organization_variable.this. No accumulator input — the gh-provisioner handles diffing externally. This matches thegithub-repo-secrets-sectionoutput pattern.variable_ids output: A
map(string)ofvariable_name→ resourceid, providing stable identifiers for Firestartr reconciliation.No
orgfield: The config does not include an org field. The GitHub provider knows the org from its configuration, and repo full names already encode the org. This matchesgithub-org-settingsandgithub-org-rulesets.value not marked sensitive: The
valuefield is treated as plaintext. GitHub Actions variables, while potentially containing non-public strings, are not encrypted secrets in the provider model and are not markedsensitive.Config field naming: All config fields use camelCase (
selectedRepositoryIds,visibility,value) to match the JSON input origin and the established TFM convention across all github modules.Delete behavior: Safe. Removing a variable entry from the map destroys only that
github_actions_organization_variable. No broader org impact. Dependent CI workflows may fail at runtime but no infrastructure mutation occurs.Resource address for import:
github_actions_organization_variable.this["<variable_name>"]. Import ID is the variable name itself.Provider version:
github >= ~> 6.0, Terraform>= 1.5— consistent with all existing TFM github modules.Testing Decisions
terraform initandterraform validatepass with a representative_examples/basic/config.all, oneprivate, oneselectedwith repo references._examples/basic/directory using the samejsondecode(file(...)).configpattern as other modules.github-org-settings/_examples/basic/pattern — amain.tfcalling the module and aconfig.jsonwith the config shape.Out of Scope
github-repo-secrets-sectionfor encrypted values).managed_variablesaccumulator input (the provisioner handles diffing externally).selectedvisibility list (the GitHub API does not support per-variable repo assignments outside this mechanism).terraform importdocumentation is sufficient.Further Notes
ghapscontract: singlevariable "config", one CR, one state, oneterraform.tfvars.json.terraform-docs .from the module directory to regenerateREADME.md.data.github_repositorylookups forselectedRepositoryIdswill cause the module to fail at plan time if a referenced repo does not exist — this is intentional for early feedback.