Skip to content

microsoftexpert/tf-mod-github-custom-properties

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🐙 GitHub Custom Properties Terraform Module

Defines an organization's custom-property schema and sets the per-repository values that populate it — the governance/classification layer that tags every repo with compliance metadata (data-classification, owning-team, sox-in-scope, …) behind one secure-by-default boundary. Deeply-typed map(object) collections, validation-enforced enums, governance defaults (required = true, org-owner-only edits), and a depends_on contract so definitions land before values in a single apply. Built for integrations/github v6.x.

Terraform GitHub provider module type resources


🧩 Overview

This composite owns the GitHub custom-property feature end to end, across two planes:

  • 🏷️ Property definitions (the schema)github_organization_custom_properties.this: the org-level definition for each property (value_type, required, default_value, allowed_values, who may edit). One instance per property, keyed by property name.
  • 📌 Per-repository values (the data)github_repository_custom_property.values: the value each repository carries for a property (one entry per repo × property), keyed by a stable "<repo>:<property>" handle.
  • 🧬 Both planes are deeply-typed map(object) collections keyed on stable strings, so adds/removes never churn unrelated entries.
  • 🛡️ Governance defaults — properties are required = true and editable only by org_actors (org owners) unless you opt out; single_select/multi_select are validation-checked to carry allowed_values.
  • 🔗 Self-wiring lifecycleproperty_type on a value is derived from its definition (declare the type once), and depends_on forces definitions to exist before any value is written.

💡 Why it matters: In a regulated estate every repository should declare what it holds and who owns it. This module makes that classification declarative and auditable — and it is the keystone that org rulesets key on to govern repositories by their compliance metadata instead of by hand-maintained name lists.


❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

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!


🗺️ Where this fits in the family

flowchart LR
 repo["tf-mod-github-repository<br/>(keystone — emits id = repo name)"]
 cp["tf-mod-github-custom-properties<br/>(THIS module)"]
 ruleset["tf-mod-github-organization-ruleset<br/>(targets repos by custom property)"]
 orgset["tf-mod-github-organization-settings"]

 repo -- "repository = id (repo name)" --> cp
 cp -- "property_names" --> ruleset
 orgset -. "org custom-property feature / plan".-> cp

 style cp fill:#8957E5,color:#fff
 style repo fill:#24292F,color:#fff
Loading

This module consumes repository names from the keystone tf-mod-github-repository (the repos whose values are set), and emits the set of defined property_names that tf-mod-github-organization-ruleset uses to target repositories by classification. See the Cross-Module Contract.


🧬 What this module builds

flowchart TD
 props["var.properties<br/>map(object) keyed by property name"]
 vals["var.repository_values<br/>map(object) keyed by repo:property"]

 props --> this["github_organization_custom_properties.this<br/>(PRIMARY · for_each · org property DEFINITIONS)"]
 vals --> values["github_repository_custom_property.values<br/>(for_each · per-repository VALUES)"]
 this -- "depends_on · value_type → property_type" --> values

 style this fill:#8957E5,color:#fff
Loading

Resource inventory

  • github_organization_custom_properties.this — the primary plane: one organization custom-property definition per properties entry, for_each over the map keyed by property name. (Despite the resource's plural name, each instance manages exactly one property.)
  • github_repository_custom_property.values — the role-named child collection: one per-repository value per repository_values entry, for_each over the map keyed by "<repo>:<property>", depends_on the definitions so a single apply defines then populates.

ℹ️ The primary resource is a for_each set, not a singleton — the provider's github_organization_custom_properties is single-property-per-instance (see Architecture Notes).


✅ Provider / Versions

Requirement Value
Terraform >= 1.12.0
Provider integrations/github ~> 6.0 (current 6.12.1)

Schema notes that bite (verified against the v6 provider binary schema):

  • Provider source is integrations/githubnever the deprecated hashicorp/github.
  • Single-property arity. github_organization_custom_properties manages one property per instance (imported by a single property name); its id is the property name. There is no whole-set "definitions" singleton.
  • Different argument names per plane. The definition uses value_type (string | single_select | multi_select | true_false, default string); the repository value uses property_type (the same set plus url) and property_value is a set(string) (a single-element set for non-multi types).
  • No repository_id. github_repository_custom_property exposes only id, property_name, property_type, property_value, repository in v6.x — there is no repository_id attribute (older docs list one; it does not exist).

📁 Module Structure

tf-mod-github-custom-properties/
├── providers.tf # terraform{} + integrations/github ~> 6.0 (no provider block)
├── variables.tf # properties (definitions) + repository_values (values) — typed maps
├── main.tf # github_organization_custom_properties.this + github_repository_custom_property.values
├── outputs.tf # property_ids / property_names / properties + repository_value_ids / repository_values
├── SCOPE.md # design contract: scope, consumes/emits, token scopes, prerequisites
└── README.md # this file

⚙️ Quick Start

module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential" # required properties should carry a default
      description    = "Data sensitivity classification for the repository."
      # required defaults to true; values_editable_by defaults to org_actors
    }
  }
}

⚠️ Always pin the module source to a tag (?ref=v1.0.0) — never a branch — so applies are reproducible.


🔌 Cross-Module Contract

Consumes

Input Type Source
repository_values[].repository string (repo name) tf-mod-github-repositoryid output
repository_values[].property_name string a property defined in this module's properties (or an existing org property)

Emits

Output Description Consumed by
property_ids Map of property name → definition resource id (the property name) Audit / drift reporting
property_names Set of defined custom-property names tf-mod-github-organization-ruleset (repository_property conditions), audit
properties Map of name → applied definition (value_type, required, default_value, allowed_values, values_editable_by, description) Governance dashboards, compliance reporting
repository_value_ids Map of "<repo>:<property>" handle → value resource id Audit / drift reporting
repository_values Map of handle → applied value (repository, property_name, property_type, value) Compliance reporting — which repos carry which classification

Sourced from this repo's SCOPE.md. There is no scalar id output — the provider has no org-level custom-properties singleton (see Architecture Notes).


📚 Example Library

1️⃣ Minimal — one required classification property (no values yet)
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
      description    = "Data sensitivity classification for the repository."
    }
  }
}

🔒 Governance defaults apply: required = true and values_editable_by = "org_actors". A required property should carry a default_value so repos without an explicit value are still compliant.

2️⃣ Full classification schema — single_select + string + true_false
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      required       = true
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
      description    = "Data sensitivity classification."
    }
    "owning-team" = {
      value_type  = "string"
      required    = true
      description = "Team accountable for the repository (set per repo via repository_values)."
    }
    "sox-in-scope" = {
      value_type    = "true_false"
      required      = true
      default_value = "false"
      description   = "Whether the repository is in scope for SOX controls."
    }
  }
}

💡 owning-team is required but has no sensible default — every repository should set it via repository_values (or callers will see it as unset/non-compliant).

3️⃣ Optional metadata property (opt out of the required default)
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "cost-center" = {
      value_type  = "string"
      required    = false # purely optional metadata — opt out of the governance default
      description = "Finance cost-center code, if applicable."
    }
  }
}
4️⃣ multi_select property — multiple compliance frameworks
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "compliance-frameworks" = {
      value_type     = "multi_select"
      required       = false
      allowed_values = ["sox", "pci-dss", "glba", "ffiec"]
      description    = "Regulatory frameworks the repository must satisfy."
    }
  }
}

⚠️ single_select and multi_select must set a non-empty allowed_values — this is validation-enforced at plan time.

5️⃣ Set one repository value (property_type derived from the definition)
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
    }
  }

  repository_values = {
    "payments-api:data-classification" = {
      repository     = "payments-api"
      property_name  = "data-classification"
      property_value = ["restricted"] # a single-element set for single_select
      # property_type omitted — derived from the definition's value_type
    }
  }
}
6️⃣ Repository value 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       = "payments-api"
  visibility = "private"
}

module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
    }
  }

  repository_values = {
    "payments-api:data-classification" = {
      repository     = module.repository.id # id == repo name
      property_name  = "data-classification"
      property_value = ["restricted"]
    }
  }
}

💡 The keystone's id output is the repository name — exactly what repository expects.

7️⃣ Explicit property_type — value for a property defined outside this module
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  # No `properties` here — "cost-center" is defined elsewhere in the org.
  repository_values = {
    "payments-api:cost-center" = {
      repository     = "payments-api"
      property_name  = "cost-center"
      property_type  = "string" # REQUIRED here — cannot be derived (not in this module's properties)
      property_value = ["CC-4815"]
    }
  }
}

⚠️ When a value targets a property not defined in this module's properties, you must set property_type explicitly — otherwise plan fails the resolvability validation.

8️⃣ Delegated editing — values_editable_by = org_and_repo_actors
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "team-contact" = {
      value_type         = "string"
      required           = false
      values_editable_by = "org_and_repo_actors" # repo admins may edit this value
      description        = "Owning-team contact (delegated to repository admins)."
    }
  }
}

🔒 Default is org_actors (org owners only). Opt into org_and_repo_actors only where decentralised editing is acceptable.

9️⃣ for_each at scale — repo values from a map(object)
locals {
  # Built from your repository inventory: repo => classification.
  repo_classifications = {
    "payments-api" = "restricted"
    "ledger-core"  = "confidential"
    "docs-site"    = "public"
  }
}

module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
    }
  }

  repository_values = {
    for repo, cls in local.repo_classifications :
    "${repo}:data-classification" => {
      repository     = repo
      property_name  = "data-classification"
      property_value = [cls]
    }
  }
}

⚠️ Bulk for_each across many repositories can trip GitHub secondary rate limits — see Troubleshooting.

🔟 multi_select value — several values on one repository
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "compliance-frameworks" = {
      value_type     = "multi_select"
      allowed_values = ["sox", "pci-dss", "glba", "ffiec"]
      required       = false
    }
  }

  repository_values = {
    "payments-api:compliance-frameworks" = {
      repository     = "payments-api"
      property_name  = "compliance-frameworks"
      property_value = ["sox", "pci-dss", "glba"] # multi_select takes multiple values
    }
  }
}
1️⃣1️⃣ Schema + values in one call (definitions land before values)
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
    }
    "owning-team" = {
      value_type = "string"
    }
  }

  repository_values = {
    "payments-api:data-classification" = { repository = "payments-api", property_name = "data-classification", property_value = ["restricted"] }
    "payments-api:owning-team"         = { repository = "payments-api", property_name = "owning-team", property_value = ["payments-platform"] }
  }
}

💡 depends_on inside the module guarantees both definitions exist before either value is written — a single apply both defines and populates.

1️⃣2️⃣ 🔒 Secure / hardened governance variant
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  # Every classification property is REQUIRED and editable ONLY by org owners.
  properties = {
    "data-classification" = {
      value_type         = "single_select"
      required           = true
      values_editable_by = "org_actors" # org owners only (default, made explicit)
      allowed_values     = ["public", "internal", "confidential", "restricted"]
      default_value      = "confidential"
      description        = "Data sensitivity classification (governed centrally)."
    }
    "owning-team" = {
      value_type         = "string"
      required           = true
      values_editable_by = "org_actors"
      description        = "Accountable team — must be set on every repository."
    }
    "sox-in-scope" = {
      value_type         = "true_false"
      required           = true
      values_editable_by = "org_actors"
      default_value      = "false"
      description        = "SOX control scope flag."
    }
  }
}

🔒 Hardening checklist: every property required = true; values_editable_by = "org_actors"; a safe default_value on each so no repo is ever non-compliant by omission; classification governed centrally, never by repo admins.

1️⃣3️⃣ 🏗️ End-to-end composition (mandatory) — repository → custom properties → org ruleset
# 1 · Keystone repository.
module "repository" {
  source     = "git::https://github.com/microsoftexpert/tf-mod-github-repository?ref=v1.0.0"
  name       = "payments-api"
  visibility = "private"
}

# 2 · Define the classification schema AND set this repo's value.
module "custom_properties" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-custom-properties?ref=v1.0.0"

  properties = {
    "data-classification" = {
      value_type     = "single_select"
      required       = true
      allowed_values = ["public", "internal", "confidential", "restricted"]
      default_value  = "confidential"
      description    = "Data sensitivity classification."
    }
  }

  repository_values = {
    "payments-api:data-classification" = {
      repository     = module.repository.id # ← repository.id (repo name)
      property_name  = "data-classification"
      property_value = ["restricted"]
    }
  }
}

# 3 · Govern: an org ruleset that targets repositories BY the custom property.
# property_names is the published contract this ruleset keys on.
module "classification_ruleset" {
  source = "git::https://github.com/microsoftexpert/tf-mod-github-organization-ruleset?ref=v1.0.0"

  name        = "protect-restricted-repos"
  target      = "branch"
  enforcement = "active"

  rules = {
    non_fast_forward = true
    deletion         = true
    pull_request = {
      required_approving_review_count = 2
      require_code_owner_review       = true
    }
  }

  conditions = {
    ref_name = {
      include = ["~DEFAULT_BRANCH"]
    }
    repository_property = {
      include = [
        {
          name            = "data-classification" # one of module.custom_properties.property_names
          property_values = ["restricted"]
        }
      ]
    }
  }

  # The ruleset can only target a property that already exists — ensure the
  # definitions apply first. property_names is emitted for exactly this contract.
  depends_on = [module.custom_properties]
}

output "classification_property_names" {
  value = module.custom_properties.property_names
}

🏗️ The canonical wiring: repository.id → the value's repository; the custom-property name flows into the org ruleset's repository_property condition so every restricted repo is governed automatically — no hand-maintained repo lists.


📥 Inputs

High-level:

  • properties (map(object), default {}) — organization custom-property definitions, keyed by property name. Governance defaults: required = true, values_editable_by = "org_actors".
  • repository_values (map(object), default {}) — per-repository values, keyed by a stable "<repo>:<property>" handle. property_type is optional (derived from the matching definition).
Full object schemas for the map inputs
# properties — map keyed by property NAME (the key IS the property name)
map(object({
 value_type = optional(string, "string") # string | single_select | multi_select | true_false
 required = optional(bool, true) # governance default: REQUIRED unless opted out
 description = optional(string) # human description of the property
 default_value = optional(string) # default applied to repos that do not set a value
 allowed_values = optional(list(string), []) # REQUIRED for single_select / multi_select; ignored otherwise
 values_editable_by = optional(string, "org_actors") # org_actors (most restrictive) | org_and_repo_actors
}))

# repository_values — map keyed by a stable "<repo>:<property>" handle
map(object({
 repository = string # repo name whose value is set (wire from tf-mod-github-repository.id)
 property_name = string # name of the (already-defined) custom property
 property_type = optional(string) # string | single_select | multi_select | true_false | url
 # OMIT to derive from the matching `properties` definition's value_type
 property_value = set(string) # the value(s): single-element set for non-multi types; multiple for multi_select
}))

Validations enforced at plan time:

  • properties[].value_typestring | single_select | multi_select | true_false.
  • properties[].values_editable_byorg_actors | org_and_repo_actors.
  • single_select / multi_select properties must set a non-empty allowed_values.
  • repository_values[].property_type, when set, ∈ string | single_select | multi_select | true_false | url.
  • Each repository_values entry must set property_type or reference a property defined in properties (so the type can be derived).
  • Each repository_values entry must have a non-empty repository, property_name, and at least one property_value.

🧾 Outputs

Output Description
property_ids Map of property name → definition resource id (the property name).
property_names Set of defined custom-property names — the key wire for tf-mod-github-organization-ruleset.
properties Map of name → applied definition (value_type, required, default_value, allowed_values, values_editable_by, description).
repository_value_ids Map of "<repo>:<property>" handle → value resource id (org:repo:property).
repository_values Map of handle → applied value (repository, property_name, property_type, value).

ℹ️ There is no scalar id — the provider has no org-level custom-properties singleton. The definitions plane is exposed as maps keyed by property name. Nothing is sensitive: property names/values are governance metadata, not secrets or member PII.


🧠 Architecture Notes

  • Per-property definitions, not a whole-set overwrite. Despite the plural name, github_organization_custom_properties manages one property per instance (imported by a single property name; its id is the name). So the definitions plane is a for_each set keyed by property name — each property is its own authoritative resource. There is no single resource that owns "the whole schema," and therefore no scalar id / node_id / slug to emit; the module exposes property_ids / property_names / properties maps instead.
  • Two argument vocabularies. The definition uses value_type (string | single_select | multi_select | true_false); the repository value uses property_type (the same set plus url) and a property_value set. The module hides this asymmetry by deriving property_type from the matching definition's value_type when you omit it — declare the type once on the definition.
  • single_select / multi_select need allowed_values. These types are meaningless without an enumerated value list, so the module validation-rejects them without one at plan time, before any API call. A value not in allowed_values fails at apply.
  • Definitions before values — one apply. github_repository_custom_property.values carries depends_on = [github_organization_custom_properties.this], so every definition exists before any value is written. A value referencing an undefined property fails at apply — keep the property in properties (or define it elsewhere and pass property_type).
  • The compliance/classification keystone. Once repositories carry data-classification, owning-team, sox-in-scope, an org ruleset (tf-mod-github-organization-ruleset) can target them by repository_property conditions — governing whole classes of repos by metadata instead of by hand-maintained name lists. property_names is the published contract for that wiring.
  • value_type is effectively immutable. Changing a property's type after repositories hold values can fail at apply. Plan type changes carefully; treat the schema as append-mostly.
  • No tags, no timeouts. GitHub has neither a resource-tagging concept nor a timeouts block on these resources — there is intentionally no tags / timeouts tail, and auth/owner are provider concerns, never module variables.
  • Eventual consistency / secondary rate limits. The GitHub REST API is eventually consistent and enforces undocumented secondary rate limits on bursts of writes. Populating values across many repositories via for_each can intermittently 403/422 mid-apply; a re-run usually converges.

🧱 Design Principles

  • 🔒 Governed by default — properties are required = true and editable only by org_actors; you opt out explicitly for optional or delegated metadata.
  • 🏷️ Type is the contract — deeply-typed map(object) collections + validation enums mean a malformed schema fails at plan time, not at apply.
  • 🧬 Declare onceproperty_type on values is derived from the definition, so the type lives in exactly one place.
  • 🪪 Least privilegevalues_editable_by defaults to the most restrictive option (org_actors).
  • 🔗 Composable governance — emits property_names so rulesets govern repos by classification; consumes repo names from the keystone.
  • 🚫 No tags, no timeouts, no auth variables — GitHub has no tags; auth and owner are provider concerns.

🚀 Runbook

terraform init -backend=false
terraform validate
terraform fmt -check
terraform plan
terraform apply
terraform output

⚠️ Pin the module source to a tag (?ref=v1.0.0) — never a branch — so applies are reproducible.

plan/apply require a configured integrations/github provider (PAT / GitHub App) with the token scopes below, an org-owner identity, and the target owner / GITHUB_OWNER set.

Required token scopes / GitHub App permissions

Classic PAT scopes

  • admin:org — read/write organization custom-property definitions.
  • repo — set custom-property values on repositories.

Fine-grained PAT / GitHub App permissions

  • Organization → Custom properties: read/write (the definitions).
  • Repository → Custom properties: read/write (per-repo values).
  • Metadata: read.

⚠️ Auth and the target org (owner / GITHUB_OWNER) are provider concerns — never module variables.

GitHub Prerequisites

  • Plan / edition. Custom properties are an organization feature, available on GitHub Team and Enterprise Cloud (managed orgs only — not user accounts). Confirm availability for the org's plan.
  • Org-owner identity. The provider identity must be an organization owner to manage definitions.
  • Definitions before values. A repository value can only be set for a property that already exists; the module wires repository_values to depends_on the definitions so a single apply succeeds.
  • Rate limits. Per-repo value writes are one API call each — watch secondary rate limits when populating values across many repositories via for_each.

🧪 Testing

The 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, and terraform fmt -check cleanly.


💬 Example Output

property_ids = {
 "data-classification" = "data-classification"
 "owning-team" = "owning-team"
 "sox-in-scope" = "sox-in-scope"
}
property_names = toset([
 "data-classification",
 "owning-team",
 "sox-in-scope",
])
properties = {
 "data-classification" = {
 "allowed_values" = tolist(["public", "internal", "confidential", "restricted"])
 "default_value" = "confidential"
 "description" = "Data sensitivity classification."
 "required" = true
 "value_type" = "single_select"
 "values_editable_by" = "org_actors"
 }
}
repository_value_ids = {
 "payments-api:data-classification" = "casey:payments-api:data-classification"
}
repository_values = {
 "payments-api:data-classification" = {
 "property_name" = "data-classification"
 "property_type" = "single_select"
 "repository" = "payments-api"
 "value" = toset(["restricted"])
 }
}

🔍 Troubleshooting

Symptom Likely cause Resolution
403 Resource not accessible by integration Token lacks admin:org/repo (classic) or Custom properties: read/write (fine-grained); or identity is not an org owner Grant the scopes above and use an org-owner identity
404 Not Found / feature unavailable Org is not Team/Enterprise, or the provider points at a user account Custom properties are an org feature on Team/Enterprise — target a managed org
Apply error: property does not exist A value references a property not yet defined Define it in properties, or define it in the org and pass property_type; depends_on handles in-module ordering
Apply error: value not allowed Value not in the property's allowed_values Use one of the enumerated values (or widen allowed_values)
Plan error: must set property_type or match a defined property A value targets an external property with no property_type Add property_type, or add the property to properties so the type can be derived
Plan error: single_select/multi_select requires allowed_values Enumerated type without a value list Add a non-empty allowed_values
Intermittent 403/422 during bulk apply GitHub secondary rate limit on bursty writes Re-run terraform apply; reduce -parallelism; stagger large rollouts
Unexpected replace on a property value_type changed (effectively immutable once values exist) Avoid type changes; migrate to a new property if the type must change

🔗 Related Docs

  • This module's design contract — SCOPE.md
  • Keystone repository module — tf-mod-github-repository (emits id = repo name)
  • Governance sibling — tf-mod-github-organization-ruleset (consumes property_names to target repos by property)
  • integrations/github provider reference — organization custom properties and repository custom property resources
  • GitHub custom properties documentation — organization metadata and classification

About

No description or website provided.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages