Repository navigation
Phase 6: deploy the metrics infrastructure (I2–I7) - #12
Merged
Merged
Conversation
Basic ACR with no admin user and no anonymous pull. A user-assigned identity holds AcrPull on it, created before the Container App so the app's first image pull already has the grant (a system-assigned identity would only exist after that pull). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8A4vwVykbRMCMnpzV6KzL
Adds a Consumption Container Apps environment with Log Analytics, and the Container App itself: external HTTPS ingress on 8080, /healthz probes, scale to zero by default, image pulled from ACR with the user-assigned identity. The identity gets the Cosmos DB Built-in Data Contributor data-plane role through sqlRoleAssignments (key auth stays disabled), and the app finds it through AZURE_CLIENT_ID. The app gets AUTH_MODE (default entra), ENTRA_TENANT_ID (this tenant by default) and ENTRA_AUDIENCE, without which it refuses to start; key mode stores METRICS_API_KEY as a Container Apps secret. The app is only deployed when imageTag is set, so the first deploy creates the registry and environment, and reports the app's future URL for the Entra redirect URI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8A4vwVykbRMCMnpzV6KzL
Bicep cannot create app registrations (D8), so an az CLI script does: - <prefix>-metrics-api with the Events.Write application role (service principals only) and an application ID URI, api://<prefix>-metrics by default, falling back to api://<client id> where the tenant requires it. - <prefix>-metrics-dashboard, the Container Apps sign-in app, with its redirect URI from the first deployment's metricsUrl, assignment required, and the signed-in user (or --dashboard-group) assigned. It is safe to rerun: registrations are found by name, and the dashboard client secret is only added on creation or with --rotate-secret. Results go to .env.entra (mode 600, git-ignored) as export lines for main.bicepparam. The Bicep CI job now also runs shellcheck over infra/*.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8A4vwVykbRMCMnpzV6KzL
The Container App gets an authConfigs/current with the Entra provider (the dashboard app registration from setup-entra.sh, issuer pinned to the tenant): unauthenticated requests are sent to sign-in, except excludedPaths /events (which checks its own credential, AUTH_MODE) and /healthz. Who may sign in is set in Entra: the dashboard app requires assignment, so only assigned users or groups get through. The client secret is a Container Apps secret. main.bicepparam reads the image tag, Entra IDs and that secret from the environment (the .env.entra file setup-entra.sh writes), so none of them land in a file in the repo. The app module requires both dashboard values, so the app cannot be deployed without sign-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8A4vwVykbRMCMnpzV6KzL
Assigns the API's Events.Write app role to the service principal behind an Azure DevOps service connection (or any sender), given its client id or object id. It finds the API from .env.entra or by name, checks the role exists, and skips the grant if it is already there. The header documents the equivalent single az rest call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8A4vwVykbRMCMnpzV6KzL
infra/README.md walks through the two-pass deploy: base resources and the app's URL, Entra registration, image build with az acr build, the app, the pipeline's Events.Write grant and its variable group, and a check that sign-in and the 401 on POST /events work. It also lists outputs, options and teardown, including the app registrations. The root README now links it instead of the in-progress note, and the CHANGELOG records the new infrastructure and scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8A4vwVykbRMCMnpzV6KzL
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.
Requested by Ziad · project thread
Completes Phase 6 of the plan in one PR, one commit per item.
What's in it
AcrPull/healthzprobes, scale-to-zero; Cosmos Built-in Data Contributor viasqlRoleAssignments;AUTH_MODE,ENTRA_TENANT_ID,ENTRA_AUDIENCEset (key mode storesMETRICS_API_KEYas a secret)infra/setup-entra.shEvents.Writeapplication role and an app ID URI; dashboard app reg with redirect URI, assignment required, signed-in user or--dashboard-groupassigned. Idempotent; writes.env.entra(mode 600, git-ignored). CI's Bicep job now also runs shellcheck oninfra/*.sh(job name unchanged, so required checks are unaffected)authConfigs/currentwith the Entra provider;excludedPaths: ['/events', '/healthz'].main.bicepparamreads the tag, IDs and secret from env vars, so nothing secret lands in the repoinfra/grant-events-write.shEvents.Writeto the pipeline's service principal; header documents the singleaz restequivalentinfra/README.mdOne deviation from the plan
I3 says system-assigned identity. This uses a user-assigned identity instead: a system-assigned identity only exists after the app is created, so its
AcrPullgrant would arrive after the first image pull and the first deploy would fail. The app finds the identity throughAZURE_CLIENT_ID, which M12'sDefaultAzureCredentialalready supports.Deploy flow
imageTag: Cosmos, ACR, identity, environment; outputs the app's futuremetricsUrl.setup-entra.sh --app-url <metricsUrl>.az acr buildthe image.source .env.entraand deploy again withPULLPICKET_IMAGE_TAGset.grant-events-write.sh --principal <service connection client id>.Testing
infra/, zero findings.azcovering first run, rerun (no duplicate secret or assignment), tenant refusingapi://<name>(falls back toapi://<client id>),--dashboard-group, an already-granted principal and an unknown principal.Generated by Claude Code