azd up provisions everything in infra/, deploys the app, seeds the policy documents,
and drops three sample requests on the queue so you see decisions immediately.
- An Azure subscription with permission to create Functions, Storage, and Microsoft Foundry resources.
- Azure Developer CLI (
azd). - uv: the
prepackagehook runsuv exportto generaterequirements.txt, and the deployment hooks and helper scripts run withuv run.
azd upazd up prompts for an environment name, subscription, and region on first run, then:
- Provisions the resources below.
- Seeds the bundled policy documents into the
policiesblob container (post-provision hook). - Packages the app: the
prepackagehook regeneratessrc/requirements.txtfromsrc/pyproject.toml+src/uv.lockviauv export, so the Functions remote build has one. - Deploys the function app.
- Sends the three
$450sample requests to the input queue (post-up hook) so decisions land on the output queues within a minute.
Read the decisions:
uv run scripts/read_decision.py --queue all --peek --cloudThe default view groups decisions by outcome and shows the selected policy and reason. Add --raw
to print the original queue JSON.
-
Function App: Flex Consumption, Python 3.13, running the agent.
-
Microsoft Foundry account + project + a
gpt-5.4model deployment. -
Storage account: the
expense-requestsinput queue, theexpense-approved/expense-review/expense-flaggedoutput queues, and apoliciesblob container that holds the approval policy documents. Shared-key access is disabled. -
User-assigned managed identity + RBAC:
Identity Role Why Function app MI Storage Queue Data Contributor trigger reads the input queue; route_expense_decisionwrites the output queuesFunction app MI Storage Blob Data the policy tools list and read the policy blobs Function app MI Cognitive Services OpenAI User the agent calls the Foundry model Deploying user Storage Queue Data Contributor + Storage Blob Data Contributor so the demo scripts and hooks can send requests, read decisions, and change policies out of the box
Key values are printed as azd outputs and saved to .azure/<env>/.env (for example
OUTPUT_STORAGE_ACCOUNT, AZURE_FUNCTION_NAME, and INPUT_QUEUE_NAME).
Two hooks in azure.yaml call
scripts/setup_demo.py so a fresh deploy works without a manual setup
step. The helper uses DefaultAzureCredential, including the identity established by azd, and both
hooks are best-effort (continueOnError) because the runtime is a guaranteed fallback:
postprovision: seed policies. Uploadssrc/policies/*.mdinto thepoliciescontainer. It seeds only when the container is empty, so re-provisioning never overwrites a policy you've edited. (The agent also re-seeds lazily on its first run if the container is ever empty.)postup: demo send. Drops the three$450samples (travel, equipment, client dinner) on the input queue so you see the multi-policy behavior immediately. It sends only when the output queues are empty, so re-runningazd upwon't pile on duplicates.
The helper scripts talk to the deployed account over Entra ID (no keys). azd already granted
your identity the roles above, so you can send, read, and change policies right away. --cloud
auto-resolves the account from your azd env:
uv run scripts/send_expense.py --file samples/travel.txt --cloud
uv run scripts/read_decision.py --queue all --peek --cloudazd down --purgeChange a policy without redeploying: customize.md. Hitting an error: troubleshooting.md.