Anatomy of an Agent Lifecycle Repo — Bicep, Configs, and the Deploy Script
2026-07-034 min readAzure, AI, DevOps, CI/CD
Chapter 1 made the argument: agents are configuration, so treat them like infrastructure. This chapter opens the hood on Eric Chansen's foundry-agents-lifecycle repo (all credit to that project for these patterns) and walks the four building blocks every agent-lifecycle repo needs — whatever you end up naming the folders in yours.
The layout, at a glance
Trimmed to the load-bearing parts:
foundry-agents-lifecycle/
├── infra/ # Bicep: Foundry account, project, models, Key Vault
│ ├── main.bicep
│ ├── modules/
│ └── environments/ # dev/test/prod parameter files
├── src/
│ ├── agent/
│ │ ├── agent_definition.py # config + prompt + tools → SDK parameters
│ │ ├── prompts/
│ │ │ ├── system_prompt.md # THE prompt, as reviewable markdown
│ │ │ └── system_prompt.prod.md# optional prod override
│ │ └── tools/ # function tools as plain Python
│ ├── scripts/
│ │ ├── deploy_agent.py # create/update the agent via SDK
│ │ ├── run_evaluation.py # the quality gate (chapter 3)
│ │ └── teardown_agent.py
│ └── tests/unit/ # validate configs before touching Azure
├── config/
│ ├── agent-config.dev.json
│ ├── agent-config.test.json
│ └── agent-config.prod.json
├── .github/workflows/ # ci.yml + cd.yml (chapter 3)
└── .azdo/pipelines/ # the same, ADO-flavored (chapter 4)Notice what this structure encodes: one agent definition, one prompt (with an optional prod override), N environment configs. The environments differ in parameters, never in logic. That's the drift-killer.
Block 1 — Infrastructure as Bicep
Before an agent can exist, its house must: a Foundry account, a project, model deployments, and a Key Vault. The repo provisions all of it from infra/ with per-environment parameter files, so "stand up a complete test environment" is one command:
az deployment sub create \
--location eastus2 \
--template-file infra/deploy-infra.bicep \
--parameters environment=dev pipelineSource=github(Or azd up if you're an Azure Developer CLI person — the repo supports both.)
The part I want you to steal even if you steal nothing else: model deployments are IaC too (modules/model-deployments.bicep). Dev gets a mini model at low capacity; prod gets the production model at 5× the throughput — declared in parameters, not clicked in a portal. When a new model generation lands, changing prod's model is a parameter-file PR: reviewed, evaluated (chapter 3), and rollback-able.
One honest caveat from the README worth repeating: the subscription-level deployment creates resource groups, which needs Contributor at subscription scope. In locked-down enterprises you'll likely get a pre-created resource group instead — the repo ships a resource-group-level entry point (main.bicep) for exactly that situation.
Block 2 — Per-environment configs
The heart of the promotion story is three small JSON files:
// config/agent-config.dev.json (abridged)
{
"agent_name": "demo-agent-dev",
"model": "gpt-4o-mini",
"prompt_file": "system_prompt.md"
}Prod's file points at the bigger model and, optionally, a different prompt file. The differences across the repo's three environments, in one table:
| | DEV | TEST | PROD | |---|---|---|---| | Model | mini | production | production | | Capacity | 10K TPM | 20K TPM | 50K TPM | | Prompt | standard | standard | prod override | | Eval threshold | 3.0 | 3.5 | 4.0 | | Approval | none | 1 approver | 2+ approvers |
Everything a stakeholder ever asks about environment differences is answered by files in git. No tribal knowledge, no portal archaeology.
Block 3 — Prompts as markdown files
system_prompt.md being a standalone file, not a string constant buried in Python, is a quietly excellent decision. Prompt PRs get proper markdown diffs that a non-developer domain expert can review. Repo-wide search finds the prompt. And the optional system_prompt.prod.md override handles the real-world case where prod needs stricter language (compliance lines, escalation rules) without forking the whole agent.
Treat your system prompt like source code, because — chapter 1's lesson — it is the source code.
Block 4 — The deploy script
src/scripts/deploy_agent.py is where configuration becomes a live agent. Its logic is deliberately boring:
- Load
config/agent-config.{env}.json. - Read the prompt file it references.
- Resolve tool definitions from
src/agent/tools/. - Build a
PromptAgentDefinition(theazure-ai-projects2.x SDK — same one from my Build Your First AI App series). - Connect to the target environment's Foundry project and call
agents.create_version(...).
That last call carries the whole deployment philosophy: create_version is idempotent-by-versioning. New agent? Created. Existing agent? A new numbered version. Every pipeline run leaves an inspectable trail in Foundry, and "roll back" means redeploying a previous git commit — versions line up on both sides.
agent = project.agents.create_version(
agent_name=config["agent_name"],
definition=PromptAgentDefinition(
model=config["model"],
instructions=prompt_text,
tools=tools,
),
)The script also supports --dry-run — build everything, call nothing. File that away; it becomes the cheapest, fastest check in the CI pipeline next chapter.
Auth, by the way, is DefaultAzureCredential everywhere: your az login locally, workload identity in pipelines. No keys in the repo, no keys in the pipeline. Secrets that must exist (there are few) live in the Bicep-provisioned Key Vault.
Unit tests for agents?
Yes — and they're not testing the LLM. src/tests/unit/ validates the machinery: configs parse and contain required fields, referenced prompt files exist, agent names satisfy Foundry's rules, tool schemas are well-formed. Five-second tests that catch the classic "renamed the prompt file, forgot the config" break before it wastes a deployment. LLM behavior is tested by evaluations, which get the spotlight next chapter.
Wrapping up
Four blocks: Bicep for the house, JSON configs for per-environment identity, markdown prompts for reviewability, and a thin SDK script that applies desired state with create_version. Nothing exotic — which is exactly why it works.
Next — Chapter 3: the pipelines themselves. GitHub Actions CI that blocks bad PRs, OIDC federation with zero stored credentials, and the evaluation gates that decide whether an agent is allowed to reach production.