Skip to content

feat: support use_immutable_subject and sub_claim_prefix on repository OIDC subject claim template - #3582

Open
madriaanse377 wants to merge 1 commit into
integrations:mainfrom
madriaanse377:feat/oidc-immutable-subject-claims
Open

madriaanse377 wants to merge 1 commit into
integrations:mainfrom
madriaanse377:feat/oidc-immutable-subject-claims

Conversation

@madriaanse377

Copy link
Copy Markdown

Summary

Wires use_immutable_subject and sub_claim_prefix — already present on go-github v89's OIDCSubjectClaimCustomTemplate struct — through to github_actions_repository_oidc_subject_claim_customization_template.

This lets a repository created before GitHub's July 15, 2026 immutable-subject-claims rollout opt in individually via Terraform, instead of requiring an org-wide toggle or an out-of-band REST call that Terraform can't track.

Changes

  • use_immutable_subject (bool, optional, computed) and sub_claim_prefix (string, optional, computed) added to the resource schema
  • Both fields wired into CreateOrUpdate and Read, following the existing GetOk + new(...) pattern already used elsewhere in this resource (include_claim_keys) and in sibling resources (e.g. resource_github_enterprise_actions_workflow_permissions.go)
  • Docs (docs/resources/...md and its .tmpl source) and the example under examples/resources/... updated to cover both new arguments
  • New acceptance test subtest asserting both fields round-trip correctly

Scoped to the repository-level resource only, matching the issue — the organization-level resource and this resource's Delete behavior are unchanged.

Closes #3548

Test plan

  • Maintainer-run acceptance suite (TestAccGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplate), since these require a live GitHub token this environment doesn't have
  • Manual diff review against the existing use_default/include_claim_keys and can_approve_pull_request_reviews patterns for consistency

@github-actions github-actions Bot added the Type: Feature New feature or request label Jul 28, 2026
@github-actions

Copy link
Copy Markdown

👋 Hi, and thank you for this contribution!

This repo is maintained by GitHub and community members on a best-effort basis. We'll get to this as soon as we can.

You can help us prioritize by joining the discussion on open issues and PRs, sharing details on the changes you need, and reviewing other contributions.


🤖 This is an automated message.

@deiga deiga added the r/repo_oidc_subject_claim_customization_tmpl actions_repository_oidc_subject_claim_customization_template label Jul 28, 2026
@RulerOf

RulerOf commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Just an FYI, I got bit by this today trying to resolve an issue with a new microservice's OIDC config. The API publishes use_immutable_subject but that value seems to lie.

In this older repo, the UI and API agree:

Google Chrome 2026-08-04 at 17 03 53
╰─❯ gh api /repos/BuyerQuest/redacted-old-repo/actions/oidc/customization/sub
{
  "use_default": true,
  "use_immutable_subject": false,
  "sub_claim_prefix": "repo:BuyerQuest/redacted-old-repo"
}

But this newer repo created after the compulsive switchover they disagree entirely:

Google Chrome 2026-08-04 at 16 57 22
gh api /repos/BuyerQuest/redacted-new-repo/actions/oidc/customization/sub
{
  "use_default": true,
  "use_immutable_subject": false,
  "sub_claim_prefix": "repo:BuyerQuest@123445689/redacted-new-repo@987654321"
}

I bring this up because what I wanted to do was inspect the true/false of use_immutable_subject from the data source to construct the sub, but it doesn't appear to be reliable via the API.

I'm using this as a workaround:

data "github_repository" "this" {
  full_name = "${local.github_org}/${var.github_repository}"
}

data "github_rest_api" "repo_oidc" {
  endpoint = "repos/${data.github_repository.this.full_name}/actions/oidc/customization/sub"
}

module "github_actions_role" {
  source  = "philips-labs/github-oidc/aws"
  version = "0.8.1"

  openid_connect_provider_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
  repo                        = data.github_repository.this.full_name
  role_name                   = "${local.name_prefix}-deploy"
  role_path                   = "/github-actions/"

  default_conditions = [
    "deny_pull_request",
  ]

  // GitHub reports the exact prefix it uses for this repository. This handles both the
  // name-based and immutable name@id formats without duplicating GitHub's selection logic.
  conditions = [
    {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:sub"
      values = [
        "${jsondecode(data.github_rest_api.repo_oidc.body).sub_claim_prefix}:environment:${github_repository_environment.this.environment}",
      ]
    },
  ]

Unless I'm mistaken, for the data source it may be worth computing it based on the qualities of sub_claim_prefix (e.g. an @ symbol in it anywhere means it should be true), or possibly disabling it until github fixes whatever the problem is.

…y OIDC subject claim template

Wires the use_immutable_subject and sub_claim_prefix fields (already present
on go-github v89's OIDCSubjectClaimCustomTemplate struct) through the
github_actions_repository_oidc_subject_claim_customization_template resource,
allowing existing repositories to opt into GitHub's immutable OIDC subject
claim format on a per-repository basis without an org-wide change.

Closes integrations#3548
@madriaanse377
madriaanse377 force-pushed the feat/oidc-immutable-subject-claims branch from 7878e57 to 08da046 Compare September 1, 2026 07:04
@dekokun

dekokun commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Not a maintainer, just helping with review triage, so this is a comment rather than a review.

Building on what @RulerOf found above: I checked the write side too, and sub_claim_prefix isn't just unreliable to read, it can't be set at all. It isn't a documented body parameter for PUT /repos/{owner}/{repo}/actions/oidc/customization/sub (it only appears in the GET response), and sending it is silently ignored. On a throwaway repo:

$ gh api -X PUT repos/OWNER/REPO/actions/oidc/customization/sub \
    --input - <<< '{"use_default": false, "include_claim_keys": ["repo"], "sub_claim_prefix": "test-prefix"}'
{}

$ gh api repos/OWNER/REPO/actions/oidc/customization/sub
{"use_default": false, "use_immutable_subject": false, "include_claim_keys": ["repo"],
 "sub_claim_prefix": "repo:OWNER@108519/REPO@1356554838"}

Same derived value before and after, exactly as in @RulerOf's examples.

That has two consequences for this PR specifically. The attribute is Optional + Computed, so a config value of "custom-prefix" against a read-back of the derived value should give "Provider produced inconsistent result after apply" - which means the example added to the docs wouldn't work as written. And the new acceptance subtest asserts sub_claim_prefix equals "custom-prefix", so I'd expect it to fail when someone runs it. Computed-only looks like the right shape for this field.

Separately, use_immutable_subject = false never reaches the API, because d.GetOk can't tell an explicit false from an unset bool. With a scratch test against a mocked API and use_immutable_subject = false in the config, the PUT body is:

{"use_default":false,"include_claim_keys":["repo"]}

The field is dropped. This matters because opting back out does work at the API level: on the same throwaway repo I set it to true and then back to false, and both took effect. Since the attribute is also Computed, the Read after apply writes true back into state, so someone trying to opt out via Terraform gets a silent no-op and a permanent diff.

There's an existing idiom for this in the repo: d.GetOkExists with a nolint comment, e.g. resource_github_repository_pages.go:193 ("necessary for bool fields") and resource_github_repository.go:636/645/1001.

I haven't run the acceptance tests.

ejfine added a commit to LabAutomationAndScreening/copier-aws-central-infrastructure that referenced this pull request Oct 6, 2026
## Link to Issue or Message thread

- GitHub changelog: [Immutable subject claims for GitHub Actions OIDC
tokens](https://github.blog/changelog/2026-04-23-immutable-subject-claims-for-github-actions-oidc-tokens/)

## Why is this change necessary?

Since 2026-07-15, GitHub enables immutable OIDC subject claims for every
newly created repository, and for any existing repository that is
renamed or transferred. Their Actions tokens carry `sub =
repo:<org>@<org_id>/<repo>@<repo_id>:<context>` instead of
`repo:<org>/<repo>:<context>`.

`create_oidc_assume_role_policy` only emitted the legacy form, so every
OIDC role this template creates (ECR push, CodeArtifact, central-infra
IaC, application OIDC, workload deploy/preview) rejects tokens from new
repos. Existing repos keep the legacy format until opted in, and GitHub
has announced no retirement date for it, so both formats must be
trusted.

## How does this change address the issue?

- New copier question `central_infra_github_organization_id` (validated
as a positive integer, with help text explaining how to look it up),
rendered into `GITHUB_ORG_IDS` in `iac_management/lib/constants.py`.
- The trust policy `sub` condition now lists both
`repo:<org>/<repo>:<ctx>` and `repo:<org>@<org_id>/<repo>@*:<ctx>`. The
org ID is pinned; the repo ID is wildcarded (a TODO documents why that
is acceptable and how to tighten it).
- The `sub` condition always uses `StringLike` (previously
`StringEquals` for ref-scoped roles), because the repo-ID wildcard would
otherwise be compared literally and main-only roles would still reject
new repos.
- `GithubOidcConfig` validation:
- restrictions may be `None` or a bare `*`; any other `*` or `?` is
rejected, so `StringLike` cannot silently widen a scoped role
- `repo_org` must have an entry in `GITHUB_ORG_IDS`, with an error that
explains how to find the ID (replaces a bare `KeyError` at policy build
time)
- TODOs pointing at removing the legacy format once all repos use
immutable subjects, and at managing `use_immutable_subject` from the
`Repo` class once
[terraform-provider-github#3582](integrations/terraform-provider-github#3582)
ships.

## What side effects does this change have?

- Every existing GitHub OIDC role's trust policy updates in place on the
next deploy (`assumeRolePolicyDocument` only; no creates, deletes, or
replaces). Verified in the downstream `aws-central-infrastructure`
preview: 56 roles in artifact-stores and 15 in iac-management.
- Ref-scoped roles switch from `StringEquals` to `StringLike`. For
literal restrictions (e.g. `ref:refs/heads/main`) this matches exactly
the same tokens, and the new validation prevents wildcard restrictions.
- Consumers must answer the new `central_infra_github_organization_id`
question on their next `copier update`.
- Consumers that build OIDC roles for repos in a second GitHub org must
add that org to `GITHUB_ORG_IDS`; the validation error says how.

## How is this change tested?

- New unit tests in
`template/tests/unit/iac_management/test_oidc_assume_role_policy.py`
covering both `sub` patterns, ref-scoped roles, wildcard restriction
rejection, bare `*` and explicit `None` restrictions, and unknown-org
rejection.
- Rendered locally with `tests/copier_data/data1.yaml` and checked the
generated `constants.py` and `.copier-answers.yml`.
downstream repo

## Other

Developed and reviewed in `aws-central-infrastructure` first, then
ported here commit by commit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added a setup question for the central infrastructure GitHub
organization ID, with guidance for finding it.
* OIDC role policies now support both legacy and immutable GitHub
subject claims for known organizations.
* Added validation for organization IDs and subject restrictions;
unsupported wildcard patterns are rejected.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

r/repo_oidc_subject_claim_customization_tmpl actions_repository_oidc_subject_claim_customization_template Type: Feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support use_immutable_subject on github_actions_repository_oidc_subject_claim_customization_template

4 participants