Fix Azure verification during RBAC propagation - #12764
Conversation
Authenticate to the Azure tenant without requiring immediate subscription visibility, then refresh and retry the configured subscription with bounded exponential backoff before continuing the existing checks. Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Bound the wait step's wall-clock duration, assert its timeout and environment wiring, and clarify that persistent subscription discovery failures can reach the bounded timeout. Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12764 +/- ##
=======================================
Coverage 54.18% 54.19%
=======================================
Files 774 774
Lines 52113 52113
=======================================
+ Hits 28240 28243 +3
+ Misses 21224 21223 -1
+ Partials 2649 2647 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Extend subscription discovery through Azure's documented 10-minute RBAC propagation window and keep a larger hard timeout for hung CLI calls. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc4d5a9-1134-4c5b-9f8f-7f8cd1ad6bed Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com>
There was a problem hiding this comment.
Pull request overview
Updates Azure verification to tolerate RBAC propagation delays while preserving credential and access checks.
Changes:
- Adds tenant-scope login and bounded subscription discovery retries.
- Adds deterministic tests and Make integration.
- Updates workflow and contributor documentation.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
docs/contributing/contributing-deploy-environments.md |
Updates Azure setup and troubleshooting guidance. |
build/test.mk |
Registers the focused verification test. |
.github/extension/verify-azure.yml |
Adds subscription retry, matching, selection, and timeout logic. |
.github/extension/verify-azure_test.sh |
Tests retry behavior and workflow contracts. |
.github/extension/README.md |
Documents updated Azure verification behavior. |
Suppressed comments (1)
docs/contributing/contributing-deploy-environments.md:61
- The AWS template does not have a separate wait and then cloud-verification sequence:
verify-aws.ymlrunsaws sts get-caller-identityas its credential-verification step, followed immediately by EKS access. As written, step 2 says to run STS while step 3 says to verify it again from a preceding step that does not exist, so this walkthrough is misleading. Collapse these into one cloud-verification step so the sequence matches the template.
2. Waits for cloud access. Azure refreshes the subscription list immediately, then retries with bounded exponential backoff for about 10 minutes before selecting `AZURE_SUBSCRIPTION_ID`; the step also has a 12-minute hard timeout for a hung Azure CLI call. AWS runs `aws sts get-caller-identity`.
3. Verifies cloud access (Azure: `az account show`; AWS: the caller identity returned by the preceding step).
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Describe AWS credential verification as the single STS check performed by the workflow while retaining Azure's subscription wait and account check. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc4d5a9-1134-4c5b-9f8f-7f8cd1ad6bed Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com>
DariuszPorowski
left a comment
There was a problem hiding this comment.
praise: Preserve end-to-end verification during Azure RBAC propagation
The tenant-scope login and bounded subscription discovery retry fix the propagation race without suppressing the Azure, AKS, or GHCR checks.
Fail malformed subscription IDs before entering the RBAC propagation retry and cover the fast-failure contract in the workflow test. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc4d5a9-1134-4c5b-9f8f-7f8cd1ad6bed Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com>
Resolve the build/test.mk conflict by retaining the latest main test targets and validation packages alongside test-verify-azure. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc4d5a9-1134-4c5b-9f8f-7f8cd1ad6bed Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com>
Radius functional test overviewClick here to see the test run details
Test Status⌛ Building Radius and pushing container images for functional tests... |
….1 (#12831) Patch release `v0.60.1` for channel `0.60`. ## Backported PRs Listed in cherry-pick (topological) order. **Gap filler** = not requested for the patch, but included because it touched the same region of a shared file between `release/0.60` and a requested commit. Without it the cherry-pick has no common merge base and conflicts. | # | Commit | PR | Role | | --- | --- | --- | --- | | 1 | `6882b5fdf` | #12640 | Requested | | 2 | `af0443809` | #12721 | Requested | | 3 | `7c6de043d` | #12733 | **Gap filler** — `.github/workflows/codeql.yml` | | 4 | `ca7b5a3e7` | #12728 | Requested | | 5 | `2eac7c1f1` | #12702 | Requested | | 6 | `b4ebe2f28` | #12751 | Requested | | 7 | `e30a2594d` | #12727 | Requested | | 8 | `3c7dfecd6` | #12769 | **Gap filler** — `.github/workflows/codeql.yml` | | 9 | `787b6ca1a` | #12765 | **Gap filler** — `build/test.mk` | | 10 | `f84184955` | #12775 | Requested | | 11 | `415d5b9c2` | #12779 | Requested | | 12 | `a1d976013` | #12782 | Requested | | 13 | `14a21bd0d` | #12786 | **Gap filler** — `build/test.mk` | | 14 | `a87146c77` | #12764 | **Gap filler** — `build/test.mk` | | 15 | `073ca3cfa` | #12758 | Requested | | 16 | `5e1f94c13` | #12785 | **Gap filler** — `pkg/cli/cmd/install/kubernetes/kubernetes.go` | | 17 | `1fd650af5` | #12742 | Requested | | 18 | `19376c3f3` | #12829 | Requested | Five of the six gap fillers are CI/test-only. #12785 is not: it adds a Helm `control-plane-readiness` Job, a `pre-upgrade wait-for-control-plane` command, UCP readiness probes, and a NetworkPolicy change. It is required for #12829 to apply. ## Validation Full chain dry-run cherry-picked onto `release/0.60` with zero conflicts. `go build ./...` clean; `pkg/cli/cmd/install/kubernetes`, `cmd/pre-upgrade/cmd`, `pkg/cli/...`, `pkg/graph/...`, and `test/validation` all pass. --------- Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
## Summary Updates the canonical Repo Radius Azure credential-verification workflow so newly-created role assignments can propagate without skipping end-to-end verification. `azure/login` now authenticates at tenant scope with the action's exact `allow-no-subscriptions: true` input. The next step checks subscription visibility immediately, refreshes Azure CLI account state on every attempt, compares the configured subscription ID with `jq`, and selects it before the existing Azure credential, AKS, and GHCR checks continue unchanged. A step-level 12-minute timeout bounds the wall clock if an Azure CLI refresh hangs. ## Reason for change This supersedes [radius-project/ai-extensions#444](radius-project/ai-extensions#444). That PR avoids the propagation race by suppressing the immediate verification dispatch in the frontend after it creates a Contributor role assignment. Skipping the dispatch removes the end-to-end signal and puts Azure-specific workflow timing policy in the wrong repository. Handling propagation in `.github/extension/verify-azure.yml` keeps verification enabled and makes the canonical upstream workflow responsible for its own Azure subscription-visibility semantics. The retry is bounded and is not a fixed startup delay: - Check immediately after OIDC login. - Retry up to 23 attempts with 5, 10, 20, then 30-second capped delays (10 minutes 5 seconds of total waiting before the final check). - Run `az account list --refresh` on every attempt without printing its JSON response. - Select only the configured subscription after an exact, case-insensitive ID match. - Apply a 12-minute step timeout so a hung CLI call cannot fall through to GitHub's multi-hour job default. Genuine OIDC, client, tenant, or federated-credential errors still fail immediately in `azure/login`. A missing subscription variable fails before discovery starts, and a direct nonzero exit from `az account list --refresh` fails immediately under `set -euo pipefail`. Azure CLI can internally absorb some subscription-discovery exceptions and return successfully with cached tenant-level state; persistent discovery failure therefore reaches the same bounded no-visible-subscription timeout as RBAC propagation or a missing role assignment. The timeout diagnostic names all three possibilities. ## How to test Exact focused validation run after the review fixes: - `make test-verify-azure` — passed (`Azure verification workflow tests passed`). - `shellcheck --rcfile .github/linters/.shellcheckrc .github/extension/verify-azure_test.sh` — passed with no findings. - `ruby -e "require 'yaml'; data=YAML.load_file('.github/extension/verify-azure.yml'); abort 'missing jobs' unless data['jobs']; puts 'workflow structure parsed'"` — passed (`workflow structure parsed`). - `npx --yes markdown-table-formatter@1.7.0 '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md' --check` — passed; no formatting changes required. - `npx --yes markdownlint-cli2@0.23.2 '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md' --config './.github/linters/.markdownlint-cli2.yaml'` — passed with 0 issues. - `npx --yes cspell@9.6.1 lint --config ./.github/linters/.cspell.yml --no-progress '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md'` — passed with 0 issues. The deterministic workflow test extracts and executes the real retry `run:` block with stubbed `az` and `sleep` commands. It covers immediate visibility, case-insensitive safe matching, missing configuration, delayed visibility and exponential backoff, a retry budget of at least 10 minutes, timeout without selecting a different subscription, direct Azure CLI failure, and the `azure/login` input contract. Its named wait-step assertion also pins `timeout-minutes: 12` and the exact `AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}` environment mapping. ## File change summary | File | Summary of change | | ---- | ----------------- | | `.github/extension/verify-azure.yml` | Authenticates without requiring immediate subscription visibility, then refreshes, retries for at least 10 minutes, matches, and selects the configured subscription under a 12-minute hard timeout. | | `.github/extension/verify-azure_test.sh` | Adds deterministic behavior tests and named-step contract assertions for login inputs, retry duration, timeout, and subscription-variable wiring. | | `build/test.mk` | Adds the focused test target to the standard unit-test prerequisites. | | `.github/extension/README.md` | Documents tenant-scope login, bounded propagation/discovery retry, hard timeout, selection, and failure behavior. | | `docs/contributing/contributing-deploy-environments.md` | Updates the contributor workflow and troubleshooting guidance for Azure RBAC propagation and subscription-discovery failure. | --------- Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc4d5a9-1134-4c5b-9f8f-7f8cd1ad6bed (cherry picked from commit a87146c) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
….1 (#12831) Patch release `v0.60.1` for channel `0.60`. ## Backported PRs Listed in cherry-pick (topological) order. **Gap filler** = not requested for the patch, but included because it touched the same region of a shared file between `release/0.60` and a requested commit. Without it the cherry-pick has no common merge base and conflicts. | # | Commit | PR | Role | | --- | --- | --- | --- | | 1 | `6882b5fdf` | #12640 | Requested | | 2 | `af0443809` | #12721 | Requested | | 3 | `7c6de043d` | #12733 | **Gap filler** — `.github/workflows/codeql.yml` | | 4 | `ca7b5a3e7` | #12728 | Requested | | 5 | `2eac7c1f1` | #12702 | Requested | | 6 | `b4ebe2f28` | #12751 | Requested | | 7 | `e30a2594d` | #12727 | Requested | | 8 | `3c7dfecd6` | #12769 | **Gap filler** — `.github/workflows/codeql.yml` | | 9 | `787b6ca1a` | #12765 | **Gap filler** — `build/test.mk` | | 10 | `f84184955` | #12775 | Requested | | 11 | `415d5b9c2` | #12779 | Requested | | 12 | `a1d976013` | #12782 | Requested | | 13 | `14a21bd0d` | #12786 | **Gap filler** — `build/test.mk` | | 14 | `a87146c77` | #12764 | **Gap filler** — `build/test.mk` | | 15 | `073ca3cfa` | #12758 | Requested | | 16 | `5e1f94c13` | #12785 | **Gap filler** — `pkg/cli/cmd/install/kubernetes/kubernetes.go` | | 17 | `1fd650af5` | #12742 | Requested | | 18 | `19376c3f3` | #12829 | Requested | Five of the six gap fillers are CI/test-only. #12785 is not: it adds a Helm `control-plane-readiness` Job, a `pre-upgrade wait-for-control-plane` command, UCP readiness probes, and a NetworkPolicy change. It is required for #12829 to apply. ## Validation Full chain dry-run cherry-picked onto `release/0.60` with zero conflicts. `go build ./...` clean; `pkg/cli/cmd/install/kubernetes`, `cmd/pre-upgrade/cmd`, `pkg/cli/...`, `pkg/graph/...`, and `test/validation` all pass. --------- Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> (cherry picked from commit e5938bf) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
## Summary Updates the canonical Repo Radius Azure credential-verification workflow so newly-created role assignments can propagate without skipping end-to-end verification. `azure/login` now authenticates at tenant scope with the action's exact `allow-no-subscriptions: true` input. The next step checks subscription visibility immediately, refreshes Azure CLI account state on every attempt, compares the configured subscription ID with `jq`, and selects it before the existing Azure credential, AKS, and GHCR checks continue unchanged. A step-level 12-minute timeout bounds the wall clock if an Azure CLI refresh hangs. ## Reason for change This supersedes [radius-project/ai-extensions#444](radius-project/ai-extensions#444). That PR avoids the propagation race by suppressing the immediate verification dispatch in the frontend after it creates a Contributor role assignment. Skipping the dispatch removes the end-to-end signal and puts Azure-specific workflow timing policy in the wrong repository. Handling propagation in `.github/extension/verify-azure.yml` keeps verification enabled and makes the canonical upstream workflow responsible for its own Azure subscription-visibility semantics. The retry is bounded and is not a fixed startup delay: - Check immediately after OIDC login. - Retry up to 23 attempts with 5, 10, 20, then 30-second capped delays (10 minutes 5 seconds of total waiting before the final check). - Run `az account list --refresh` on every attempt without printing its JSON response. - Select only the configured subscription after an exact, case-insensitive ID match. - Apply a 12-minute step timeout so a hung CLI call cannot fall through to GitHub's multi-hour job default. Genuine OIDC, client, tenant, or federated-credential errors still fail immediately in `azure/login`. A missing subscription variable fails before discovery starts, and a direct nonzero exit from `az account list --refresh` fails immediately under `set -euo pipefail`. Azure CLI can internally absorb some subscription-discovery exceptions and return successfully with cached tenant-level state; persistent discovery failure therefore reaches the same bounded no-visible-subscription timeout as RBAC propagation or a missing role assignment. The timeout diagnostic names all three possibilities. ## How to test Exact focused validation run after the review fixes: - `make test-verify-azure` — passed (`Azure verification workflow tests passed`). - `shellcheck --rcfile .github/linters/.shellcheckrc .github/extension/verify-azure_test.sh` — passed with no findings. - `ruby -e "require 'yaml'; data=YAML.load_file('.github/extension/verify-azure.yml'); abort 'missing jobs' unless data['jobs']; puts 'workflow structure parsed'"` — passed (`workflow structure parsed`). - `npx --yes markdown-table-formatter@1.7.0 '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md' --check` — passed; no formatting changes required. - `npx --yes markdownlint-cli2@0.23.2 '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md' --config './.github/linters/.markdownlint-cli2.yaml'` — passed with 0 issues. - `npx --yes cspell@9.6.1 lint --config ./.github/linters/.cspell.yml --no-progress '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md'` — passed with 0 issues. The deterministic workflow test extracts and executes the real retry `run:` block with stubbed `az` and `sleep` commands. It covers immediate visibility, case-insensitive safe matching, missing configuration, delayed visibility and exponential backoff, a retry budget of at least 10 minutes, timeout without selecting a different subscription, direct Azure CLI failure, and the `azure/login` input contract. Its named wait-step assertion also pins `timeout-minutes: 12` and the exact `AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}` environment mapping. ## File change summary | File | Summary of change | | ---- | ----------------- | | `.github/extension/verify-azure.yml` | Authenticates without requiring immediate subscription visibility, then refreshes, retries for at least 10 minutes, matches, and selects the configured subscription under a 12-minute hard timeout. | | `.github/extension/verify-azure_test.sh` | Adds deterministic behavior tests and named-step contract assertions for login inputs, retry duration, timeout, and subscription-variable wiring. | | `build/test.mk` | Adds the focused test target to the standard unit-test prerequisites. | | `.github/extension/README.md` | Documents tenant-scope login, bounded propagation/discovery retry, hard timeout, selection, and failure behavior. | | `docs/contributing/contributing-deploy-environments.md` | Updates the contributor workflow and troubleshooting guidance for Azure RBAC propagation and subscription-discovery failure. | --------- Signed-off-by: Ryan Waite <ryanwjwaite@outlook.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc4d5a9-1134-4c5b-9f8f-7f8cd1ad6bed (cherry picked from commit a87146c) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
….1 (#12831) Patch release `v0.60.1` for channel `0.60`. ## Backported PRs Listed in cherry-pick (topological) order. **Gap filler** = not requested for the patch, but included because it touched the same region of a shared file between `release/0.60` and a requested commit. Without it the cherry-pick has no common merge base and conflicts. | # | Commit | PR | Role | | --- | --- | --- | --- | | 1 | `6882b5fdf` | #12640 | Requested | | 2 | `af0443809` | #12721 | Requested | | 3 | `7c6de043d` | #12733 | **Gap filler** — `.github/workflows/codeql.yml` | | 4 | `ca7b5a3e7` | #12728 | Requested | | 5 | `2eac7c1f1` | #12702 | Requested | | 6 | `b4ebe2f28` | #12751 | Requested | | 7 | `e30a2594d` | #12727 | Requested | | 8 | `3c7dfecd6` | #12769 | **Gap filler** — `.github/workflows/codeql.yml` | | 9 | `787b6ca1a` | #12765 | **Gap filler** — `build/test.mk` | | 10 | `f84184955` | #12775 | Requested | | 11 | `415d5b9c2` | #12779 | Requested | | 12 | `a1d976013` | #12782 | Requested | | 13 | `14a21bd0d` | #12786 | **Gap filler** — `build/test.mk` | | 14 | `a87146c77` | #12764 | **Gap filler** — `build/test.mk` | | 15 | `073ca3cfa` | #12758 | Requested | | 16 | `5e1f94c13` | #12785 | **Gap filler** — `pkg/cli/cmd/install/kubernetes/kubernetes.go` | | 17 | `1fd650af5` | #12742 | Requested | | 18 | `19376c3f3` | #12829 | Requested | Five of the six gap fillers are CI/test-only. #12785 is not: it adds a Helm `control-plane-readiness` Job, a `pre-upgrade wait-for-control-plane` command, UCP readiness probes, and a NetworkPolicy change. It is required for #12829 to apply. ## Validation Full chain dry-run cherry-picked onto `release/0.60` with zero conflicts. `go build ./...` clean; `pkg/cli/cmd/install/kubernetes`, `cmd/pre-upgrade/cmd`, `pkg/cli/...`, `pkg/graph/...`, and `test/validation` all pass. --------- Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> (cherry picked from commit e5938bf) Signed-off-by: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com>
Summary
Updates the canonical Repo Radius Azure credential-verification workflow so newly-created role assignments can propagate without skipping end-to-end verification.
azure/loginnow authenticates at tenant scope with the action's exactallow-no-subscriptions: trueinput. The next step checks subscription visibility immediately, refreshes Azure CLI account state on every attempt, compares the configured subscription ID withjq, and selects it before the existing Azure credential, AKS, and GHCR checks continue unchanged. A step-level 12-minute timeout bounds the wall clock if an Azure CLI refresh hangs.Reason for change
This supersedes radius-project/ai-extensions#444. That PR avoids the propagation race by suppressing the immediate verification dispatch in the frontend after it creates a Contributor role assignment. Skipping the dispatch removes the end-to-end signal and puts Azure-specific workflow timing policy in the wrong repository. Handling propagation in
.github/extension/verify-azure.ymlkeeps verification enabled and makes the canonical upstream workflow responsible for its own Azure subscription-visibility semantics.The retry is bounded and is not a fixed startup delay:
az account list --refreshon every attempt without printing its JSON response.Genuine OIDC, client, tenant, or federated-credential errors still fail immediately in
azure/login. A missing subscription variable fails before discovery starts, and a direct nonzero exit fromaz account list --refreshfails immediately underset -euo pipefail. Azure CLI can internally absorb some subscription-discovery exceptions and return successfully with cached tenant-level state; persistent discovery failure therefore reaches the same bounded no-visible-subscription timeout as RBAC propagation or a missing role assignment. The timeout diagnostic names all three possibilities.How to test
Exact focused validation run after the review fixes:
make test-verify-azure— passed (Azure verification workflow tests passed).shellcheck --rcfile .github/linters/.shellcheckrc .github/extension/verify-azure_test.sh— passed with no findings.ruby -e "require 'yaml'; data=YAML.load_file('.github/extension/verify-azure.yml'); abort 'missing jobs' unless data['jobs']; puts 'workflow structure parsed'"— passed (workflow structure parsed).npx --yes markdown-table-formatter@1.7.0 '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md' --check— passed; no formatting changes required.npx --yes markdownlint-cli2@0.23.2 '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md' --config './.github/linters/.markdownlint-cli2.yaml'— passed with 0 issues.npx --yes cspell@9.6.1 lint --config ./.github/linters/.cspell.yml --no-progress '.github/extension/README.md' 'docs/contributing/contributing-deploy-environments.md'— passed with 0 issues.The deterministic workflow test extracts and executes the real retry
run:block with stubbedazandsleepcommands. It covers immediate visibility, case-insensitive safe matching, missing configuration, delayed visibility and exponential backoff, a retry budget of at least 10 minutes, timeout without selecting a different subscription, direct Azure CLI failure, and theazure/logininput contract. Its named wait-step assertion also pinstimeout-minutes: 12and the exactAZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}environment mapping.File change summary
.github/extension/verify-azure.yml.github/extension/verify-azure_test.shbuild/test.mk.github/extension/README.mddocs/contributing/contributing-deploy-environments.md