Output from azd version
azd version 1.27.0 (commit dcd7c53153bada76c3c9462b57fab53d4f8b69a0) (stable)
Describe the bug
When a service uses the experimental azure.ai.agent host, azd rewrites the agent service block in azure.yaml on deploy/provision and strips any fields it does not model — most notably the service-level hooks block (and ${ENV_VAR} templating in image).
This means a per-service predeploy / postdeploy hook configured on an azure.ai.agent service:
- runs once for the deploy where it was just added, then
- disappears from
azure.yaml after the agent project handler writes the normalized block back, so it never runs again on subsequent deploys.
Service-level hooks are valid per the base azd schema, so this is surprising and silent (no warning, no error) — the block just vanishes.
Repro steps
azure.yaml:
name: my-app
services:
agent:
project: src/my-agent
host: azure.ai.agent
language: python
image: myregistry.azurecr.io/my-agent:${MY_TAG} # templating also gets flattened
kind: hosted
name: my-chat-agent
protocols:
- protocol: responses
version: 2.0.0
startupCommand: python init.py
hooks:
predeploy:
shell: sh
run: ./hooks/agent-predeploy.sh
interactive: true
postdeploy:
shell: sh
run: ./hooks/agent-postdeploy.sh
interactive: true
azd deploy agent — the predeploy/postdeploy hooks execute for this run.
- Inspect
azure.yaml afterward (or git diff): the entire hooks: block under services.agent is gone, and image: has been rewritten to a literal value (the ${MY_TAG} template is flattened).
azd deploy agent again — the hooks no longer run, because they were removed from azure.yaml.
The same stripping is observable on preprovision/provision; a log line we captured:
invokeProjectHandler error for event preprovision: failed to update environment
for service "agent": agent.yaml is not valid: validation failed:
- template.kind must be one of: [hosted workflow], got ''
which shows the agent project handler is (re)writing the composed agent definition.
Expected behavior
azd should preserve user-authored fields on an azure.ai.agent service that it doesn't own — at minimum the service-level hooks block, which is a first-class, schema-valid service field. Round-tripping the agent block through the extension's typed model should not silently drop hooks (or flatten ${ENV_VAR} templating in image).
At the very least, azd should emit a warning when it drops user-authored fields during the agent block rewrite, rather than doing it silently.
Root-cause analysis (what we found)
hooks is schema-valid on the agent service:
- Base schema
schemas/v1.0/azure.yaml.json: every service supports hooks (predeploy, postdeploy, …). The azure.ai.agent host conditional only disables k8s, apiVersion, and network — not hooks.
- Extension schema
cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json: top-level "additionalProperties": true, so extra keys like hooks pass validation.
So this is not a validation rejection. It appears to be the agent project handler's write-back: it deserializes the agent block into the extension's typed model (whose known fields are exactly those in azure.ai.agent.json — which does not include hooks), then re-serializes only the modeled fields back into azure.yaml. additionalProperties: true relaxes validation but the struct-based round-trip doesn't preserve unmodeled keys, so hooks is dropped.
Impact
Any workflow that relies on per-service agent hooks (e.g. granting the hosted agent's runtime managed identity its RBAC in postdeploy, or building/pushing the container image in predeploy) breaks after the first deploy, with no indication why. It's easy to misdiagnose as a formatter or an accidental revert.
Workaround
Move the agent-specific logic out of the (strippable) per-service hooks block and into the top-level command hooks (hooks.predeploy / hooks.postdeploy at the root of azure.yaml), which azd does not rewrite. Because command hooks have no native "which service" signal, gate expensive steps behind an env var (e.g. only build the agent image when the deploy targets the agent).
Environment
- azd 1.27.0 (stable), macOS
- Host:
azure.ai.agent (hosted Foundry container agent), azure.ai.agents extension
Output from
azd versionazd version 1.27.0 (commit dcd7c53153bada76c3c9462b57fab53d4f8b69a0) (stable)Describe the bug
When a service uses the experimental
azure.ai.agenthost,azdrewrites the agent service block inazure.yamlon deploy/provision and strips any fields it does not model — most notably the service-levelhooksblock (and${ENV_VAR}templating inimage).This means a per-service
predeploy/postdeployhook configured on anazure.ai.agentservice:azure.yamlafter the agent project handler writes the normalized block back, so it never runs again on subsequent deploys.Service-level
hooksare valid per the base azd schema, so this is surprising and silent (no warning, no error) — the block just vanishes.Repro steps
azure.yaml:azd deploy agent— thepredeploy/postdeployhooks execute for this run.azure.yamlafterward (orgit diff): the entirehooks:block underservices.agentis gone, andimage:has been rewritten to a literal value (the${MY_TAG}template is flattened).azd deploy agentagain — the hooks no longer run, because they were removed fromazure.yaml.The same stripping is observable on
preprovision/provision; a log line we captured:which shows the agent project handler is (re)writing the composed agent definition.
Expected behavior
azdshould preserve user-authored fields on anazure.ai.agentservice that it doesn't own — at minimum the service-levelhooksblock, which is a first-class, schema-valid service field. Round-tripping the agent block through the extension's typed model should not silently drophooks(or flatten${ENV_VAR}templating inimage).At the very least,
azdshould emit a warning when it drops user-authored fields during the agent block rewrite, rather than doing it silently.Root-cause analysis (what we found)
hooksis schema-valid on the agent service:schemas/v1.0/azure.yaml.json: every service supportshooks(predeploy,postdeploy, …). Theazure.ai.agenthost conditional only disablesk8s,apiVersion, andnetwork— nothooks.cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json: top-level"additionalProperties": true, so extra keys likehookspass validation.So this is not a validation rejection. It appears to be the agent project handler's write-back: it deserializes the agent block into the extension's typed model (whose known fields are exactly those in
azure.ai.agent.json— which does not includehooks), then re-serializes only the modeled fields back intoazure.yaml.additionalProperties: truerelaxes validation but the struct-based round-trip doesn't preserve unmodeled keys, sohooksis dropped.Impact
Any workflow that relies on per-service agent hooks (e.g. granting the hosted agent's runtime managed identity its RBAC in
postdeploy, or building/pushing the container image inpredeploy) breaks after the first deploy, with no indication why. It's easy to misdiagnose as a formatter or an accidental revert.Workaround
Move the agent-specific logic out of the (strippable) per-service
hooksblock and into the top-level command hooks (hooks.predeploy/hooks.postdeployat the root ofazure.yaml), whichazddoes not rewrite. Because command hooks have no native "which service" signal, gate expensive steps behind an env var (e.g. only build the agent image when the deploy targets the agent).Environment
azure.ai.agent(hosted Foundry container agent),azure.ai.agentsextension