Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate deployment-ready infrastructure code from an architecture plan, verify it with adversarial self-review, and bridge to validation — all without deploying.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 152% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 148% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 225% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 97% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 197% | 0% |
Generate deployment-ready infrastructure code from an architecture plan, verify it with adversarial self-review, and bridge to validation — all without deploying.
| Property | Value | |----------|-------| | Parent | azure-app-onboard | | Best for | Turning prepare-plan.json service list into Bicep templates with secure-by-default patterns | | Inputs | prepare-plan.json (services, naming, quotas), context.json (overrides, components, repo info) | | Outputs | scaffold-manifest.json, generated IaC files in infra/ | | Pipeline position | Phase 3 of 4: prereq → prepare → scaffold → deploy | | IaC format | Bicep (v1 default). Terraform when existing .tf detected or user override. |
Invoked by the azure-app-onboard orchestrator at Phase 3 when prepare-plan.json exists with services[]. Not directly user-routable in v1.
> Return to orchestrator: When complete, return control to azure-app-onboard. Do NOT directly invoke deploy — the orchestrator manages phase transitions.
| Scenario | Use Instead | |----------|-------------| | User-triggered IaC (no prepare-plan.json) | azure-prepare | | Subscription-scope landing zones | azure-enterprise-infra-planner | | Execute deployment (azd up) | azure-deploy (do NOT invoke from AppOnboard pipeline) |
> See shared tools for cross-phase tools and global parameters. See scaffold tools for full parameter tables.
| Tool | Sub-command | Purpose | Parameters | |------|-----------|---------|------------| | mcp_azure_mcp_bicepschema | bicepschema_get | ARM resource type schemas | resource_type (Required), api_version (Optional) | | mcp_bicep_list_avm_metadata | (flat) | AVM module catalog | None | | mcp_bicep_get_bicep_best_practices | (flat) | Bicep best practices | None | | mcp_bicep_get_az_resource_type_schema | (flat) | ARM resource type JSON schema | azResourceType, apiVersion (Required) | | mcp_bicep_build_bicep | (flat) | Validate .bicep files (self-review L3) | filePath (Required) | | mcp_bicep_format_bicep_file | (flat) | Format .bicep files (LF enforcement) | filePath (Required) | | mcp_azure_mcp_deploy | deploy_iac_rules_get | IaC best practices and rules | deployment-tool, iac-type, resource-types | | mcp_azure_mcp_deploy | deploy_pipeline_guidance_get | CI/CD pipeline config | is-azd-project, pipeline-platform, deploy-option | | mcp_azure_mcp_get_azure_bestpractices | get_azure_bestpractices_get | SDK/Functions best practices | resource, action | | mcp_azure_mcp_azureterraformbestpractices | (flat) | Terraform patterns (TF path only) | resource_type (Required) |
Session folder: .copilot-azure/sessions/{uuid}/ — reads prepare-plan.json + context.json, writes scaffold-manifest.json.
prepare-plan.json — verify services[] exists, read naming config (especially naming.resourcePrefix, naming.suffix, naming.resources[]). Read resource group name from context.json.azure.resourceGroup. ⛔ Use EXACTLY these names in generated IaC — do NOT invent names, derive them from environmentName, or append your own suffixes. ⛔ Use EXACTLY the names from prepare-plan.json.naming.resources[] as Bicep parameters. Do NOT derive names with take(), substring(), or string manipulation. The plan is the source of truth. Missing → trigger prepare backfill via azure-app-onboard orchestrator.context.json — check overrides[] for iacFormat preference, detectedInfra[] for existing .tf, detectedInfraProvider for cloud provider classification.context.json.overrides[] contains ignoreExistingInfra: true. Otherwise:.bicep, azure.yaml, .tf with azurerm): ask_user → "Start fresh" (rename infra/ to infra.bak/) or "Use existing" (route to azure-prepare, stop pipeline)..tf with GCP/AWS): respect context.json.overrides[].iacFormat from prepare. Default: Bicep alongside existing TF.detectedInfraProvider.terraform == "unknown"): ask user which provider before routing.4b. Pre-check API versions (main thread) — MCP tool access is unreliable in task agents — call these in the main thread before dispatching. Call mcp_bicep_list_az_resource_types_for_provider (or bicep-list_az_resource_types_for_provider) once per provider namespace in prepare-plan.json.services[] (e.g., Microsoft.Web, Microsoft.App, Microsoft.DBforPostgreSQL, Microsoft.Cache, Microsoft.KeyVault, Microsoft.ContainerRegistry). Extract the latest GA API version (no -preview) for each resource type. Build an apiVersions map and pass it to the IaC gen sub-agent at Step 5. Fallback: if MCP unavailable, run az provider show --namespace {ns} --query "resourceTypes[?resourceType=='{type}'].apiVersions[?!contains(@, 'preview')] | [0][0]" -o tsv per resource type — this filters to GA-only and picks the latest. Pass "MCP unavailable" only if both MCP AND CLI fail. Sub-agent still validates generated Bicep via az bicep build.
> ⛔ File boundary: NEVER modify files outside infra/, .copilot-azure/. Scaffold only writes files — no install/build commands.
> ⛔ Sub-agent delegation is MANDATORY for Steps 5, 6–9, and 10–12. Each step reads its subagent-*.md template, then dispatches a task call. Do NOT read any reference file not explicitly named in these steps. > > ⛔ Dispatch type: task ONLY — NEVER general-purpose. general-purpose leaks sub-agent context into the main thread, accelerating compaction and evicting the orchestrator workflow. task isolates sub-agent context. > > ⛔ How to dispatch — VERBATIM COPY required: > 1. view the subagent-*.md template file > 2. Your NEXT action MUST be a task tool call — not view, powershell, create, or ANY other tool > 3. The task prompt MUST contain the COMPLETE and UNMODIFIED template text. Copy the template between <<<TEMPLATE_START>>> / <<<TEMPLATE_END>>> delimiters exactly as shown below. Do NOT summarize, paraphrase, reword, or omit ANY part of it — the sub-agent needs every "Read file]" and "Do:" instruction to produce correct output > 4. AFTER the template block, append the data sections (plan JSON, overrides, etc.) > > Anti-pattern (causes regressions): Writing your OWN prompt that lists workflow steps or describes what to generate. The template already contains the complete workflow — your job is to COPY it, not rewrite it.
subagent-iac-gen.md as a task. ⛔ agent_type: "task" — NEVER "general-purpose". <<<TEMPLATE_START>>> {paste the ENTIRE content of subagent-iac-gen.md here — unmodified} <<<TEMPLATE_END>>>
## Data (appended by orchestrator) ### prepare-plan.json {full JSON} ### context.json.overrides {overrides array} ### prereq-output.json.buildRequirements {buildRequirements object} ### prereq-output.json.warnings] {warnings array} ### Compute targets {App Service/Functions, Container Apps, or both + whether PostgreSQL/Redis present} ### apiVersions {map from Step 4b, e.g. {"Microsoft.KeyVault/vaults": "2023-07-01", ...} — or "MCP unavailable" if skipped} ### Working directory {absolute path}
infra/, file list returned for scaffold-manifest.json.files[]app-onboard-skill: 'true' MUST appear verbatim in generated Bicep.5b. Deploy checklist (parallel with Step 5) — Dispatch as a task in parallel with the IaC gen subagent above. ⛔ agent_type: "task" — NEVER "general-purpose". <<<TEMPLATE_START>>> You are a deploy-checklist generator. Do NOT invoke any skills.
create tool. This file survives conversation compaction — deploy re-reads it after every long-running command.<<<TEMPLATE_END>>>
## Data (appended by orchestrator) ### prepare-plan.json {full JSON} ### Session path {.copilot-azure/sessions/{uuid}/} ### Compute targets {App Service, Container Apps, Static Web Apps, or combination}
deploy-checklist.md written to session folder. If this subagent fails, the validate subagent (Steps 10b–12.5) will catch the missing file.6–9. Self-review — ⛔ You MUST dispatch subagent-review.md as a task. ⛔ agent_type: "task" — NEVER "general-purpose". <<<TEMPLATE_START>>> {paste the ENTIRE content of subagent-review.md here — unmodified} <<<TEMPLATE_END>>>
## Data (appended by orchestrator) ### Generated IaC files {full content of every .bicep/.tf file} ### prepare-plan.json (services, naming, deploymentVariables) {relevant sections} ### prereq-output.json.warnings] {warnings array}
scaffold-manifest.json.selfReview10a. Format IaC (main thread) — For each .bicep file in infra/ (including modules/): call mcp_bicep_format_bicep_file (or bicep-format_bicep_file) with { filePath: "<absolute path>" }.This enforces LF line endings via the bicepconfig.json written during IaC generation. Fallback: skip if unavailable.
10a-conf. Conformance gate (main thread — MANDATORY for Bicep) — ⛔ Skip this entire step when the scaffold emitted Terraform (infra/main.bicep absent) — these checks are Bicep-only (Terraform is syntax-validated via terraform validate in the validate subagent). Otherwise run the conformance script from this skill's scripts/ dir; it deterministically catches ARM-rejected values az bicep build can't (invalid Bicep values, wrong DB version, reserved DB login, enablePurgeProtection): {scaffoldDir}/scripts/scaffold-conformance.ps1 -SessionPath ".copilot-azure/sessions/{uuid}" -InfraPath infra # pwsh (preferred) bash {scaffoldDir}/scripts/scaffold-conformance.sh ".copilot-azure/sessions/{uuid}" infra # bash (only if pwsh unavailable; needs jq for the plan-dependent checks) ⛔ Prefer the .ps1 when pwsh is available — it runs every check unconditionally. The .sh twin skips the plan-dependent checks (DB-VERSION-MATCH, SERVICES-COMPLETE, DB-NAME-PRESENT, WARN-FIXED) when jq is absent. ⛔ Any BLOCK failure → fix the IaC, re-run (max 3); never present the deploy gate with an open BLOCK. Run it here in the main thread — do NOT delegate to the validate subagent or hand-judge the result when a shell exists. Pass the JSON to the validate subagent for scaffold-manifest.json.conformance.
10b–12.5. Validation + manifest — ⛔ You MUST dispatch subagent-validate.md as a task. ⛔ agent_type: "task" — NEVER "general-purpose". <<<TEMPLATE_START>>> {paste the ENTIRE content of subagent-validate.md here — unmodified} <<<TEMPLATE_END>>>
## Data (appended by orchestrator) ### IaC file paths {list of generated files} ### Self-review findings (from Steps 6–9) {findings JSON} ### prepare-plan.json {full JSON} ### prereq-output.json.warnings] {warnings array} ### prereq-output.json.healthEndpoint {detected health path string or null} ### Conformance result {JSON from Step 10a-conf} ### Session path {.copilot-azure/sessions/{uuid}/}
scaffold-manifest.json with validationResult, deploy checklist generateddeploy-checklist.md exists (written at Step 5b) — if missing, create NOW from deploy-checklist-template.md. Verify deploy-result.json exists — if missing, create from deploy-schemas.ts.context.json update (main-thread — do NOT delegate). Read .copilot-azure/sessions/{uuid}/context.json. If completedPhases does not include "scaffold" OR currentPhase is not "deploy", write it yourself via edit / create: append "scaffold" to completedPhases, set currentPhase to "deploy", update lastModifiedUtc to current UTC ISO 8601. This is a phase-boundary write required by pipeline-rules.md — do not skip it.🚀 Ready to deploy? ...) is the ONLY correct next output.On validation failure → read scaffold-healing-rules.md (healing cadence, PLAN_LEVEL_CHANGE, artifact consistency). Do NOT pre-read.
prepare-plan.json: trigger backfill via orchestrator.Other measured skills in the registry, with their headline benchmark lift.