One secure-by-default boundary for every GitHub Codespaces secret β repository, organization, and user scope β plus the selected-repository allow-list that pairs with
selected-visibility org secrets. Private-by-default org visibility,encrypted_valuepreferred over plaintext, sensitive inputs, and zero secret values in outputs. Built for integrations/github v6.x.
This is an aggregation module β a Codespaces secrets bundle. It has no single primary resource; instead it groups four independent, role-named for_each collections behind one boundary, each defaulting to {} so you enable only the scopes you use:
- π Repository secrets (
repository_secrets) β Codespaces secrets scoped to a single repository. - π’ Organization secrets (
organization_secrets) β org-wide Codespaces secrets withall/private/selectedvisibility (defaultprivate). - π― Selected-repository allow-lists (
secret_repository_access) β the repository allow-list that aselected-visibility org secret reads from, managed as its own resource. - π€ User secrets (
user_secrets) β Codespaces secrets scoped to the authenticated user, optionally granted to specific repositories.
π‘ Why it matters: Codespaces secrets feed live developer environments with credentials. This module makes that surface declarative, least-privilege, and auditable β secrets are
sensitiveinputs, org secrets default toprivate, and only names and resource ids are ever emitted. It is the direct mirror oftf-mod-github-dependabot, one scope up to include the user-level secret, so secrets-by-feature stay consistent across Dependabot / Actions / Codespaces.
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!
The keystone tf-mod-github-repository emits the repository name (id) and numeric id (repo_id) that this module consumes; the other secret-family siblings consume the same keystone. This module emits only names and ids β to audit/reporting tooling, never to another module's secret input.
flowchart LR
repo["ποΈ tf-mod-github-repository<br/>keystone"]
cs["π tf-mod-github-codespaces<br/>(this module)"]
dep["tf-mod-github-dependabot"]
act["tf-mod-github-actions-repository"]
audit["π Audit / Reporting"]
repo -->|"id = repo name"| cs
repo -->|"repo_id (numeric)"| cs
repo -.-> dep
repo -.-> act
cs -->|"*_secret_names / *_secret_ids"| audit
style cs fill:#8957E5,color:#fff
style repo fill:#24292F,color:#fff
π Consumes: repository name (repo secrets) and numeric
repo_id(orgselectedallow-lists + user-secret access) fromtf-mod-github-repository. Emits: per-scope secret-name sets and id maps for audit/reporting. See the Cross-Module Contract.
There is no this resource. The diagram shows the four independent role-named collections; the only internal relationship is that a selected-visibility org secret pairs with the allow-list resource (own that allow-list in exactly one place).
flowchart TD
rs["var.repository_secrets<br/>map(object) β sensitive"] --> repo["github_codespaces_secret.repo<br/>for_each"]
os["var.organization_secrets<br/>map(object) β sensitive"] --> org["github_codespaces_organization_secret.org<br/>for_each"]
sra["var.secret_repository_access<br/>map(object)"] --> allow["github_codespaces_organization_secret_repositories.allow<br/>for_each"]
us["var.user_secrets<br/>map(object) β sensitive"] --> user["github_codespaces_user_secret.user<br/>for_each"]
org -.->|"visibility = selected<br/>allow-list owned in one place"| allow
style repo fill:#8957E5,color:#fff
style org fill:#8957E5,color:#fff
style allow fill:#8957E5,color:#fff
style user fill:#8957E5,color:#fff
Resource inventory
github_codespaces_secret.repoβ repository-scoped Codespaces secrets, one perrepository_secretsentry.github_codespaces_organization_secret.orgβ organization-scoped Codespaces secrets withvisibility, one perorganization_secretsentry.github_codespaces_organization_secret_repositories.allowβ the selected-repository allow-list for aselected-visibility org secret, one persecret_repository_accessentry.github_codespaces_user_secret.userβ user-scoped Codespaces secrets, one peruser_secretsentry.
- Terraform
>= 1.12.0 - integrations/github
~> 6.0β validated against 6.12.1 - Never
hashicorp/github(deprecated). Migrate old state withterraform state replace-provider registry.terraform.io/hashicorp/github registry.terraform.io/integrations/github.
βΉοΈ 6.x schema notes surfaced during authoring:
- No
key_idargument. All Codespaces secret resources acceptencrypted_value(Base64) andplaintext_valueonly β there is nokey_idfield. The provider resolves the appropriate Codespaces public key internally. Pre-encrypt against the right public key data source and pass just theencrypted_value.- No
visibilityon user secrets.github_codespaces_user_secrethas no visibility; it takes an optionalselected_repository_idsthat grants repositories access to the secret inside the user's Codespaces.- Only
created_at/updated_atare computed. These resources expose nonode_id,slug,repo_id, orhtml_urlβ the cross-module reference is the resourceid.
tf-mod-github-codespaces/
βββ providers.tf # github provider requirement (~> 6.0); NO provider {} block
βββ variables.tf # four role-named map(object) collections; secret-bearing ones sensitive
βββ main.tf # repo / org / allow / user collections (no 'this'); nonsensitive(keys) pattern
βββ outputs.tf # per-scope name sets + id maps; NEVER a secret value
βββ README.md # this file
βββ SCOPE.md # token scopes, prerequisites, consumes/emits contract, gotchas
The smallest useful call defines a single repository Codespaces secret. Every collection defaults to {}, so you supply only the scopes you need.
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
repository_secrets = {
npm_token = {
repository = "loan-origination-svc"
secret_name = "NPM_TOKEN"
encrypted_value = var.npm_token_encrypted # Base64, pre-encrypted with the repo Codespaces public key
}
}
}βΉοΈ The organization and authentication are the provider's
owner/GITHUB_OWNERand token/App β never module variables. Point the provider at the target org before applying.
Sourced directly from SCOPE.md.
| Input | Type | Source |
|---|---|---|
repository_secrets[].repository |
string (repo name) |
tf-mod-github-repository β id |
organization_secrets[].selected_repository_ids |
list(number) |
tf-mod-github-repository β repo_id |
secret_repository_access[].selected_repository_ids |
list(number) |
tf-mod-github-repository β repo_id |
user_secrets[].selected_repository_ids |
list(number) |
tf-mod-github-repository β repo_id |
| Output | Description | Consumed by |
|---|---|---|
repository_secret_names |
Set of managed repo Codespaces secret names (values never emitted) | Audit / drift detection |
repository_secret_ids |
Map: handle β github_codespaces_secret resource id |
Reporting |
organization_secret_names |
Set of managed org Codespaces secret names | Audit |
organization_secret_ids |
Map: handle β github_codespaces_organization_secret resource id |
Reporting |
user_secret_names |
Set of managed user Codespaces secret names | Audit |
user_secret_ids |
Map: handle β github_codespaces_user_secret resource id |
Reporting |
secret_repository_access_ids |
Map: handle β github_codespaces_organization_secret_repositories resource id |
Reporting |
π No secret value is ever output. Only names and resource ids are emitted.
Every collection is keyed by a stable caller handle (the map key) β never a list index. Examples use the new GitHub-org source and sibling tf-mod-github-* references.
1 Β· Minimal β one repository secret
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
repository_secrets = {
npm_token = {
repository = "loan-origination-svc"
secret_name = "NPM_TOKEN"
plaintext_value = var.npm_token # provider encrypts on write
}
}
}π‘ Supply exactly one of
plaintext_valueorencrypted_valueper secret.
2 Β· Organization secret β visibility = all
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
organization_secrets = {
shared_registry = {
secret_name = "SHARED_REGISTRY_TOKEN"
visibility = "all" # every repo in the org, including public
plaintext_value = var.shared_registry_token
}
}
}
β οΈ allexposes the secret to every repository, including public ones. Preferprivateorselectedat a regulated institution.
3 Β· Organization secret β visibility = private (secure default)
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
organization_secrets = {
artifactory = {
secret_name = "ARTIFACTORY_TOKEN"
# visibility omitted β defaults to "private" (all private/internal repos)
encrypted_value = var.artifactory_token_encrypted
}
}
}4 Β· Organization secret β visibility = selected, allow-list managed separately
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
organization_secrets = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
visibility = "selected"
encrypted_value = var.payments_key_encrypted
# NOTE: do NOT set selected_repository_ids inline β owned by the allow-list below.
}
}
secret_repository_access = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
selected_repository_ids = [12345678, 23456789] # numeric repo ids
}
}
}π Own each
selectedsecret's allow-list in exactly one place β here, or inline viaselected_repository_idson the secret (example 5), never both.
5 Β· Organization secret β visibility = selected, allow-list inline
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
organization_secrets = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
visibility = "selected"
encrypted_value = var.payments_key_encrypted
selected_repository_ids = [12345678, 23456789] # inline allow-list
}
}
# Do not also add a secret_repository_access entry for PAYMENTS_API_KEY.
}6 Β· User secret
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
user_secrets = {
gh_pat = {
secret_name = "PERSONAL_GH_PAT"
plaintext_value = var.personal_gh_pat
}
}
}βΉοΈ User secrets attach to the authenticated user (the provider identity), not the org. They need the
codespace:secretsclassic scope.
7 Β· User secret granted to specific repositories
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
user_secrets = {
gh_pat = {
secret_name = "PERSONAL_GH_PAT"
encrypted_value = var.personal_gh_pat_encrypted
selected_repository_ids = [12345678] # repos that may read the secret in the user's Codespaces
}
}
}8 Β· encrypted_value everywhere (preferred over plaintext)
# Encrypt against the matching Codespaces public key BEFORE Terraform runs.
data "github_codespaces_public_key" "repo" {
repository = "loan-origination-svc"
}
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
repository_secrets = {
db_password = {
repository = "loan-origination-svc"
secret_name = "DB_PASSWORD"
encrypted_value = var.db_password_encrypted # Base64, encrypted with the repo public key
}
}
}π Prefer
encrypted_value: the plaintext never enters your HCL. (Note: there is nokey_idargument β the provider resolves the public key internally; you supply only the Base64 ciphertext.) State always holds the value regardless β keep your backend encrypted.
9 Β· for_each at scale β many repo secrets from one map(object)
variable "repo_codespaces_secrets" {
type = map(object({
repository = string
secret_name = string
encrypted_value = string
}))
default = {}
sensitive = true
}
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
repository_secrets = var.repo_codespaces_secrets
}
β οΈ Each secret is one API write plus a public-key fetch. Large maps can trip secondary rate limits β split very large applies.
10 Β· Mixed scopes in a single call
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
repository_secrets = {
npm = { repository = "web-portal", secret_name = "NPM_TOKEN", encrypted_value = var.npm_enc }
}
organization_secrets = {
artifactory = { secret_name = "ARTIFACTORY_TOKEN", visibility = "private", encrypted_value = var.arti_enc }
}
user_secrets = {
gh_pat = { secret_name = "PERSONAL_GH_PAT", encrypted_value = var.pat_enc }
}
}11 Β· Repository secrets wired from tf-mod-github-repository (cross-module)
module "repository" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-repository?ref=v1.0.0"
name = "loan-origination-svc"
visibility = "private"
}
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
repository_secrets = {
npm_token = {
repository = module.repository.id # repo NAME from the keystone
secret_name = "NPM_TOKEN"
encrypted_value = var.npm_token_encrypted
}
}
}12 Β· Selected org secret wired by numeric repo_id (cross-module)
module "repository" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-repository?ref=v1.0.0"
name = "payments-svc"
visibility = "private"
}
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
organization_secrets = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
visibility = "selected"
encrypted_value = var.payments_key_encrypted
}
}
secret_repository_access = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
selected_repository_ids = [module.repository.repo_id] # NUMERIC id from the keystone
}
}
}13 Β· π Secure / hardened variant β encrypted everywhere, selected, least repos
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
# Repo secrets: encrypted_value only, never plaintext in code.
repository_secrets = {
db = {
repository = module.repository.id
secret_name = "DB_PASSWORD"
encrypted_value = var.db_password_encrypted
}
}
# Org secrets: selected visibility, the narrowest possible allow-list.
organization_secrets = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
visibility = "selected"
encrypted_value = var.payments_key_encrypted
}
}
secret_repository_access = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
selected_repository_ids = [module.repository.repo_id] # exactly the repos that need it
}
}
}π Hardened posture:
encrypted_valueeverywhere, org secretsselected(neverall), allow-lists scoped to the minimum repositories, secrets aresensitiveinputs, no values in outputs.
14 Β· Consuming the outputs for audit / reporting
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
#... collections...
}
output "managed_codespaces_secret_names" {
value = {
repository = module.codespaces.repository_secret_names
organization = module.codespaces.organization_secret_names
user = module.codespaces.user_secret_names
}
}
# Assert the org never holds an unexpected Codespaces secret (drift detection).
check "no_unexpected_org_secret" {
assert {
condition = !contains(module.codespaces.organization_secret_names, "LEGACY_TOKEN")
error_message = "A retired Codespaces org secret (LEGACY_TOKEN) is still managed."
}
}βΉοΈ Outputs are names and ids only β never values. Each map/set is empty (
{}/[]) when its scope is unused, nevernull.
15 Β· ποΈ End-to-end composition (mandatory finale) β repository β codespaces, outputs β inputs
# 1) Keystone repository.
module "repository" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-repository?ref=v1.0.0"
name = "loan-origination-svc"
visibility = "private"
vulnerability_alerts = true
}
# 2) Codespaces secrets wired entirely from the keystone's outputs.
module "codespaces" {
source = "git::https://github.com/microsoftexpert/tf-mod-github-codespaces?ref=v1.0.0"
# repo name β repository_secrets[].repository
repository_secrets = {
npm_token = {
repository = module.repository.id
secret_name = "NPM_TOKEN"
encrypted_value = var.npm_token_encrypted
}
}
# org secret, selected visibility
organization_secrets = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
visibility = "selected"
encrypted_value = var.payments_key_encrypted
}
}
# numeric repo_id β allow-list
secret_repository_access = {
payments_key = {
secret_name = "PAYMENTS_API_KEY"
selected_repository_ids = [module.repository.repo_id]
}
}
}
output "codespaces_repo_secret_ids" {
value = module.codespaces.repository_secret_ids
}π‘ The keystone emits
id(the repo name) andrepo_id(the numeric id). The name flows into repo-secret entries; the numeric id flows intoselectedallow-lists. Apply the repository before the secrets.
All four inputs are map(object(...)) keyed by a stable caller handle, each defaulting to {} (independently optional). The three secret-bearing collections are sensitive = true.
repository_secrets(sensitive) β repo-scoped secrets. Required per entry:repository,secret_name. Optional:plaintext_value,encrypted_value(supply one; prefer encrypted).organization_secrets(sensitive) β org-scoped secrets. Required:secret_name. Optional:visibility(defaultprivate; validatedall/private/selected),plaintext_value,encrypted_value,selected_repository_ids(inline allow-list, default[]).secret_repository_access(not sensitive) β allow-lists forselectedorg secrets. Required:secret_name,selected_repository_ids.user_secrets(sensitive) β user-scoped secrets. Required:secret_name. Optional:plaintext_value,encrypted_value,selected_repository_ids(repos granted access, default[]).
Full object schemas
variable "repository_secrets" {
type = map(object({
repository = string
secret_name = string
plaintext_value = optional(string)
encrypted_value = optional(string)
}))
default = {}
sensitive = true
}
variable "organization_secrets" {
type = map(object({
secret_name = string
visibility = optional(string, "private") # all | private | selected
plaintext_value = optional(string)
encrypted_value = optional(string)
selected_repository_ids = optional(list(number), [])
}))
default = {}
sensitive = true
# validation: visibility β {all, private, selected}
}
variable "secret_repository_access" {
type = map(object({
secret_name = string
selected_repository_ids = list(number)
}))
default = {}
}
variable "user_secrets" {
type = map(object({
secret_name = string
plaintext_value = optional(string)
encrypted_value = optional(string)
selected_repository_ids = optional(list(number), [])
}))
default = {}
sensitive = true
}| Output | Description |
|---|---|
repository_secret_ids |
Map: handle β github_codespaces_secret resource id. Empty {} when unused. |
repository_secret_names |
Set of managed repository Codespaces secret names. |
organization_secret_ids |
Map: handle β github_codespaces_organization_secret resource id. |
organization_secret_names |
Set of managed organization Codespaces secret names. |
secret_repository_access_ids |
Map: handle β github_codespaces_organization_secret_repositories resource id. |
user_secret_ids |
Map: handle β github_codespaces_user_secret resource id. |
user_secret_names |
Set of managed user Codespaces secret names. |
π No output is
sensitiveβ and none is a secret value. Only names and resource ids are emitted; structural fields are revealed withnonsensitive. Every map/set is empty (notnull) when its scope is unused.
- Aggregation shape β no
this. There is no single dominant resource; each scope is a peerfor_eachcollection named by role (repo,org,allow,user). This mirrorstf-mod-github-dependabot(repo + org + allow) one scope up, addinggithub_codespaces_user_secret. sensitiveinputs +nonsensitive(keys).repository_secrets,organization_secrets, anduser_secretsaresensitive = truebecause they carry secret values. Terraform forbidsfor_eachover a sensitive value, somain.tfiteratesnonsensitive(toset(keys(...)))and indexes the map inside each resource. Non-secret structural fields (repository,secret_name,visibility) are revealed withnonsensitiveso plan output and outputs stay useful; the secret value flows straight through to the schema-sensitiveplaintext_value/encrypted_valuearguments.encrypted_valuepreferred β and there is nokey_id. Preferencrypted_value(pre-encrypted, Base64) overplaintext_valueso plaintext never enters HCL. These resources expose nokey_idargument β the provider resolves the Codespaces public key internally.β οΈ Either way the value lives in state β keep the backend encrypted.- Org visibility & the one-place allow-list rule.
organization_secrets[].visibilityisall/private/selected(defaultprivate, validated). Forselected, own the allow-list in exactly one place: inlineselected_repository_idson the secret or asecret_repository_accessentry β never both for the same secret, or they fight over the same allow-list. - Values are write-only; names/ids are the only outputs. The provider cannot read a secret's value back, so it cannot detect remote-value drift β changing a value in code re-writes it; a value changed only in the GitHub UI is invisible to Terraform. Secret names are tracked. On
import, the provider does not fetch the value (terraform import github_codespaces_secret.repo[\"handle\"] repo/SECRET_NAME; org/user import by secret name) β you may needignore_changeson the value as a workaround. - No tags, no timeouts. GitHub has no resource tagging concept and these resources expose no
timeoutsblock β there is no tags/timeouts tail (the key difference from the azurerm/aws libraries).
- π Private by default β org-secret
visibilitydefaults toprivate, neverall/public. Wider exposure is an explicit, reviewed opt-in. - ποΈ Encrypt over plaintext β
encrypted_valueis the documented preference so secrets never sit in HCL;plaintext_valueis the fallback. - π Secrets are
sensitiveβ every secret-bearing input map issensitive = true; no secret value is ever emitted as an output. - π― Least privilege β
selectedorg secrets scope to the minimum repositories; allow-lists are owned in one place to avoid accidental over-grant. - π§© Composable, auth-free β no
provider {}block, noowner/token/app_authvariables; the caller configures auth and the target org.integrations/githubonly β neverhashicorp/github. - π§Ύ Auditable β emits exactly the names and ids audit/reporting tooling needs, and nothing more.
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. Codespaces secrets feed live developer environments at a regulated institution; a movingrefis unacceptable.
Offline proof gate (no live org required):
terraform fmt -check # zero formatting differences
terraform validate # configuration is valid
tflint # core rules (no dedicated GitHub ruleset exists)β This module passes
terraform init -backend=false,terraform validate("Success! The configuration is valid."), andterraform fmt -check(zero diffs) againstintegrations/github6.12.1.
repository_secret_ids = {
"npm_token" = "loan-origination-svc:NPM_TOKEN"
}
repository_secret_names = toset([
"NPM_TOKEN",
])
organization_secret_ids = {
"payments_key" = "PAYMENTS_API_KEY"
}
organization_secret_names = toset([
"PAYMENTS_API_KEY",
])
secret_repository_access_ids = {
"payments_key" = "PAYMENTS_API_KEY"
}
user_secret_ids = {}
user_secret_names = toset([])
| Symptom | Cause | Resolution |
|---|---|---|
Invalid for_each argument / "sensitive value" on plan |
A secret-bearing map was iterated directly | The module already uses for_each = nonsensitive(toset(keys(...))); if you adapt it, never for_each a sensitive value directly. |
403 / Resource not accessible on org secrets |
Provider identity lacks org rights | Token/App needs admin:org (classic) or Organization β Codespaces secrets: read/write (fine-grained). See SCOPE.md. |
403 on user secrets |
Missing user scope | Classic PAT needs codespace:secrets; fine-grained needs Codespaces user secrets: read/write on the user. |
| Apply fails creating any Codespaces secret | Codespaces not enabled for the org / not billed | Enable Codespaces and configure billing first. Many regulated orgs disable Codespaces β confirm it is part of the footprint. |
| Org secret apply fails on a Free org | Org Codespaces secrets need Team / Enterprise Cloud | Upgrade the plan or scope secrets to repo/user level. |
selected_repository_ids rejected / no effect |
Wrong id type, or set on a non-selected secret |
Use numeric repo_id (wire module.repository.repo_id), and only with visibility = "selected". |
| Allow-list keeps reverting | Allow-list owned in two places | Set selected_repository_ids inline or via secret_repository_access for a given secret β never both. |
| Value changed in the GitHub UI not detected | Values are write-only | The provider can't read values back; re-apply from code (the source of truth), or use ignore_changes for externally-rotated secrets. |
| Secondary rate limit on a big apply | Many secrets = many writes + key fetches | Split large for_each maps across applies. |
- integrations/github provider β Codespaces Secret, Codespaces Organization Secret, Codespaces Organization Secret Repositories, and Codespaces User Secret resource references
- integrations/github provider β Codespaces public-key data sources (repo / organization / user) for
encrypted_value - module β tf-mod-github-repository (keystone; emits
idandrepo_id) - module β tf-mod-github-dependabot (the direct mirror β repo + org + allow-list)
- module β tf-mod-github-actions-repository / tf-mod-github-repository-environment (sibling secrets surfaces)
- GitHub Docs β Managing encrypted secrets for your Codespaces, and the Codespaces organization/billing policy matrix