Skip to content

Spec: github-org-variables-section module #1360

Description

@frmadem

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

  1. 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.
  2. 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.
  3. As a Firestartr operator, I want variables with selected visibility scoped to specific repositories, so that sensitive values are only exposed to intended repos.
  4. 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.
  5. 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.
  6. As a Firestartr operator, I want variable names validated at plan time against GitHub Actions naming rules, so that I catch errors before apply.
  7. As a Firestartr operator, I want visibility validated against the allowed enum (all, private, selected), so that typos are caught early.
  8. 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.
  9. As a Firestartr operator, I want a map of variable names to resource IDs in the output, so that downstream tooling can verify state.
  10. 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.
  11. 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

  1. 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.

  2. 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))
      }))
    })
  3. Visibility enum: Validated to all, private, selected — complete coverage of the provider enum.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. variable_ids output: A map(string) of variable_name → resource id, providing stable identifiers for Firestartr reconciliation.

  9. 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.

  10. 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.

  11. 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.

  12. 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.

  13. Resource address for import: github_actions_organization_variable.this["<variable_name>"]. Import ID is the variable name itself.

  14. 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions