feat(agents): support Foundry ${{...}} server-side expressions in env expansion#8602
Conversation
Add a shared ExpandEnv helper in the azure.ai.agents extension that expands
azd ${VAR} references while leaving Foundry server-side ${{...}} expressions
untouched, and route the existing env-expansion call sites through it.
drone/envsubst cannot parse ${{...}} and returns the whole string unexpanded,
so any ${VAR} appearing alongside a ${{...}} span was silently dropped.
ExpandEnv splits on ${{...}} spans, expands only the gaps, and reattaches the
spans verbatim. This is the single shared expander Foundry fields will use as
part of unifying Foundry config in azure.yaml (design spec section 2.5).
Refactors resolveEnvironmentVariables (service_target_agent.go), resolveEnvValue
(listen.go), and resolveAgentDefinitionEnvVars (run.go) to use it.
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared templating helper in the azure.ai.agents extension to expand client-side ${VAR} references while preserving Foundry server-side ${{...}} expressions verbatim, and wires existing call sites through it to fix mixed-template expansion failures.
Changes:
- Add
project.ExpandEnvhelper to expand${VAR}while keeping${{...}}spans untouched. - Add unit tests covering mixed
${VAR}+${{...}}scenarios and invariants. - Route existing envsubst expansion call sites through
project.ExpandEnvfor consistent behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/extensions/azure.ai.agents/internal/project/templating.go | Adds the shared ${VAR} expander that preserves Foundry ${{...}} spans. |
| cli/azd/extensions/azure.ai.agents/internal/project/templating_test.go | Adds tests validating preservation and mixed expansion behaviors. |
| cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go | Switches environment variable resolution to use ExpandEnv. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/run.go | Switches agent definition env var resolution to use project.ExpandEnv. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go | Switches toolbox/env resolution to use project.ExpandEnv and updates the function doc comment. |
- Document that a ${VAR} inside a ${{...}} span is preserved verbatim, and
add a test asserting it.
- Drop the now-redundant error fallback in resolveAgentDefinitionEnvVars;
ExpandEnv already returns the original value on error.
Switch ExpandEnv from splitting on ${{...}} spans to masking each span with
a sentinel placeholder, running a single envsubst pass, then restoring the
spans. The split approach failed when a ${{...}} expression was the default
of a ${VAR:-default} construct (e.g. ${MISSING:-${{event.body}}}), leaving an
unterminated "${MISSING:-" fragment that envsubst could not parse, so the
whole value was returned unexpanded. The sentinel contains no $, { or }, so
envsubst leaves it untouched even after a literal $. Adds tests for the
default-value cases and a leading-$ case.
Addresses review comment on internal/project/templating.go.
|
Minor (non-blocking)
Neither point blocks merging. The behavior-change table in the PR description accurately reflects the code (the previously-broken mixed and ${VAR:-default} cases now expand correctly), and the change stays correctly extension-only with no core/schema/gRPC impact. |
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
|
/check-enforcer override |
Summary
First incremental PR toward unifying Foundry config in
azure.yaml(design spec #8590). It introduces the shared env expander the unified Foundry experience needs: a single helper that resolves azd${VAR}references while preserving Foundry's server-side${{...}}expressions verbatim. This is the building block called out in design spec §2.5 ("Templating:${VAR}and${{...}}").Framed as a feature because it adds support for a new templating syntax (
${{...}}) the extension did not handle before. It also corrects a latent bug where a value mixing both syntaxes was returned unexpanded.What it adds
project.ExpandEnv(value, mapping)in theazure.ai.agentsextension.${VAR}(including${VAR:-default}and multiple expressions) viadrone/envsubst.${{...}}span verbatim for Foundry's server-side resolver.${{...}}span with a sentinel placeholder (no$/{/}, whichenvsubstnever rewrites since it only expands the braced${...}form), runs a singleenvsubst.Eval, then restores the spans. Masking instead of splitting preserves full${VAR:-default}semantics even when the default value is itself a${{...}}expression (for example${MISSING:-${{event.body}}}).Behavior change
${VAR}${{x}}prefix ${VAR} ${{x}}${VAR}silently dropped)${VAR}resolved,${{x}}preserved${MISSING:-${{x}}}${{x}}Affected areas
Extension-only (
cli/azd/extensions/azure.ai.agents). The three existing${VAR}expansion call sites now route throughExpandEnv:internal/project/service_target_agent.go->resolveEnvironmentVariables: agent env-var resolution duringazd ai agentdeploy (service target).internal/cmd/listen.go->resolveEnvValue(+ the recursive map/slice walk): env reference resolution in the provision/deploy lifecycle hooks (connection credentials, toolbox env, and similar).internal/cmd/run.go->resolveAgentDefinitionEnvVars: env-var resolution forazd ai agent run(local dev).Not affected:
pkg/osutil/expandable_string.gois intentionally untouched (design spec §2.5 places this in the extension, not core).host: microsoft.foundryschema is the separate PR feat(agents): add microsoft.foundry azure.yaml schema #8603.Blast radius is low: pure
${VAR}values behave exactly as before; the only change is that previously-broken mixed and${VAR:-default}cases now expand correctly.Tests
go build,go vet,gofmt,golangci-lint(0 issues),cspell, and the full extension test suite pass.templating_test.gocovers plain/defaulted/missing vars, preserved${{...}}, mixed, adjacent spans, a var nested inside a span, the${MISSING:-${{event.body}}}default case, a leading-$case, multiline spans, and malformed input.Fixes #8609