Repository navigation
ci: stage MSAL Go pipeline + add Managed Identity IMDS & Azure Arc E2E tests - #641
Merged
Merged
Conversation
Gladwin Johnson VR (gladjohn)
force-pushed
the
gladjohn/mi-e2e-imds
branch
from
July 28, 2026 04:24
6faf5aa to
ff3c0a5
Compare
Gladwin Johnson VR (gladjohn)
changed the base branch from
gladjohn/ci-stage-build-test
to
main
July 28, 2026 04:31
Gladwin Johnson VR (gladjohn)
force-pushed
the
gladjohn/mi-e2e-imds
branch
from
July 28, 2026 04:37
ff3c0a5 to
515cf60
Compare
Gladwin Johnson VR (gladjohn)
marked this pull request as ready for review
July 28, 2026 04:42
Gladwin Johnson VR (gladjohn)
requested review from
Nilesh Choudhary (4gust) and
Bogdan Gavril (bgavrilMS)
July 28, 2026 04:46
Gladwin Johnson VR (gladjohn)
enabled auto-merge (squash)
July 28, 2026 04:55
…E tests Restructure ado/build_test.yaml from a single job into discrete stages (modeled on MSAL .NET) and add Managed Identity E2E coverage for both IMDS (VM) and Azure Arc. Pipeline stages: - Build - Unit Tests (dependsOn Build) - Integration Tests (dependsOn Build) - MI E2E - IMDS (self-hosted MISEManagedIdentity pool) - MI E2E - Azure Arc (self-hosted MISEAZUREARC pool) Managed Identity E2E tests (apps/tests/e2e, build tag "e2e"): - TestManagedIdentityImds: acquires an ARM token over IMDS for the system-assigned identity and each user-assigned binding (client ID, resource ID, object ID). - TestManagedIdentityAzureArc: acquires an ARM token for the system-assigned identity (Arc supports SAMI only). - Both assert the first call hits the identity provider and the second is served from the token cache. They reuse the same lab identities as the MSAL .NET IMDS/Arc E2E tests. Each stage selects its test via "go test -run". Locked tooling (build/tools): gotestsum is installed from a dedicated module with committed go.mod + go.sum via a lock-file-enforcing command, satisfying SonarCloud S8545 (no unpinned "go install pkg@version"). Air-gapped Arc agent: the MISEAZUREARC agent has no outbound Go connectivity, so the Arc stage uses a pre-baked Go toolchain (C:\Go) and a pre-baked Go module cache (C:\gomodcache), running "go test" with GOPROXY=off; it falls back to warming the cache via curl from go.sum when no pre-baked cache exists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2d02b5ca-22b5-4b48-840a-de79786b3f9d
Gladwin Johnson VR (gladjohn)
force-pushed
the
gladjohn/mi-e2e-imds
branch
from
July 28, 2026 13:17
7b8f831 to
994f96d
Compare
|
Nilesh Choudhary (4gust)
approved these changes
Jul 28, 2026
Gladwin Johnson VR (gladjohn)
added a commit
that referenced
this pull request
Aug 2, 2026
Move the Azure Arc non-existent user-assigned identity E2E test into the build-tagged e2e suite (apps/tests/e2e/managedidentity_arc_e2e_test.go) that runs on the MISEAZUREARC pool, following the pattern from #641, and remove the ad-hoc self-gating version from the unit test file. The test requests an unassigned UAMI and asserts a 404 / identity_not_found with no token, mirroring MSAL.NET's AcquireToken_ForNonExistentUami_OnAzureArc_Fails. Broaden the Arc E2E stage -run filter so both Arc E2E tests execute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c
Gladwin Johnson VR (gladjohn)
added a commit
that referenced
this pull request
Aug 10, 2026
….NET #6128 parity) (#644) * feat(managedidentity): Azure Arc user-assigned managed identity fail-closed support Port of MSAL.NET #6128. Remove the client-side block that rejected user-assigned managed identities on Azure Arc, forward the client_id / mi_res_id / object_id selector on the Arc token request, and validate the identity the agent echoes in the token response against the requested selector (case-insensitive; accepts both mi_res_id and msi_res_id). Fail closed (error, no token) when the echo is absent or does not match, so a legacy agent's silent auto-SAMI is never returned for a UAMI request. System-assigned is unaffected; a 404 surfaces as a normal service error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(managedidentity): reduce Arc test cognitive complexity; assert 404 in Arc UAMI E2E - Extract the per-case bodies of TestAzureArcUserAssignedHonored and TestAzureArcUserAssignedNotHonoredFailsClosed into helpers to drop the loop/closure nesting flagged by SonarCloud (go:S3776, complexity 17/16 > 15). - Tighten TestAzureArcUserAssignedNonExistentE2E to assert a 404 / identity_not_found for a non-existent UAMI, matching MSAL.NET's AcquireToken_ForNonExistentUami_OnAzureArc_Fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(e2e): add Azure Arc non-existent UAMI E2E test to the e2e suite Move the Azure Arc non-existent user-assigned identity E2E test into the build-tagged e2e suite (apps/tests/e2e/managedidentity_arc_e2e_test.go) that runs on the MISEAZUREARC pool, following the pattern from #641, and remove the ad-hoc self-gating version from the unit test file. The test requests an unassigned UAMI and asserts a 404 / identity_not_found with no token, mirroring MSAL.NET's AcquireToken_ForNonExistentUami_OnAzureArc_Fails. Broaden the Arc E2E stage -run filter so both Arc E2E tests execute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(managedidentity): add Azure Arc UAMI cache-partitioning test Port .NET's AzureArcUserAssignedManagedIdentityCacheIsPartitionedByIdentity: assert the token cache is partitioned by the requested identity so a cache lookup never returns a token for a different identity. SAMI and each of the three user-assigned selectors get a distinct token; the first acquisition per identity is a cache miss (identity provider) and the second returns that identity's own token from cache (with no queued HTTP responses, so a miss would panic). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(managedidentity): assert Arc UAMI cache keys, not just behavior Address review: in addition to the behavioral round-trip proof, assert the managed-identity token cache keys directly (via Manager.Marshal) so the test matches .NET's RecordAccess client-id check. There must be exactly one access-token entry per identity, and each identity's identifier (the system-assigned default and each user-assigned client/resource/object id) must key exactly one entry, which catches a key collision that behavior alone might mask. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * refactor(managedidentity): rename miQueryParameterResourceIdIMDS to miQueryParameterMsiResourceId The constant holds the msi_res_id resource-id spelling and is no longer IMDS-only: verifyAzureArcUserAssignedIdentity accepts it as one of the two resource-id spellings Azure Arc may echo. Name it after the value (the msi_res_id parameter) rather than a single source, per PR review feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * fix(managedidentity): send msi_res_id (not mi_res_id) for Azure Arc resource-id UAMI Live testing against the Azure Connected Machine agent (1.67, user-assigned preview) showed the Arc token endpoint only honors the IMDS 'msi_res_id' spelling for the resource-id selector: a 'mi_res_id' request is silently ignored and returns the system-assigned identity, so a resource-id UAMI always failed closed. Send 'msi_res_id' on the Arc request (matching IMDS); the echo verification still accepts either spelling as a safety net. Update the happy-path/cache-partition tests to the msi_res_id request+echo and repurpose the echo-spelling test to cover the mi_res_id echo fallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(e2e): accept fail-closed message (not just 404) for non-existent Arc UAMI A non-existent user-assigned identity returns no token on any Azure Arc agent, but the surfaced error differs by agent: a user-assigned-aware agent returns 404 (identity_not_found), while a legacy agent ignores the selector and returns the system-assigned identity, which MSAL rejects (fail closed). Accept either error and correct the comment that claimed every agent returns 404, per review feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(managedidentity): de-duplicate Arc honored-path helpers (SonarCloud) Extract the shared Azure Arc happy-path setup into assertArcEchoHonored so both assertArcUserAssignedHonored and the mi_res_id echo safety-net test are thin wrappers, removing the duplicated mock-setup block flagged by the SonarCloud duplication gate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * test(managedidentity): route Arc helpers through one mock-setup helper (SonarCloud) Extract newArcChallengeClient so assertArcEchoHonored, assertArcUserAssignedFailsClosed, and assertArcAcquireFromIdP no longer each repeat the Azure Arc 401-challenge + 200-body mock setup. This removes the remaining duplicated block flagged by the SonarCloud duplication gate on new code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c * refactor(managedidentity): extract shared user-assigned selector helper (SonarCloud) The msi_res_id fix made the Azure Arc request-building switch byte-for-byte identical to the IMDS one, which SonarCloud flagged as duplicated code on new code (20.5%). Extract setUserAssignedQueryParam and route IMDS, Azure Arc, and verifyAzureArcUserAssignedIdentity through it, so the type switch over the identity kind exists exactly once. Behavior is unchanged; App Service keeps its own mi_res_id spelling. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ad57850-7d1f-4b9a-bf9f-723d69a9687c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Restructures the MSAL Go CI pipeline into discrete stages (modeled on MSAL .NET) and adds Managed Identity E2E tests for both IMDS (VM) and Azure Arc, running on the same self-hosted agents MSAL .NET uses.
1. Pipeline staging (
ado/build_test.yaml)The previous single job is split into stages, each reporting independently in Azure DevOps:
ubuntu-latestgo build ./apps/...ubuntu-latestgo test -race -shortacrossapps/...ubuntu-latestgo test -race ./apps/tests/integration/...MISEManagedIdentityMISEAZUREARCThe two MI E2E stages are independent (
dependsOn: []) and run on every PR.2. Managed Identity E2E tests (
apps/tests/e2e/, build tage2e)TestManagedIdentityImds— acquires an ARM token (https://management.azure.com) over IMDS for the system-assigned identity and each user-assigned binding: client ID, resource ID, object ID.TestManagedIdentityAzureArc— acquires an ARM token for the system-assigned identity (Azure Arc supports SAMI only).go test -run, so neither runs the other's environment-specific test.e2ebuild tag, so normalgo build/go testexcludes them.3. Test reporting + locked tooling (
build/tools/)Why
gotestsum? The stages run tests throughgotestsum(rather than the plainGo@0test task) so each stage publishes JUnit test results to the ADO test UI viaPublishTestResults@2— you get per-test pass/fail, not just a job exit code.Why a separate module?
gotestsummust be installed, and the obviousgo install gotest.tools/gotestsum@<version>trips SonarCloud S8545 ("Go dependencies should be locked to verified versions"). To fix that,build/toolsis a small dedicated module that pins the tool via committedgo.mod+go.sum, installed with a lock-file-enforcing command:go.mod— pinsgotest.tools/gotestsum v1.12.2(newest release compatible with the pipeline's Go 1.22.3).tools.go— build-tagged (//go:build tools), never compiled into any binary; blank-imports gotestsum's command packages sogo mod tidykeepsgo.sumcomplete (standard Go "tools module" pattern).go.sum— the lock file (checksums of gotestsum + transitive deps).It's kept as its own module so the tooling's dependencies stay out of the published MSAL library's dependency graph. This is a Sonar/reporting concern only — unrelated to the Arc network issue below.
4. Air-gapped Azure Arc agent
The
MISEAZUREARCagent has no outbound Go connectivity — the Go downloader andcurltogo.dev/proxy.golang.orgare TLS-reset by egress inspection, soGoTool@0and on-demandgo install/ module downloads fail on that pool (they work fine on theubuntu-latestand IMDS agents). The Arc stage therefore:C:\Go) and a pre-baked Go module cache (C:\gomodcache), runninggo testwithGOPROXY=off(fully offline);go.sumviacurlwhen no pre-baked cache is present.The module set (8 modules) is derived from
go.sumand was verified to build the E2E test offline.Validation
go vet -tags e2e ./apps/tests/e2e/...— clean; both tests compile & list.gofmt -l— clean.build/toolsgo.sumisgo mod tidy-stable; lock-enforcing install verified locally.GOPROXY=off+ pre-baked cache builds the E2E test.msal-go-cistages green: Build, Unit, Integration, MI E2E – IMDS, MI E2E – Azure Arc; SonarCloud gate passing.Follow-up
Bake
C:\Go+C:\gomodcacheinto theMISEAZUREARC(and, if desired,MISEManagedIdentity) agent image so the offline cache is durable across reimaging.