VNet peering RBAC remediation¶
Motivation¶
The Settings → VNet peering flow ("Peer & probe") failed repeatedly with
AuthorizationFailed / LinkedAuthorizationFailed and only offered the generic
peer-cluster-network.sh recovery command. That script calls
POST /api/aks/peer-with-platform, which peers platform ↔ AKS under the same
managed identity — it cannot fix a target-VNet ↔ AKS peering, so pasting it
left the operator stuck in the same error.
VNet peering requires …/virtualNetworkPeerings/write on both ends. The
dashboard managed identity holds Contributor on the AKS-VNet resource group but
has no write on the operator-selected target VNet, so both directions are denied.
User-facing change¶
When a target-VNet peering fails with an Azure RBAC denial, the peering result
payload now carries an rbac_remediation block and the Settings panel renders it
as an error line above the (still-shown) generic recovery hint. It contains:
- a one-line explanation that peering needs write on both ends and that
peer-cluster-network.shdoes not fix target-to-AKS peering; - a ready-to-paste, least-privilege
az role assignment creategrantingNetwork Contributorscoped to the target VNet (not its resource group or the subscription), with the managed-identity object id parsed out of the Azure error message.
After running the grant and re-clicking "Peer & probe", peering succeeds.
API / IaC diff summary¶
api/tasks/azure/peering.py: added_is_authorization_failure,_mi_object_id_from_error,_rbac_remediation;ensure_vnet_peering_with_targetnow attachesrbac_remediationto its payload when the peering error is an RBAC denial. No new Azure write is performed — the helper only renders the command.web/src/api/settings.ts:VnetPeeringResponsegains the optionalrbac_remediationobject.web/src/components/SettingsPanel.tsx: rendersrbac_remediation.message+commandwhen present, plus two affordances — a Copy command button and an "I granted the role — retry" button. The retry runspeerVneton a backoff loop (10s, then 20s, then 30s ×4 ≈ 2.5 min) to absorb Azure RBAC propagation delay (1-5 min) and stops as soon as the response no longer carriesrbac_remediation(both peering directions succeeded). The manual "Peer & probe" button is disabled while a retry loop is in flight to avoid a concurrent round-trip.- No IaC change. No new role is granted by the dashboard; the operator runs the grant out-of-band, keeping the managed identity least-privilege. The retry loop only re-issues the existing peering call — it never grants a role itself.
Validation¶
uv run pytest -q api/tests/test_azure_peering.py— 18 passed, including the newtest_target_helper_surfaces_rbac_remediation_on_authz_failure(asserts role, target-VNet scope, parsed MI object id) andtest_target_helper_omits_rbac_remediation_on_non_authz_error(no remediation on a non-RBAC fault).uv run ruff check api/tasks/azure/peering.py api/tests/test_azure_peering.py— clean.cd web && npm run build— succeeds.