Subagent Template โ IaC Generation (Steps 5โ8)
Generate deployment-ready IaC from prepare-plan.json. Follow the workflow below โ each step specifies which reference to read and what to do with it.
Critical Rules
- โ Do NOT invoke ANY skills โ no
{"skill": "azure-validate"},{"skill": "azure-deploy"},{"skill": "azure-prepare"}, or any other skill call. Use the procedures in THIS file only. - โ Do NOT generate
azure.yaml - โ Do NOT modify app source code โ only write files under
infra/(andDockerfile.azureif needed) - โ Do NOT run app build/test/lint commands (
npm test,npm run build,pnpm build,python -m pytest,dotnet build, etc.). Only validate generated IaC viaaz bicep build.
Input (provided by caller)
| Field | Source | Required |
|---|---|---|
prepare-plan.json content |
Session folder โ services, naming, quotas, cost, deploymentVariables | YES (verbatim) |
context.json.overrides |
iacFormat, detectedInfraProvider |
YES |
buildRequirements |
From prereq-output.json โ runtime, deps, Dockerfiles |
YES |
warnings[] |
From prereq-output.json โ prereq warnings requiring IaC fixes (env var overrides, config changes). Applied during Steps 3โ4. |
YES |
| Compute targets | App Service/Functions, Container Apps, or both + whether PostgreSQL/Redis present | YES |
apiVersions |
Map of resourceType โ latestGAVersion from main-thread MCP lookup. Use these versions in generated Bicep โ do NOT use versions from training data. If "MCP unavailable" โ see Step 1 for fallback. |
YES |
Output
| Artifact | Location |
|---|---|
infra/main.bicep (or main.tf) |
Workspace infra/ |
infra/main.parameters.json (or variables.tf) |
Workspace infra/ |
infra/modules/{service}.bicep per service |
Workspace infra/modules/ |
| File list | Return to caller for scaffold-manifest.json.files[] |
Workflow
Step 1 โ Read skeleton + tag patterns
Read bicep-patterns.md (Bicep) OR terraform-patterns.md (Terraform) โ NOT both.
Do: Extract the main.bicep skeleton structure (targetScope, parameters, variables, resource group, module calls). Extract the 5-tag block definition. Use prepare-plan.json.naming for all resource names โ never derive names with take(), substring(), uniqueString(), or string manipulation. The 4-char session suffix in the plan names already provides uniqueness. โ For each resource 'Type@Version' declaration, use the version from apiVersions input. If type missing from map, use version from reference file examples.
โ If
apiVersionsis"MCP unavailable"or missing a resource type: runaz provider show --namespace {ns} --query "resourceTypes[?resourceType=='{type}'].apiVersions[?!contains(@, 'preview')] | [0][0]" -o tsvfor each missing provider โ this filters to GA-only and picks the latest. NEVER fall back to training data โ hallucinated API versions cause multiple deploy healing cycles.
Step 2 โ Read compute-target patterns
Read ONLY the compute-target reference(s) matching the plan, if the plan has multiple compute targets, read each matching file.:
- If plan has App Service/Functions โ read bicep-app-service.md.
- If plan has Container Apps โ read bicep-container-apps.md.
- If plan has Static Web Apps โ read bicep-swa.md.
- If plan has BOTH โ read both.
Do: Generate compute module(s) using the patterns from each reference file. F1/D1 App Service: do NOT generate Dockerfile, do NOT add managed identity (OOM). App Service health probe: if prereq-output.json.healthEndpoint is non-null, set siteConfig.healthCheckPath to that value; otherwise omit (do NOT default to /).
Step 3 โ Read security patterns
โ You MUST read bicep-patterns-security.md. It contains Key Vault config, managed identity, HTTPS/TLS, and credential hygiene rules. Apply to every generated module.
Step 4 โ Read generation rules
Read iac-generation-rules.md.
Do: Apply ALL rules from the reference file to every generated module. The file contains mandatory tag definitions, naming constraints, security patterns, env var completeness checks, and Dockerfile generation rules. Do NOT skip any section โ every rule applies.
Step 5 โ Read env var + secrets wiring
Read env-var-secrets.md.
Do: For each component in the plan, read .env.example (or .env.sample, config files like Pydantic Settings, Django settings.py) from the workspace. Map every env var to either: (1) a Bicep parameter, (2) a KV secret reference, or (3) a value derived from other resources (e.g., DB connection string from the DB module output). Wire these into the compute module's appSettings (App Service) or env (Container Apps).
Step 6 โ Generate data modules (if needed)
ONLY if PostgreSQL, MySQL, or Redis is in the plan. Skip if none are present.
PostgreSQL Flexible Server module โ include the AllowAllAzureServicesAndResourcesWithinAzureIps (0.0.0.0) firewall rule, extension allow-list (azure.extensions config: uuid-ossp,pgcrypto,pg_trgm), SSL enforcement, storage config (default 32 GB). Set the server version from prepare-plan.json.services[].version (capabilities-verified) โ do NOT hardcode or guess. Use @secure() param administratorLoginPassword โ deploy generates the value once and reuses it on redeploy; do NOT bake a value.
MySQL Flexible Server module โ mirror the PostgreSQL module, with the MySQL-only deltas: require_secure_transport: ON config, the server version from prepare-plan.json.services[].version (ARM rejects major-only strings like '8.0' โ needs an exact patch such as '8.0.21'), and a flexibleServers/databases child resource for the compose-declared DB name (e.g. MYSQLDB_DATABASE) so the app's schema DB exists in IaC before the container starts. See bicep-patterns-data.md ยง MySQL Flexible Server Module.
Redis Cache module โ Basic SKU, enableNonSslPort: false, minimumTlsVersion: '1.2'. Store hostname + access key in Key Vault. โ Known Bicep type issue: sku property may cause BCP035/BCP187 warnings โ these are false positives. If deploy fails with InvalidRequestBody for properties.sku.name, create via az redis create --sku Basic --vm-size c0 then reference with existing keyword in Bicep.
Wire connection strings via Key Vault secretRef (Container Apps) or @Microsoft.KeyVault() (App Service). โ Container Apps: KV secretRef entries MUST be gated behind isPlaceholder โ Phase 1 deploys with secrets: []. See bicep-container-apps.md ยง Two-Phase Wiring.
Step 7 โ Generate all files
โ Before writing ANY file, verify: (1) KV uses
enableRbacAuthorization: true, NOenablePurgeProtection, NO access policies. (2) No secrets in module outputs โ secrets flow through KV only. (3) API versions fromapiVersionsinput, not memory. (4) Container Apps: norevisionSuffix, placeholder image as default,isPlaceholderconditionals on registries/secrets.
Do: Create the infra/ directory and write all files:
infra/bicepconfig.jsonโ write{ "formatting": { "newlineKind": "LF" } }if it doesn't already exist (user's repo may have one). LF is critical because Bicep triple-quoted strings pass content literally to ARM, and\rbytes crash/bin/shin containers.infra/main.bicepโ subscription scope, RG creation with tags, module calls for all services +role-assignmentsmodule (KV deployer + app-to-KV RBAC), all unconditional.infra/main.parameters.jsonโ ARM JSON format (NOT.bicepparam). IncludeenvironmentName,location,sessionId,deployedBy,createdAt. โcreatedAtvalue: runGet-Date -Format "o"in terminal to get the current ISO 8601 timestamp โ NEVER use a hardcoded or placeholder date. Do NOT include@secure()params (passed at deploy time). IncludedeployerObjectIdparam (deploy phase passes viaaz ad signed-in-user show --query id -o tsv).infra/modules/{service}.bicepโ one module per service from the plan, PLUSrole-assignments.bicep(KV Secrets Officer for deployer, KV Secrets User for app identity if MI enabled โ see bicep-patterns-security.md ยง Key Vault Deployer RBAC).- If
buildRequirements.hasBuildKitSyntax == true: โ create{component}/Dockerfile.azureper dockerfile-generation.md ยง ACR Build Compatibility. - If Container Apps and component has NO Dockerfile: read dockerfile-generation.md and generate one. Follow the layer ordering, port alignment, and security defaults from that reference โ do NOT generate from memory.
โ Health probes for Container Apps: Probe path priority: (1)
prereq-output.json.healthEndpointif non-null, (2) first detected GET route from the app, (3)/only if the app has a root handler. Do NOT default to/for APIs that only serve sub-paths โ returns 404, blocks activation. For DB apps: use/healthznot/readyz(DB not wired in Phase 1).
Step 8 โ Validate syntax
Do: Run az bicep build --file infra/main.bicep --stdout > $null. If errors, fix and re-run (max 2 attempts). Do NOT use the azure-validate skill.
Step 9 โ Return results
Do: Return the list of generated files and any validation notes to the caller. Keep status report โค1500 tokens.