Skip to content

Secretless OIDC deployment identity with just-in-time roles (#500) - #903

Merged
Roger Doherty (MSFT) (doherty100) merged 4 commits into
vnextfrom
feature/500-oidc-deployment-identity
Oct 20, 2026
Merged

Roger Doherty (MSFT) (doherty100) merged 4 commits into
vnextfrom
feature/500-oidc-deployment-identity

Conversation

@doherty100

Copy link
Copy Markdown
Contributor

Purpose

Implements #500. The Sandbox deployment workflow now authenticates the root sandbox providers with GitHub OIDC. It federates to a user-assigned managed identity that is not attached to jumplinux2 and holds standing Reader only. Before approving each run, the operator grants it time-bound Contributor and ABAC-conditioned Role Based Access Control Administrator in the portal.

  • rg-devops-iac: deployment_identity gains oidc. This creates the unattached identity, a standing Reader assignment and one federated identity credential per environment. The subjects use immutable ids (repository_owner_id:<id>:repository_id:<id>:environment:<name>) to match the repository's existing OIDC subject claim template, so no repository override is needed. The ids are looked up anonymously from the GitHub API using github_runner_url (new hashicorp/http provider). github_repository_id and github_repository_owner_id are optional overrides for private repositories. New outputs: deployment_identity_github_subjects and deployment_identity_jit_condition.
  • Root module: auth_mode accepts oidc, and use_oidc is wired into azurerm, azapi and azuread. The state backend still uses the VM's system-assigned identity.
  • Workflow and scripts:
    • Only the sandbox job gets id-token: write, and the workflow is now OIDC-only.
    • Preflight waits for the time-bound roles and stops unless at least SANDBOX_JIT_MIN_REMAINING_MINUTES remain (default 150). Apply and unit-test recheck the roles before they start.
    • Azure CLI and Az PowerShell sign in again with a fresh GitHub token for each stage, and during long test runs (Invoke-UnitTests.ps1 -SessionRefresh).
    • The selection test asserts the id-token scope.
  • Docs:
    • rg-devops-iac README: OIDC setup, the portal grant procedure with a CLI fallback, residual exposure, and the variable and output tables.
    • Root README: the authentication section.
    • CONTRIBUTING: the deployment environment and OIDC settings.
    • Copilot instructions.

Teardown stays manual. The workflow never destroys a sandbox.

Does this introduce a breaking change?

[x] Yes
[ ] No

Existing client_secret and managed_identity modes are unchanged. The workflow now requires auth_mode = "oidc". To migrate:

  1. Apply rg-devops-iac with deployment_identity = "oidc".
  2. Update SANDBOX_TFVARS_HCL in both environments (auth_mode = "oidc", client_id = the deployment_identity_client_id output).
  3. Grant the time-bound roles before each run.

Pull Request Type

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Documentation content changes
[ ] Other... Please describe:

How to Test

  • Apply rg-devops-iac with deployment_identity = "oidc". Verify the identity has only Reader, the federated credential subjects match deployment_identity_github_subjects, and jumplinux2 has a system-assigned identity only.
  • After this reaches main: grant the time-bound roles, approve a Sandbox deployment run, and confirm preflight, apply and unit-test pass.
  • Negative checks:
    • A run without a grant fails fast in preflight.
    • A short remaining window is refused.
    • The VM's managed identity endpoint can't get tokens for the deployment identity.

What to Check

Other Information

Add a third auth mode, oidc, so the Sandbox deployment workflow signs in
with GitHub Actions OIDC to an unattached user-assigned managed identity
that holds standing Reader only. The operator grants time-bound Contributor
and conditioned Role Based Access Control Administrator before approving
each run.

- rg-devops-iac: deployment_identity accepts oidc, creating the unattached
  identity, standing Reader, and per-environment federated credentials
  with custom subjects (repository_id, environment, workflow_ref). The
  repository owner/name is derived from the repository-level
  github_runner_url. New github_repository_id, github_oidc_environments
  and github_oidc_workflow_ref inputs, and deployment_identity_github_subjects
  and deployment_identity_jit_condition outputs.
- Root: auth_mode accepts oidc and wires use_oidc into all providers.
- Workflow: only the sandbox job gets id-token: write (asserted by
  Test-SandboxDeploymentSelection.sh).
- Deployment scripts: re-login per stage with a fresh GitHub ID token,
  preflight waits for the JIT roles and enforces a minimum remaining
  window (SANDBOX_JIT_MIN_REMAINING_MINUTES, default 150), and apply and
  unit-test re-check the roles. Invoke-UnitTests.ps1 gains -SessionRefresh
  to re-authenticate before each test suite.
- The state backend keeps using the VM's system-assigned identity.
- Docs: rg-devops-iac README OIDC setup and portal JIT procedure, root
  README, CONTRIBUTING.md and copilot instructions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the federated credential subjects to the subject claim template
{"include_claim_keys":["repository_owner_id","repository_id","context"]}
that organizations commonly require, instead of overriding the
repository's template with a workflow_ref-based one.

- Replace github_oidc_workflow_ref with github_repository_owner_id.
- Drop the repository-URL requirement on github_runner_url for oidc.
- README: check the existing template first and set it only when the
  repository uses the default; document the workflow-file trade-off.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Derive github_repository_id and github_repository_owner_id from the
repository in github_runner_url with an anonymous GitHub API request
(hashicorp/http data source), so oidc needs no extra tfvars for public
repositories. Both variables remain optional overrides for private
repositories or to skip the lookup. The request is unauthenticated
because data source arguments are saved in state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a maintainer section listing the environment and OIDC subject
template settings that restrict the deployment identity, and link the
release approval step to emergency merges.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant