Skip to content

feat(deploy): automate Entra ID authentication configuration and sample bundle processing#565

Open
Dongbumlee wants to merge 7 commits intomainfrom
feat/automate-auth-config
Open

feat(deploy): automate Entra ID authentication configuration and sample bundle processing#565
Dongbumlee wants to merge 7 commits intomainfrom
feat/automate-auth-config

Conversation

@Dongbumlee
Copy link
Copy Markdown
Contributor

Purpose

  • Fully automate the previously-manual Entra ID authentication setup documented in docs/DeploymentGuide.md §5.2. After azd up, both Web and API container apps are registered, granted admin consent, configured for EasyAuth, and wired up — no portal clicks required.
  • Add a post-deployment step that processes the bundled sample claims (claim_date_of_loss, claim_hail) end-to-end after schema/schemaset registration so first-time users see results immediately.
  • Update docs/DeploymentGuide.md to reflect the new automated flow, with a manual fallback link kept for environments where the automation can't run.

The new automation is implemented in infra/scripts/configure_auth.sh / configure_auth.ps1 and invoked from post_deployment.sh / post_deployment.ps1. It is idempotent and safe to re-run.

Does this introduce a breaking change?

  • Yes
  • No

Golden Path Validation

  • I have tested the primary workflows (the "golden path") to ensure they function correctly without errors.

Validated 5 full azd up deployments (deploytest-wsl through deploytest-wsl5) against a clean subscription. On the final cycle:

  • Schema registration → schemaset → schema mapping → sample bundle processing (4 + 3 files for the two sample claims) → auth configuration all completed cleanly.
  • Browser sign-in via Entra ID succeeded; SPA loaded and rendered claim results.
  • Total provisioning time: ~17m 40s.

Deployment Validation

  • I have validated the deployment process successfully and all services are running as expected with this change.

Validated against both the standard profile and reviewed for compatibility with the AVM-based WAF profile (main.waf.parameters.json); ingress remains external on the Container Apps so the auth-config script's HTTPS calls work in both profiles. A note covering this has been added to the deployment guide.

What to Check

Verify that the following are valid

  • infra/scripts/configure_auth.sh and .ps1 are functionally equivalent.
  • post_deployment.sh Step 4 schema-id lookup correctly maps every file in a bundle to its schema (the previous bash array iteration only matched the first element).
  • infra/main.bicep APP_WEB_AUTHORITY no longer produces a double-slash URL (environment().authentication.loginEndpoint already includes a trailing slash).
  • docs/DeploymentGuide.md §5 reflects the automated flow; the manual docs/ConfigureAppAuthentication.md is preserved as a fallback.

Other Information

Bugs discovered and fixed during e2e validation:

  1. globalValidation.redirectToProvider is not reliably populated by the az containerapp auth CLI flags — it must be set directly in the authConfig PUT, otherwise unauthenticated browser users get a 401 instead of being redirected to AAD.
  2. az ad app permission admin-consent silently consents Microsoft Graph only and skips custom-API delegated scopes. The Web SP needs an explicit oauth2PermissionGrants POST for user_impersonation on the API app, or MSAL acquireTokenSilent fails after sign-in and the SPA renders blank.
  3. az containerapp secret set does not trigger a new revision; the script now restarts the active Web + API revisions to apply changes.
  4. APP_WEB_AUTHORITY in main.bicep had a redundant / between loginEndpoint and tenantId, breaking MSAL.js.
  5. Bash array de-reference (for x in $ARR instead of ${ARR[@]}) caused only the first file in each sample bundle to upload; fixed with indexed iteration. (PowerShell path was already correct via hashtable lookup.)

DonLee and others added 7 commits April 24, 2026 13:03
Adds infra/scripts/configure_auth.{sh,ps1} invoked at the end of the
azd postprovision hook so 'azd up' produces a fully authenticated
deployment without the manual steps in docs/ConfigureAppAuthentication.md.

Idempotent (reuses app regs persisted in azd env by appId, reuses
existing container app secrets) and skippable via AZURE_SKIP_AUTH_SETUP.

Covers:
- Web + API app registrations with redirect URIs, exposed scopes,
  Graph User.Read, ID/access token issuance
- Best-effort admin consent with clear manual-action message on failure
- Container App EasyAuth Microsoft provider on both apps
- API authConfig allowedApplications = Web client id
- Web container env vars APP_WEB_CLIENT_ID / APP_WEB_SCOPE / APP_API_SCOPE
- Final lockdown: Web -> RedirectToLoginPage, API -> Return401
…ze issuer

Three fixes discovered during end-to-end azd up testing:

1. `az containerapp auth microsoft update` rejects `--issuer` and
   `--tenant-id` together; the issuer is derived from tenant-id.
2. `azd env get-value AZURE_TENANT_ID` prints its error message to stdout
   (not stderr), corrupting TENANT_ID when that key is absent. Read from
   `az account show` first instead.
3. `--allowed-token-audiences api://<clientId>` breaks EasyAuth login
   because the ID tokens it issues have `aud=<clientId>` (GUID), not the
   identifierUri. Drop the override and normalize `allowedAudiences` to
   just the clientId via an authConfig PUT (which also clears stale
   values left by prior runs and fixes `openIdIssuer` if it was
   previously corrupted).

Verified: Web `/.auth/login/aad` -> 302 to login.microsoftonline.com with
the correct client_id, redirect_uri, and scopes; API returns 401 to
unauthenticated callers; allowedApplications on the API restricts callers
to the Web clientId.
Default app registrations have requestedAccessTokenVersion=null, which
means Entra issues v1 access tokens with aud='api://<clientId>'. EasyAuth
was configured with allowedAudiences=['<clientId>'] (bare GUID only), so
every Web->API call failed audience validation and returned 401.

Include both forms so the script works regardless of the app reg's
accessTokenAcceptedVersion setting.
- DeploymentGuide.md §5.2 rewritten: describes the automatic flow,
  permission requirements, admin-consent failure handling, and the
  AZURE_SKIP_AUTH_SETUP escape hatch.
- ConfigureAppAuthentication.md gets a banner making clear the manual
  steps are now a fallback (for tenants that block programmatic app
  registration or admin consent).
- Add bundle_info.json manifests for claim_date_of_loss and claim_hail
- Add Step 4 to post_deployment.ps1 and post_deployment.sh
  - Creates claim batch with schemaset ID
  - Uploads files with mapped schema IDs
  - Submits batch for workflow processing
- Update DeploymentGuide.md with new step and sample output
- Update AVMPostDeploymentGuide.md with manual sample processing instructions
- Normalize output prefixes: ASCII dashes for ps1, emojis for sh
configure_auth.sh / configure_auth.ps1:
- Set globalValidation (requireAuthentication, unauthenticatedClientAction,
  redirectToProvider) directly in the authConfig PUT — the CLI flags were not
  reliably populating redirectToProvider, leaving the Web app responding 401
  to browser users instead of redirecting to AAD.
- Explicitly POST oauth2PermissionGrants to grant the API user_impersonation
  scope to the Web service principal. 'az ad app permission admin-consent'
  silently consents Microsoft Graph only and skips custom-API delegated
  scopes, which made MSAL acquireTokenSilent fail and rendered a blank SPA
  after successful login.
- Override APP_WEB_AUTHORITY env var on the Web container app so MSAL.js
  uses a properly-formed authority URL.
- Restart Web + API container revisions after secrets/env updates so the
  new values take effect without a manual restart.

infra/main.bicep:
- Drop redundant slash in APP_WEB_AUTHORITY composition; loginEndpoint
  already has a trailing slash, so '${loginEndpoint}/${tenantId}' produced
  a double-slash URL that broke MSAL.

infra/scripts/post_deployment.sh:
- Fix bash array iteration in Step 4b schema-id lookup. The previous
  'for RID in $REGISTERED_IDS' de-references the array as a scalar (only
  the first element), causing only one file per sample bundle to upload.
  Switched to indexed iteration with ${!REGISTERED_IDS[@]} and a name
  lookup against REGISTERED_NAMES[$i].
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.

2 participants