docs(CLM hack): Ch1 local clone + venv; Ch4 deploy fail-fast (provider-register + containerapp-up) - #431
Merged
Marco Weber (qxsch) merged 8 commits intoAug 21, 2026
Conversation
Switch the Contract Lifecycle Management hack's Challenge 1 from the Dev Container / GitHub Codespaces flow to a local clone + Python virtual environment flow, and drop the now-unused dev container. - challenge-01.md / solution-01.md: clone + open in VS Code, then `python -m venv .venv`, activate, `pip install -r src/requirements.txt`, and VS Code "Python: Select Interpreter -> .venv"; note the PEP 668 externally-managed-environment error a venv avoids on Python 3.12+. - Add the two endpoint screenshots (Foundry Project endpoint and Azure AI Search Url) with a "where to copy them from" section in solution-01. - Remove the hack-scoped .devcontainer/ and the two Codespace step images; sweep stray Codespaces/devcontainer mentions from README, src, docs, labautomation, deploy, and challenge-04. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
az provider register (Microsoft.App / Microsoft.OperationalInsights) is a SUBSCRIPTION-scope action; in a resource-group lab the user only owns the RG, so it fails with AuthorizationFailed. Under PowerShell 5.1 with $ErrorActionPreference=Stop that stderr becomes a terminating error and aborts the deploy even though the platform already registered the providers. - deploy.ps1 / deploy.sh: check registrationState and only register when needed; relax EAP so the best-effort call can never abort the deploy. - challenge-04.md / solution-04.md: document the AuthorizationFailed is harmless and now skipped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
az containerapp up --source cloud build crashes on Azure CLI core 2.86.0 with 'NoneType' object has no attribute 'linux' in queue_acr_build (Azure/azure-cli#33369, fixed in 2.87.0+). The PS deploy script did not stop on that failure, so it cascaded into misleading 'does not exist' / '--assignee-object-id: expected one argument' errors. - deploy.ps1 / deploy.sh: check the exit code of az containerapp up and stop with an actionable message (run az upgrade, need >= 2.87.0). - challenge-04.md / solution-04.md: troubleshooting rows for the bug. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
… absent deploy.ps1/.sh only looked for a repo-root .env, so participants who keep their .env in src/ (next to .env.example, where the agents run from) hit 'AZURE_AI_PROJECT_ENDPOINT is not set' unless they passed -EnvFile. - deploy.ps1 / deploy.sh: resolve env file as repo-root .env first, then src/.env; explicit -EnvFile / ENV_FILE still wins. src/.env is already excluded from the image build context by src/.dockerignore. - Clearer 'endpoint not set' message + troubleshooting rows in the docs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
A Container Apps managed environment left in a non-Succeeded state by an earlier crashed run makes every re-run of 'az containerapp up' fail with ManagedEnvironmentNotProvisioned (it reuses the '<app>-env' env by name), cascading into 'clm-mcp does not exist' and the empty-principalId role error. - deploy.ps1 / deploy.sh: before 'az containerapp up', delete the '<app>-env' env when its provisioningState is not Succeeded, so 'up' recreates it cleanly. Generalize the failure hint to cover both the CLI 2.86.0 bug and ManagedEnvironmentNotProvisioned, and guard the role step when principalId is empty. - challenge-04.md / solution-04.md: troubleshooting rows for the env provisioning failure. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
… Foundry User rename) The final deploy step failed with Role 'Azure AI User' doesn't exist. even though the app was already created. The built-in role "Azure AI User" was renamed to "Foundry User"; its display name no longer resolves via 'az ... --role "Azure AI User"', but the role-definition GUID (53ca6127-db72-4b80-b1b0-d745d6d5456d) is unchanged. - deploy.ps1 / deploy.sh: assign the role by GUID first, then fall back to 'Cognitive Services User' and 'Cognitive Services OpenAI User' by name; treat an existing assignment as success; don't let a failed try abort the run. - challenge-04.md / solution-04.md: troubleshooting row for the rename; 401/403 rows cite the GUID. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
Publishing the agent to Teams/M365 Copilot auto-creates an Azure Bot, which needs the Microsoft.BotService resource provider registered on the SUBSCRIPTION. The platform pre-registers Microsoft.App / Microsoft.OperationalInsights but not this one, so the 'Azure bot services' dropdown failed with MissingSubscriptionRegistration (409). In a resourcegroup lab the participant only holds RG-Owner and cannot register a subscription-scope provider themselves. - labautomation/deploy-lab.ps1: best-effort Register-AzResourceProvider for Microsoft.BotService (idempotent, subscription-wide, non-fatal). Param contract untouched. - challenge-05.md / solution-05.md: an RG-scoped participant can't self-register; a subscription Owner runs it once. Added a troubleshooting row for the 409. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
Add the platform's once-per-subscription hook (shared-deploy-lab.ps1) to register the Microsoft.BotService resource provider that Challenge 5's Teams / M365 publish needs. In a resourcegroup lab the participant is only subscription-Reader (+ RG-Owner) and cannot self-register it; the shared hook runs before the labs with subscription rights and registers it once per subscription, best-effort (never throws, so it can never block Challenges 1-4). Keeps deploymentType=resourcegroup so participants keep RG isolation (vs resourcegroup-with-subscriptionowner, which would make all 8 labs sharing a subscription Owners of it). Drops the redundant inline registration from deploy-lab.ps1 and updates Challenge 5 docs + the labautomation instructions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 47fea2b8-adc4-49b2-8c83-64d04bc5f28f
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
Fixes to the Agentic Contract Lifecycle Management hack (
03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management/), from running it with live participants.1 · Migrate Challenge 1 setup: Dev Container / Codespaces → local clone + venv
challenges/challenge-01.md,walkthrough/challenge-01/solution-01.md): clone the repo, open it in VS Code, thenpython -m venv .venv→ activate →pip install -r src/requirements.txt, and Python: Select Interpreter →.venv. Notes the PEP 668externally-managed-environmenterror a venv avoids on Python 3.12+.endpoint-foundry-project.pngandendpoint-azure-search.pngwith a "where to copy the two endpoints from" section in solution-01 (Foundry Project endpoint and Azure AI Search Url)..devcontainer/and the two Codespace step images (02-create-codespace.png,03-codespace-ready.png).README.md,src/,docs/coach-guide.md,labautomation/,deploy/mcp-server/, andchallenge-04.md— comment/label-only. Thelabautomation/deploy-lab.ps1platform parameter block is untouched.2 · Fix Challenge 4 deploy: provider-register AuthorizationFailed aborts the deploy
The MCP-server deploy scripts ran
az provider register --namespace Microsoft.App / Microsoft.OperationalInsights, a subscription-scope action. In a resource-group lab the participant only owns the RG, so it fails withAuthorizationFailed— and under PowerShell 5.1 with$ErrorActionPreference=Stop, az's stderr becomes a terminating error that aborts the whole deploy, even though the platform already registered those providers.deploy/mcp-server/deploy.ps1/deploy.sh: queryregistrationStateand only register when actually needed; relax error handling so the best-effort call can never abort the deploy.challenges/challenge-04.md/walkthrough/challenge-04/solution-04.md: document that theAuthorizationFailedon provider register is harmless and now skipped.3 · Fix Challenge 4 deploy: fail fast on the
az containerapp upcloud-build bugaz containerapp up --sourcecrashes on Azure CLI core 2.86.0 with'NoneType' object has no attribute 'linux'inqueue_acr_build— a known CLI regression (Azure/azure-cli#33369, fixed in 2.87.0+) where the cloud image-build path resolves the OS/Architecture SDK models from the wrong package. In the PowerShell script that failure did not stop execution, so it cascaded into misleadingcontainerapp 'clm-mcp' does not existand--assignee-object-id: expected one argumenterrors that hid the real cause.deploy/mcp-server/deploy.ps1/deploy.sh: capture the exit code ofaz containerapp upand stop with an actionable message (runaz upgrade, need>= 2.87.0) instead of cascading into the identity/role steps.challenges/challenge-04.md/walkthrough/challenge-04/solution-04.md: troubleshooting rows for the bug (fix isaz upgradeon the participant's machine; partial ACR/env are reused on re-run).4 · Fix Challenge 4 deploy: find
.envinsrc/too (not only repo root)The deploy scripts only looked for a repo-root
.env. Participants who copysrc/.env.examplein place keep their.envinsrc/(the CWD their agents run from), so a zero-arg./deploy.ps1threwAZURE_AI_PROJECT_ENDPOINT is not setunless they remembered-EnvFile.deploy/mcp-server/deploy.ps1/deploy.sh: resolve the env file as repo-root.envfirst, thensrc/.env; an explicit-EnvFile/ENV_FILEstill wins.src/.envis already excluded from the image build context bysrc/.dockerignore, so this is safe. Clearer "endpoint not set" message.challenges/challenge-04.md/walkthrough/challenge-04/solution-04.md: troubleshooting rows for the error.Scope is limited to files under the hack directory; no workflow or platform files are changed.
5 · Fix Challenge 4 deploy: auto-heal a stuck Container Apps environment
After the CLI upgrade (section 3) the ACR build succeeds, but the earlier crashed runs can leave the Container Apps managed environment (auto-named
<app>-env=clm-mcp-env) stuck in a non-Succeededstate.az containerapp upreuses that env by name, so every re-run then fails withManagedEnvironmentNotProvisioned— cascading again intoclm-mcp does not existand the empty-principalIdrole error.deploy/mcp-server/deploy.ps1/deploy.sh: beforeaz containerapp up, query the env'sprovisioningStateand delete it when it is notSucceededsouprecreates it cleanly. Generalized the post-upfailure hint to cover both the CLI 2.86.0 bug andManagedEnvironmentNotProvisioned, and guard the role step whenprincipalIdis empty.challenges/challenge-04.md/walkthrough/challenge-04/solution-04.md: troubleshooting row (deleteclm-mcp-envand re-run; if a fresh env also fails it is usually region capacity — trywesteurope).6 · Fix Challenge 4 deploy: assign the Foundry role by GUID (
Azure AI User→Foundry Userrename)The very last deploy step — granting the container's managed identity access to Foundry — failed with
Role 'Azure AI User' doesn't exist.even though the app had already been created and deployed. The built-in role "Azure AI User" was renamed to "Foundry User", soaz ... --role "Azure AI User"(by display name) no longer resolves — but the role-definition GUID53ca6127-db72-4b80-b1b0-d745d6d5456dis unchanged. (Confirmed:az role definition list --name "Azure AI User"returns empty;--name "Foundry User"returns that GUID.)deploy/mcp-server/deploy.ps1/deploy.sh: assign the role by GUID first, then fall back toCognitive Services User/Cognitive Services OpenAI Userby name; treat an already-existing assignment as success; never let a failed attempt abort the run. Thegrant it yourselfhint now uses the GUID.challenges/challenge-04.md/walkthrough/challenge-04/solution-04.md: troubleshooting row for the rename, and the401/403rows now cite the GUID.7 · Challenge 5: pre-register
Microsoft.BotServicefor the Teams publishPublishing the agent to Teams / M365 Copilot (Challenge 5) auto-creates an Azure Bot, which needs the
Microsoft.BotServiceresource provider registered on the subscription. The platform pre-registersMicrosoft.App/Microsoft.OperationalInsightsbut not this one, so the Azure bot services dropdown failed withMissingSubscriptionRegistration/ "The subscription is not registered to use namespace 'Microsoft.BotService'" (409). In aresourcegrouplab the participant only holds RG-Owner and cannot register a subscription-scope provider themselves.labautomation/deploy-lab.ps1: best-effortRegister-AzResourceProvider -ProviderNamespace Microsoft.BotService(idempotent, subscription-wide, non-fatal) so it's ready long before teams reach Challenge 5; a clear[WARN]with the manual command if the provisioning identity lacks subscription rights. The platform param contract is untouched.challenges/challenge-05.md/walkthrough/challenge-05/solution-05.md: corrected the guidance — an RG-scoped participant can't self-register; a subscription Owner runsaz provider register --namespace Microsoft.BotServiceonce (unblocks every lab in the subscription). Added a troubleshooting row for the 409.8. Challenge 5: register
Microsoft.BotServiceviashared-deploy-lab.ps1(self-service, no isolation downgrade)Symptom. Challenge 5 "Publish to Teams / M365 Copilot" auto-creates an Azure Bot. In the Foundry publish dialog the Azure bot services dropdown returned a 409
MissingSubscriptionRegistration— "The subscription is not registered to use namespaceMicrosoft.BotService." A participant then ranaz provider register --namespace Microsoft.BotServiceand gotAuthorizationFailed: in aresourcegrouplab the participant holds only subscription-Reader(+ RG-Owner), and provider registration is a subscription-scope write — so no lab account can self-register it.Root cause. The platform pre-registers
Microsoft.App/Microsoft.OperationalInsightsbut notMicrosoft.BotService, andresourcegroupparticipants have no path to register a provider themselves.Fix — the platform-sanctioned once-per-subscription hook. Added
labautomation/shared-deploy-lab.ps1, which the platform runs once per subscription, before the per-participantdeploy-lab.ps1fan-out — the README's named home for "registering resource providers." It registersMicrosoft.BotServiceonce per subscription, best-effort (try/catch +[WARN], never throws, so a registration hiccup can never block provisioning of Challenges 1-4).Why not switch
deploymentType?resourcegroup-with-subscriptionownerwould make every one of the up-to-labsPerSubscription(8) participants sharing a subscription an Owner of that subscription — able to see and delete each other's resource groups. The shared hook keepsdeploymentType: resourcegroup, so participants stay subscription-Reader+ RG-Ownerand lab isolation is preserved.Files.
labautomation/shared-deploy-lab.ps1— new once-per-subscription hook (exact platform param contract) registeringMicrosoft.BotService.labautomation/deploy-lab.ps1— removed the redundant inline registration (inresourcegroupit ran as sub-Readerand would only ever[WARN], and per-lab jobs would race); points to the shared hook.labautomation/lab-defaults.json— addedestimatedSharedDeploymentDailyCostsUsd: 0.0(registration is free) to document the shared hook.challenges/challenge-05.md/walkthrough/challenge-05/solution-05.md—Microsoft.BotServiceis now pre-registered once per subscription; the manualaz provider registeris documented only as a subscription-Owner fallback (an RG-scoped participant can't run it)..github/instructions/labautomation.instructions.md— documented the shared-hook parameter contract and why provider registration belongs there, not indeploy-lab.ps1.