GitHub Copilot ChatGPT Claude Codex CLI Cursor opencode Skill Text

azure-prepare

Prepare azd-based Azure projects for deployment: generates azure.yaml, infrastructure (Bicep/Terraform), and Dockerfiles for the Azure Developer CLI (azd) workflow. USE ONLY when the user explicitly wants to use azd as the deployment tool, or the project already has an azure.yaml

Ciza · 0 points · 21 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-skills_skills_azure-prepare-23d0dac.zip · 249 KB
Part of microsoft/skills — 195 skills

Install

skills CLI npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-skills/skills/azure-prepare
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git git clone https://github.com/microsoft/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Azure Prepare

AUTHORITATIVE GUIDANCE — MANDATORY COMPLIANCE

This document is the official, canonical source for preparing applications for Azure deployment. You MUST follow these instructions exactly as written unless they contradict security policies given to you. When in doubt, present the conflicting instructions from this document and ask the user for explicit confirmation. Do not improvise, infer, or substitute steps.


Triggers

Activate this skill when user wants to:

  • Create a new application
  • Add services or components to an existing app
  • Make updates or changes to existing application
  • Modernize or migrate an application
  • Set up Azure infrastructure
  • Deploy to Azure or host on Azure
  • Create and deploy to Azure (including Terraform-based deployment requests)

Rules

  1. Plan first — MANDATORY — You MUST physically write an initial .azure/deployment-plan.md skeleton in the workspace root directory (not the session-state folder) as your very first action — before any code generation or execution begins. Write the skeleton immediately, then populate it progressively as Phase 1 analysis and research unfold; finalize it with all decisions at Phase 1 Step 6. This file must exist on disk throughout. azure-validate and azure-deploy depend on it and will fail without it. Do not skip or defer this step.
  2. Get approval — Present plan to user before execution
  3. Research before generating — Load references and invoke related skills
  4. Update plan progressively — Mark steps complete as you go
  5. Validate before deploy — Invoke azure-validate before azure-deploy
  6. Confirm Azure context — Use ask_user for subscription and location per Azure Context
  7. ❌ Destructive actions require ask_user — Global Rules
  8. ⛔ NEVER delete user project or workspace directories — When adding features to an existing project, MODIFY existing files. azd init -t <template> is for NEW projects only; do NOT run azd init -t in an existing workspace. Plain azd init (without a template argument) may be used in existing workspaces when appropriate. File deletions within a project (e.g., removing build artifacts or temp files) are permitted when appropriate, but NEVER delete the user's project or workspace directory itself. See Global Rules.
  9. Scope: preparation only — This skill generates infrastructure code and configuration files. Deployment execution (azd up, azd deploy, terraform apply) is handled by the azure-deploy skill, which provides built-in error recovery and deployment verification.
  10. ⛔ SQL Server Bicep: NEVER generate administratorLogin or administratorLoginPassword — not in direct properties, not in conditional/ternary branches, not anywhere in the file. Always use Entra-only authentication (azureADOnlyAuthentication: true) unconditionally. See references/services/sql-database/bicep.md.
  11. Remove stale template IaC after conversion — If you converted Bicep templates from the selected azd template into Terraform templates, remove the Bicep templates that were introduced by that azd template and are now fully replaced by Terraform equivalents. Do not remove user-authored Bicep files. Only remove those template-provided Bicep files after the Terraform IaC is complete and Terraform has been selected as the deployment path. Before handing off to azure-validate skill, keep only the IaC templates required by the chosen deployment path.

❌ PLAN-FIRST WORKFLOW — MANDATORY

YOU MUST CREATE A PLAN BEFORE DOING ANY WORK

  1. STOP — Do not generate any code, infrastructure, or configuration yet
  2. CREATE SKELETON - Write an initial .azure/deployment-plan.md skeleton to disk immediately (before any code generation or execution begins), then populate it progressively as Phase 1 steps 1-5 reveal details; finalize it at Step 6
  3. CONFIRM — Present the completed plan to the user and get approval
  4. EXECUTE — Only after approval, execute the plan step by step

The .azure/deployment-plan.md file is the source of truth for this workflow and for azure-validate and azure-deploy skills. Without it, those skills will fail.

⚠️ CRITICAL: .azure/deployment-plan.md must be WRITTEN TO DISK inside the workspace root (e.g., <workspace-root>/.azure/deployment-plan.md), not in the session-state folder. Use a file-write tool to create this file. This is the deployment plan artifact read by azure-validate and azure-deploy. You MUST create this file — do not proceed without it. ⚠️ CRITICAL: You must create the file with the name .azure/deployment-plan.md as is. You must not use other names such as .azure/plan.md.

⛔ Critical: Skipping the plan file creation will cause azure-validate and azure-deploy to fail. This requirement has no exceptions.


❌ STEP 0: Specialized Technology Check — MANDATORY FIRST ACTION

BEFORE starting Phase 1, check if the user's prompt OR workspace codebase matches a specialized technology that has a dedicated skill with tested templates. If matched, invoke that skill FIRST — then resume azure-prepare for validation and deployment.

Check 1: Prompt keywords

Prompt keywords Invoke FIRST
Python + App Service (e.g., "deploy Python to App Service", "Flask on Azure App Service", "publish Python web app to App Service") python-appservice-deploy
Lambda, AWS Lambda, migrate AWS, migrate GCP, Lambda to Functions, migrate from AWS, migrate from GCP azure-cloud-migrate
Azure Functions, function app, serverless function, timer trigger, HTTP trigger, func new Stay in azure-prepare — prefer Azure Functions templates in Step 4; for plan choice/cold starts see hosting-plans.md and cold-start.md
APIM, API Management, API gateway, deploy APIM Stay in azure-prepare — see APIM Deployment Guide
AI gateway, AI gateway policy, AI gateway backend, AI gateway configuration azure-aigateway
workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable, order processing Stay in azure-prepare — select durable recipe in Step 4. MUST load durable.md, DTS reference, and DTS Bicep patterns.

⚠️ Check the user's prompt text — not just existing code. Critical for greenfield projects with no codebase to scan. See full routing table.

After the specialized skill completes, resume azure-prepare at Phase 1 Step 4 (Select Recipe) for remaining infrastructure, validation, and deployment.


Phase 1: Planning (BLOCKING — Complete Before Any Execution)

Create .azure/deployment-plan.md by completing these steps. Do NOT generate any artifacts until the plan is approved.

# Action Reference
0 If the prompt matches a specialized technology with a dedicated skill, invoke that skill first specialized-routing.md
1 Analyze Workspace — Determine mode: NEW, MODIFY, or MODERNIZE analyze.md
2 Gather Requirements — Classification, scale, budget requirements.md
3 Scan Codebase — Identify components, technologies, dependencies scan.md
4 Select Recipe — Choose AZD (default), AZCLI, Bicep, or Terraform recipe-selection.md
5 Plan Architecture — Select stack + map components to Azure services architecture.md
6 Finalize Plan (MANDATORY) - Use a file-write tool to finalize .azure/deployment-plan.md with all decisions from steps 1-5. Update the skeleton written at the start of Phase 1 with the complete content. The file must be fully populated before you present the plan to the user. plan-template.md
7 Present Plan — Show plan to user and ask for approval .azure/deployment-plan.md
8 Destructive actions require ask_user Global Rules

❌ STOP HERE — Do NOT proceed to Phase 2 until the user approves the plan.


Phase 2: Execution (Only After Plan Approval)

Execute the approved plan. Update .azure/deployment-plan.md status after each step.

# Action Reference
1 Research Components — Load service references + invoke related skills research.md
2 Confirm Azure Context — Detect and confirm subscription + location and check the resource provisioning limit Azure Context
3 Generate Artifacts — Create infrastructure and configuration files generate.md
4 Harden Security — Apply security best practices security.md
5 Functional Verification — Verify the app works (UI + backend), locally if possible functional-verification.md
6 ⛔ Update Plan (MANDATORY before hand-off) — Use the edit tool to change the Status in .azure/deployment-plan.md to Ready for Validation. You MUST complete this edit BEFORE invoking azure-validate. Do NOT skip this step. .azure/deployment-plan.md
7 ⛔ MANDATORY Hand Off — Invoke azure-validate skill. Your preparation work is done. Do NOT run azd up, azd deploy, or any deployment command directly — all deployment execution is handled by azure-deploy after azure-validate completes. PREREQUISITE: Step 6 must be completed first — .azure/deployment-plan.md status must say Ready for Validation. —

Outputs

Artifact Location
Plan .azure/deployment-plan.md
Infrastructure ./infra/
AZD Config azure.yaml (AZD only)
Dockerfiles src/<component>/Dockerfile

SDK Quick References


Next

⛔ MANDATORY NEXT STEP — DO NOT SKIP

After completing preparation, you MUST invoke azure-validate before any deployment attempt. Do NOT skip validation. Do NOT go directly to azure-deploy. Do NOT run azd up or any deployment command directly. The workflow is:

azure-prepare → azure-validate → azure-deploy

⛔ BEFORE invoking azure-validate, you MUST use the edit tool to update .azure/deployment-plan.md status to Ready for Validation. If the plan status has not been updated, the validation will fail.

This applies to ALL deployment scenarios including containerized apps, Container Apps, App Service, Azure Functions, static sites, and any other Azure target. No exceptions.

Skipping validation leads to deployment failures. Be patient and follow the complete workflow for the highest success outcome.

→ Update plan status to Ready for Validation, then invoke azure-validate

Files (skills)
  • references
    • recipes
      • azcli
        • commands.md 1.7 KB
          # Azure CLI Commands
          
          Common az commands for deployment workflows.
          
          ## Resource Group
          
          ```bash
          # Create
          az group create --name <rg-name> --location <location>
          
          # Delete
          az group delete --name <rg-name> --yes --no-wait
          ```
          
          ## Container Registry
          
          ```bash
          # Create
          az acr create --name <acr-name> --resource-group <rg-name> --sku Basic
          
          # Login
          az acr login --name <acr-name>
          
          # Build and push
          az acr build --registry <acr-name> --image <image:tag> .
          ```
          
          ## Container Apps
          
          ```bash
          # Create environment
          az containerapp env create \
            --name <env-name> \
            --resource-group <rg-name> \
            --location <location>
          
          # Deploy app
          az containerapp create \
            --name <app-name> \
            --resource-group <rg-name> \
            --environment <env-name> \
            --image <acr-name>.azurecr.io/<image:tag> \
            --target-port 8080 \
            --ingress external
          ```
          
          ## App Service
          
          ```bash
          # Create plan
          az appservice plan create \
            --name <plan-name> \
            --resource-group <rg-name> \
            --sku B1 --is-linux
          
          # Create web app
          az webapp create \
            --name <app-name> \
            --resource-group <rg-name> \
            --plan <plan-name> \
            --runtime "NODE:22-lts"
          ```
          
          ## Functions
          
          ```bash
          # Create function app
          az functionapp create \
            --name <func-name> \
            --resource-group <rg-name> \
            --storage-account <storage-name> \
            --consumption-plan-location <location> \
            --runtime node \
            --functions-version 4
          ```
          
          ## Key Vault
          
          ```bash
          # Create
          az keyvault create \
            --name <kv-name> \
            --resource-group <rg-name> \
            --location <location>
          
          # Set secret
          az keyvault secret set \
            --vault-name <kv-name> \
            --name <secret-name> \
            --value <secret-value>
          ```
          
        • README.md 1.9 KB
          # AZCLI Recipe
          
          Azure CLI workflow for imperative Azure deployments.
          
          ## When to Use
          
          - Existing az scripts in project
          - Need imperative control over deployment
          - Custom deployment pipelines
          - AKS deployments
          - Direct resource manipulation
          
          ## Before Generation
          
          **REQUIRED: Research best practices before generating any files.**
          
          | Artifact | Research Action |
          |----------|-----------------|
          | Bicep files | Call `mcp_bicep_get_bicep_best_practices` |
          | Bicep modules | Call `mcp_bicep_list_avm_metadata` and follow [AVM module order](../azd/iac-rules.md#avm-module-selection-order-mandatory) |
          | Azure CLI commands | Call `activate_azure_cli_management_tools` |
          | Azure best practices | Call `mcp_azure_mcp_get_azure_bestpractices` |
          
          ## Generation Steps
          
          ### 1. Generate Infrastructure (Bicep)
          
          Create Bicep templates in `./infra/`.
          
          **Structure:**
          ```
          infra/
          ├── main.bicep
          ├── main.parameters.json
          └── modules/
              └── *.bicep
          ```
          
          ### 2. Generate Deployment Scripts
          
          Create deployment scripts for provisioning.
          
          → [scripts.md](scripts.md)
          
          ### 3. Generate Dockerfiles (if containerized)
          
          Manual Dockerfile creation required.
          
          ## Output Checklist
          
          | Artifact | Path |
          |----------|------|
          | Main Bicep | `./infra/main.bicep` |
          | Parameters | `./infra/main.parameters.json` |
          | Modules | `./infra/modules/*.bicep` |
          | Deploy script | `./scripts/deploy.sh` or `deploy.ps1` |
          | Dockerfiles | `src/<service>/Dockerfile` |
          
          ## Deployment Commands
          
          See [commands.md](commands.md) for common patterns.
          
          ## Naming Convention
          
          Resources: `{prefix}{token}{instance}`
          - Alphanumeric only, no special characters
          - Prefix ≤3 chars (e.g., `kv` for Key Vault)
          - Token = 5 char random string
          - Total ≤32 characters
          
          ## References
          
          - [Deployment Commands](commands.md)
          - [Deployment Scripts](scripts.md)
          
          ## Next
          
          → Update `.azure/deployment-plan.md` → **azure-validate**
          
        • scripts.md 2.3 KB
          # Deployment Scripts
          
          Script templates for AZCLI deployments.
          
          ## Bash Script
          
          ```bash
          #!/bin/bash
          set -euo pipefail
          
          # Configuration
          RESOURCE_GROUP="${RESOURCE_GROUP:-rg-myapp}"
          LOCATION="${LOCATION:-eastus2}"
          ENVIRONMENT="${ENVIRONMENT:-dev}"
          
          echo "Deploying to $ENVIRONMENT environment..."
          
          # Create resource group
          az group create \
            --name "$RESOURCE_GROUP" \
            --location "$LOCATION" \
            --tags environment="$ENVIRONMENT"
          
          # Deploy infrastructure
          az deployment group create \
            --resource-group "$RESOURCE_GROUP" \
            --template-file ./infra/main.bicep \
            --parameters ./infra/main.parameters.json \
            --parameters environmentName="$ENVIRONMENT"
          
          # Get outputs
          ACR_NAME=$(az deployment group show \
            --resource-group "$RESOURCE_GROUP" \
            --name main \
            --query properties.outputs.acrName.value -o tsv)
          
          # Build and push containers
          az acr login --name "$ACR_NAME"
          az acr build --registry "$ACR_NAME" --image api:latest ./src/api
          
          echo "Deployment complete!"
          ```
          
          ## PowerShell Script
          
          ```powershell
          #Requires -Version 7.0
          $ErrorActionPreference = "Stop"
          
          # Configuration
          $ResourceGroup = $env:RESOURCE_GROUP ?? "rg-myapp"
          $Location = $env:LOCATION ?? "eastus2"
          $Environment = $env:ENVIRONMENT ?? "dev"
          
          Write-Host "Deploying to $Environment environment..."
          
          # Create resource group
          az group create `
            --name $ResourceGroup `
            --location $Location `
            --tags environment=$Environment
          
          # Deploy infrastructure
          az deployment group create `
            --resource-group $ResourceGroup `
            --template-file ./infra/main.bicep `
            --parameters ./infra/main.parameters.json `
            --parameters environmentName=$Environment
          
          # Get outputs
          $AcrName = az deployment group show `
            --resource-group $ResourceGroup `
            --name main `
            --query properties.outputs.acrName.value -o tsv
          
          # Build and push containers
          az acr login --name $AcrName
          az acr build --registry $AcrName --image api:latest ./src/api
          
          Write-Host "Deployment complete!"
          ```
          
          ## Script Best Practices
          
          | Practice | Description |
          |----------|-------------|
          | Fail fast | `set -euo pipefail` (bash) or `$ErrorActionPreference = "Stop"` (pwsh) |
          | Use variables | Environment-based configuration |
          | Idempotent | Safe to run multiple times |
          | Output logging | Clear progress messages |
          | Error handling | Capture and report failures |
          
      • azd
        • aspire.md 8.9 KB
          # .NET Aspire Projects with AZD
          
          **⛔ MANDATORY: For .NET Aspire projects, NEVER manually create azure.yaml. Use `azd init --from-code` instead.**
          
          ## Detection
          
          | Indicator | How to Detect |
          |-----------|---------------|
          | `*.AppHost.csproj` | `find . -name "*.AppHost.csproj"` |
          | `Aspire.Hosting` package | `grep -r "Aspire\.Hosting" . --include="*.csproj"` |
          | `Aspire.AppHost.Sdk` | `grep -r "Aspire\.AppHost\.Sdk" . --include="*.csproj"` |
          
          ## Workflow
          
          ### ⛔ DO NOT (Wrong Approach)
          
          ```yaml
          # ❌ WRONG - Missing services section
          name: aspire-app
          metadata:
            template: azd-init
          # Results in: "Could not find infra\main.bicep" error
          ```
          
          ### ✅ DO (Correct Approach)
          
          ```bash
          # Generate environment name
          ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
          
          # Use azd init with auto-detection
          azd init --from-code -e "$ENV_NAME"
          ```
          
          **Generated azure.yaml:**
          ```yaml
          name: aspire-app
          metadata:
            template: azd-init
          services:
            app:
              language: dotnet
              project: ./MyApp.AppHost/MyApp.AppHost.csproj
              host: containerapp
          ```
          
          > 💡 **AddDockerfile services:** If the AppHost uses `AddDockerfile()` (e.g., `builder.AddDockerfile("ginapp", "./ginapp")`), do NOT add separate service entries for those resources. Aspire handles container builds for `AddDockerfile` resources at runtime through the AppHost. The `azure.yaml` should contain only the single `app` service pointing to the AppHost.
          
          > ⛔ **BEFORE continuing:** Complete the mandatory [AddParameter + WithBuildArg scan](#after-azd-init-fix-addparameter-used-with-withbuildarg-before-builddeploy) below. Skipping this step is the #1 cause of failed Aspire container-build deployments.
          
          ## Command Flags
          
          | Flag | Required | Purpose |
          |------|----------|---------|
          | `--from-code` | ✅ | Auto-detect AppHost, no prompts |
          | `-e <name>` | ✅ | Environment name (non-interactive) |
          | `--no-prompt` | Optional | Skip all confirmations |
          
          **Why `--from-code` is critical:**
          - Without: Prompts "How do you want to initialize?" (needs TTY)
          - With: Auto-detects AppHost, no interaction needed
          - Essential for agents and CI/CD
          
          ## ⛔ After `azd init`: Fix AddParameter Used with WithBuildArg Before Build/Deploy
          
          > **MANDATORY** — After running `azd init --from-code`, but before `azd package`, `azd up`, or any Docker image build/deploy step, scan the AppHost source for `AddParameter` calls that are passed to `WithBuildArg` or `WithBuildSecret`. This pattern triggers an azd bug that causes Docker builds to fail.
          
          .NET Aspire [external parameters](https://aspire.dev/fundamentals/external-parameters/) let apps request values from the deployment environment. Each `AddParameter()` call generates a Bicep parameter in the deployment manifest. However, azd cannot resolve these Bicep parameters during the Docker image build phase, producing the error `parameter infra.parameters.<name> not found`. This bug has been present across all azd versions tested (including 1.24.0).
          
          ### Scan for the pattern
          
          **Bash:**
          ```bash
          grep -RIn --include="*.cs" -E "AddParameter|WithBuildArg|WithBuildSecret" <path/to/AppHost>
          ```
          
          **PowerShell:**
          ```powershell
          Get-ChildItem -Path "<path/to/AppHost>" -Recurse -Filter "*.cs" |
            Select-String -Pattern "AddParameter|WithBuildArg|WithBuildSecret"
          ```
          
          **Problematic pattern:**
          ```csharp
          // ❌ azd cannot resolve AddParameter values during Docker builds
          var goVersion = builder.AddParameter("goversion", "1.25.4", publishValueAsDefault: true);
          builder.AddDockerfile("ginapp", "./ginapp")
              .WithBuildArg("GO_VERSION", goVersion);
          ```
          
          ### Fix Option A: Replace AddParameter with a constant (preferred)
          
          For every `AddParameter(name, defaultValue, ...)` whose result is used **only** as a `WithBuildArg` argument, replace it with a `const string` (or `string`) constant:
          
          ```csharp
          // ✅ Use a constant instead
          const string goVersion = "1.25.4";
          builder.AddDockerfile("ginapp", "./ginapp")
              .WithBuildArg("GO_VERSION", goVersion);
          ```
          
          **Why:** This eliminates the parameter from Bicep output entirely, so azd never attempts to resolve it during Docker builds. Use this when the build arg value does not need to vary per deployment environment.
          
          ### Fix Option B: Pre-set parameter in azd environment config
          
          If the value must remain an [external parameter](https://aspire.dev/fundamentals/external-parameters/) (e.g., it varies per environment), pre-set it in the azd environment config immediately after `azd init`:
          
          ```bash
          azd env config set infra.parameters.<name> <value>
          ```
          
          **Example:**
          ```bash
          azd env config set infra.parameters.goversion 1.25.4
          ```
          
          **Why:** This writes the parameter value into `.azure/<env>/config.json` so azd can find it during Docker builds without modifying AppHost source code. Use this when the build arg value must be configurable per environment.
          
          > ⚠️ **Do NOT skip this step for container-build projects.** If the AppHost passes an `AddParameter` result to `WithBuildArg`, apply one of these fixes before running `azd up`.
          
          ---
          
          ## Troubleshooting
          
          ### Error: "Could not find infra\main.bicep"
          
          **Cause:** Manual azure.yaml without services section
          
          **Fix:**
          1. Delete manual azure.yaml
          2. Run `azd init --from-code -e <env-name>`
          3. Verify services section exists
          
          ### Error: "no default response for prompt"
          
          **Cause:** Missing `--from-code` flag
          
          **Fix:** Always use `--from-code` for Aspire:
          ```bash
          azd init --from-code -e "$ENV_NAME"
          ```
          
          ### Error: "parameter infra.parameters.<name> not found"
          
          **Cause:** The AppHost uses `AddParameter()` as a `WithBuildArg` argument. azd cannot resolve Aspire [external parameters](https://aspire.dev/fundamentals/external-parameters/) during Docker builds, regardless of azd version.
          
          **Example error:**
          ```
          ERROR: building service 'ginapp': parameter infra.parameters.goversion not found
          ```
          
          **Fix (Option A — preferred):** In the AppHost source, replace the `AddParameter(...)` call with a constant:
          
          ```csharp
          // ❌ Before (causes the error)
          var goVersion = builder.AddParameter("goversion", "1.25.4", publishValueAsDefault: true);
          builder.AddDockerfile("ginapp", "./ginapp")
              .WithBuildArg("GO_VERSION", goVersion);
          
          // ✅ After (fix)
          const string goVersion = "1.25.4";
          builder.AddDockerfile("ginapp", "./ginapp")
              .WithBuildArg("GO_VERSION", goVersion);
          ```
          
          **Fix (Option B):** Pre-set the parameter in azd environment config:
          ```bash
          azd env config set infra.parameters.goversion 1.25.4
          ```
          
          ### AppHost Not Detected
          
          **Solutions:**
          1. Verify: `find . -name "*.AppHost.csproj"`
          2. Build: `dotnet build`
          3. Check package references in .csproj
          4. Run from solution root
          
          ### Error: "unsupported resource type" — Custom Aspire Resources
          
          **Symptoms:** `azd init --from-code` fails with `unsupported resource type` for one or more resources in the AppHost (e.g., custom child resources, ClockHand, or other custom Aspire integration types).
          
          **Cause:** The AppHost contains custom Aspire resource types designed for local development tooling only. These resources have no Azure equivalent and are not deployable.
          
          **Resolution:**
          
          1. ⛔ **Stop — do NOT fix this error by modifying source code.** Do not add `.ExcludeFromManifest()` to suppress the error.
          2. ⛔ **Do NOT proceed with deployment.**
          3. ✅ Inform the user: the application uses custom Aspire resource authoring patterns intended for local tooling, not cloud deployment.
          4. ✅ Record a deployment blocker: "AppHost contains custom Aspire resource types (`unsupported resource type`) with no Azure deployment target."
          
          > ⚠️ Adding `.ExcludeFromManifest()` to work around this error violates the application's design intent and may produce an incomplete or incorrect deployment.
          
          ## Infrastructure Auto-Generation
          
          | Traditional | Aspire |
          |------------|--------|
          | Manual infra/main.bicep | Auto-gen from AppHost |
          | Define in IaC | Define in C# code |
          | Update IaC per service | Add to AppHost |
          
          **How it works:**
          1. AppHost defines services in C#
          2. `azd provision` analyzes AppHost
          3. Generates Bicep automatically
          4. Deploys to Azure Container Apps
          
          ## Validation Steps
          
          1. **⛔ Fix `AddParameter` used with `WithBuildArg`** — see [Post-Init: Fix AddParameter Used with WithBuildArg](#after-azd-init-fix-addparameter-used-with-withbuildarg-before-builddeploy)
          2. Verify azure.yaml has a non-empty services section
          3. Do NOT add separate service entries for `AddDockerfile()` resources — Aspire handles container builds at runtime through the AppHost
          4. Run `azd package` to validate Docker build succeeds
          5. Review generated infra/ (don't modify)
          
          ## Next Steps
          
          1. Set subscription: `azd env set AZURE_SUBSCRIPTION_ID <id>`
          2. Proceed to **azure-validate**
          3. Deploy with **azure-deploy** (`azd up`)
          
          ## References
          
          - [.NET Aspire External Parameters](https://aspire.dev/fundamentals/external-parameters/)
          - [.NET Aspire Docs](https://learn.microsoft.com/dotnet/aspire/)
          - [azd + Aspire](https://learn.microsoft.com/dotnet/aspire/deployment/azure/aca-deployment-azd-in-depth)
          - [Samples](https://github.com/dotnet/aspire-samples)
          - [Main Guide](../../aspire.md)
          - [azure.yaml Schema](azure-yaml.md)
          - [Docker Guide](docker.md)
        • azure-yaml.md 6.9 KB
          # azure.yaml Generation
          
          > ⛔ **CRITICAL: Check for .NET Aspire projects FIRST**
          >
          > **DO NOT manually create azure.yaml for .NET Aspire projects.** If you detect:
          > - Files ending with `*.AppHost.csproj` (e.g., `MyApp.AppHost.csproj`)
          > - `Aspire.Hosting` or `Aspire.AppHost.Sdk` in `.csproj` files
          >
          > **STOP and use `azd init --from-code` instead.** See [aspire.md](aspire.md) for details.
          
          Create `azure.yaml` in project root for AZD.
          
          ## Structure
          
          ### Basic (Bicep - default)
          
          ```yaml
          name: <project-name>
          metadata:
            template: azd-init
          
          services:
            <service-name>:
              project: <path-to-source>
              language: <python|js|ts|java|dotnet|go>
              host: <containerapp|appservice|function|staticwebapp|aks>
          ```
          
          ### With Terraform Provider
          
          ```yaml
          name: <project-name>
          metadata:
            template: azd-init
          
          # Specify Terraform as IaC provider
          infra:
            provider: terraform
            path: ./infra
          
          services:
            <service-name>:
              project: <path-to-source>
              language: <python|js|ts|java|dotnet|go>
              host: <containerapp|appservice|function|staticwebapp|aks>
          ```
          
          > 💡 **Tip:** Omit `infra` section to use Bicep (default). Add `infra.provider: terraform` to use Terraform. See [terraform.md](terraform.md) for details.
          
          ## Host Types
          
          | Host | Azure Service | Use For |
          |------|---------------|---------|
          | `containerapp` | Container Apps | APIs, microservices, workers |
          | `appservice` | App Service | Traditional web apps |
          | `function` | Azure Functions | Serverless functions |
          | `staticwebapp` | Static Web Apps | SPAs, static sites |
          | `aks` | AKS | Kubernetes workloads |
          
          ## Examples
          
          ### Container App with Bicep (default)
          
          ```yaml
          name: myapp
          
          services:
            api:
              project: ./src/api
              language: python
              host: containerapp
              docker:
                path: ./src/api/Dockerfile
          ```
          
          ### Container App with Terraform
          
          ```yaml
          name: myapp
          
          infra:
            provider: terraform
            path: ./infra
          
          services:
            api:
              project: ./src/api
              language: python
              host: containerapp
              docker:
                path: ./src/api/Dockerfile
          ```
          
          ### Container App with Custom Docker Context
          
          When a **non-Aspire** project has a Dockerfile that expects files relative to a specific directory:
          
          ```yaml
          name: myapp
          
          services:
            ginapp:
              project: .
              host: containerapp
              image: ginapp
              docker:
                path: ginapp/Dockerfile
                context: ginapp
          ```
          
          > 💡 **Tip:** The `context` field specifies the Docker build context directory. This is crucial for:
          > - Dockerfiles with `COPY` commands expecting files relative to a subdirectory
          > - Multi-service repos where each service has its own context
          
          > ⚠️ **Aspire projects:** Do NOT manually add per-service entries with `docker.context` for Aspire `AddDockerfile()` resources. Aspire handles container builds at runtime through the AppHost. The generated `azure.yaml` should contain only a single `app` service pointing to the AppHost. See [aspire.md](aspire.md) for details.
          
          > ⚠️ **Language Field:** When using the `docker` section, the `language` field should be **omitted** or set to the language that azd will use for framework-specific behaviors. For containerized apps with custom Dockerfiles, the language is not used by azd since the build is handled by Docker. Only include `language` if you need azd to perform additional framework-specific actions beyond Docker build.
          
          ### Azure Functions
          
          ```yaml
          services:
            functions:
              project: ./src/functions
              language: js
              host: function
          ```
          
          ### Static Web App (with framework build)
          
          For React, Vue, Angular, Next.js, etc. that require `npm run build`:
          
          ```yaml
          services:
            web:
              project: ./src/web     # folder containing package.json
              language: js           # triggers: npm install && npm run build
              host: staticwebapp
              dist: dist             # build output folder (e.g., dist, build, out)
          ```
          
          ### Static Web App (pure HTML/CSS - no build)
          
          For pure HTML sites without a framework build step:
          
          **Static files in subfolder (recommended):**
          ```yaml
          services:
            web:
              project: ./src/web     # folder containing index.html
              host: staticwebapp
              dist: .                # works when project != root
          ```
          
          **Static files in root - requires build script:**
          
          > ⚠️ **SWA CLI Limitation:** When `project: .`, you cannot use `dist: .`. Files must be copied to a separate output folder.
          
          Add a minimal `package.json` with a build script:
          ```json
          {
            "scripts": {
              "build": "node -e \"require('fs').mkdirSync('public',{recursive:true});require('fs').readdirSync('.').filter(f=>/\\.(html|css|js|png|jpe?g|gif|svg|ico|json|xml|txt|webmanifest|map)$/i.test(f)).forEach(f=>require('fs').copyFileSync(f,'public/'+f))\""
            }
          }
          ```
          
          Then configure azure.yaml with `language: js` to trigger the build:
          ```yaml
          services:
            web:
              project: .
              language: js           # triggers npm install && npm run build
              host: staticwebapp
              dist: public
          ```
          
          ### SWA Project Structure Detection
          
          | Layout | Configuration |
          |--------|---------------|
          | Static in root | `project: .`, `language: js`, `dist: public` + package.json build script |
          | Framework in root | `project: .`, `language: js`, `dist: <output>` |
          | Static in subfolder | `project: ./path`, `dist: .` |
          | Framework in subfolder | `project: ./path`, `language: js`, `dist: <output>` |
          
          > **Key rules:**
          > - `dist` is **relative to `project`** path
          > - **SWA CLI limitation**: When `project: .`, cannot use `dist: .` - must use a distinct folder
          > - For static files in root, add `package.json` with build script to copy files to dist folder
          > - Use `language: js` to trigger npm build even for pure static sites in root
          > - `language: html` and `language: static` are **NOT valid** - will fail
          
          ### SWA Bicep Requirement
          
          Bicep must include the `azd-service-name` tag:
          ```bicep
          resource staticWebApp 'Microsoft.Web/staticSites@2022-09-01' = {
            name: name
            location: location
            tags: union(tags, { 'azd-service-name': 'web' })}
          ```
          }
          ```
          
          ### App Service
          
          ```yaml
          services:
            api:
              project: ./src/api
              language: dotnet
              host: appservice
          ```
          
          ## Hooks (Optional)
          
          ```yaml
          hooks:
            preprovision:
              shell: sh
              run: ./scripts/setup.sh
            postprovision:
              shell: sh
              run: ./scripts/seed-data.sh
          ```
          
          ## Valid Values
          
          | Field | Options |
          |-------|---------|
          | `language` | python, js, ts, java, dotnet, go (omit for staticwebapp without build) |
          | `host` | containerapp, appservice, function, staticwebapp, aks |
          | `docker.path` | Path to Dockerfile (relative to project root) |
          | `docker.context` | Docker build context directory (optional, defaults to directory containing Dockerfile) |
          
          > 💡 **Docker Context:** When `docker.context` is omitted, azd uses the directory containing the Dockerfile as the build context. Specify `context` explicitly when the Dockerfile expects files from a different directory.
          
          ## Output
          
          - `./azure.yaml`
          
        • docker.md 2.1 KB
          # Dockerfile Generation
          
          Create Dockerfiles for containerized services.
          
          ## When to Containerize
          
          | Include | Exclude |
          |---------|---------|
          | APIs, microservices | Static websites (use Static Web Apps) |
          | Web apps (SSR) | Azure Functions (native deploy) |
          | Background workers | Database services |
          | Message processors | Logic Apps |
          
          ## Templates by Language
          
          ### Node.js
          
          ```dockerfile
          FROM node:22-slim
          WORKDIR /app
          COPY package*.json ./
          RUN npm ci --only=production
          COPY . .
          EXPOSE 3000
          CMD ["node", "index.js"]
          ```
          
          ### Python
          
          ```dockerfile
          FROM python:3.13-slim
          WORKDIR /app
          COPY requirements.txt .
          RUN pip install --no-cache-dir -r requirements.txt
          COPY . .
          EXPOSE 8000
          CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
          ```
          
          ### .NET
          
          ```dockerfile
          FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
          WORKDIR /app
          EXPOSE 8080
          
          FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
          WORKDIR /src
          COPY ["*.csproj", "./"]
          RUN dotnet restore
          COPY . .
          RUN dotnet publish -c Release -o /app/publish
          
          FROM base AS final
          WORKDIR /app
          COPY --from=build /app/publish .
          ENTRYPOINT ["dotnet", "App.dll"]
          ```
          
          ### Java
          
          ```dockerfile
          FROM eclipse-temurin:21-jdk-alpine AS build
          WORKDIR /app
          COPY . .
          RUN ./mvnw package -DskipTests
          
          FROM eclipse-temurin:21-jre-alpine
          WORKDIR /app
          COPY --from=build /app/target/*.jar app.jar
          EXPOSE 8080
          ENTRYPOINT ["java", "-jar", "app.jar"]
          ```
          
          ### Go
          
          ```dockerfile
          FROM golang:1.22-alpine AS build
          WORKDIR /app
          COPY go.* ./
          RUN go mod download
          COPY . .
          RUN CGO_ENABLED=0 go build -o main .
          
          FROM alpine:latest
          WORKDIR /app
          COPY --from=build /app/main .
          EXPOSE 8080
          CMD ["./main"]
          ```
          
          ## .dockerignore
          
          ```
          .git
          node_modules
          __pycache__
          *.pyc
          .env
          .azure
          ```
          
          ## Best Practices
          
          - Use slim/alpine base images
          - Multi-stage builds for compiled languages
          - Non-root user when possible
          - Include health check endpoint in app
          
          ## Runtime-Specific Configuration
          
          For production settings specific to each runtime:
          
          | Runtime | Reference |
          |---------|-----------| 
          | Node.js/Express | [runtimes/nodejs.md](../../runtimes/nodejs.md) |
          
        • iac-rules.md 5.8 KB
          # AZD IAC Rules
          
          IaC rules for AZD projects. **Additive** — for Bicep, apply `mcp_bicep_get_bicep_best_practices`, `mcp_bicep_list_avm_metadata`, and `mcp_bicep_get_az_resource_type_schema` first; for Terraform, apply `mcp_azure_mcp_azureterraformbestpractices` first; then apply these azd-specific rules.
          
          ## AVM Module Selection Order (MANDATORY)
          
          Always prefer modules in provider-specific order:
          
          For **Bicep**:
          1. AVM Bicep Pattern Modules (AVM+AZD first when available)
          2. AVM Bicep Resource Modules
          3. AVM Bicep Utility Modules
          
          For **Terraform**:
          1. AVM Terraform Pattern Modules
          2. AVM Terraform Resource Modules
          3. AVM Terraform Utility Modules
          
          If no pattern module exists for the active provider, default immediately to AVM modules in the same provider order (resource, then utility) instead of using non-AVM modules.
          
          ## Retrieval Strategy (azure-documentation MCP primary + optional Context7)
          
          - **Primary (authoritative):** Use `mcp_azure_mcp_documentation` (`azure-documentation`) for current Azure guidance and AVM integration documentation.
          - **Primary (module catalog):** Use `mcp_bicep_list_avm_metadata` plus official AVM indexes to select concrete modules.
          - **Secondary (supplemental):** Use Context7 only for implementation examples when `mcp_azure_mcp_documentation` does not provide enough detail. If Context7 is not available, instruct the user to install it: `npx -y @upstash/context7-mcp@latest`.
          
          ## Validation Plan
          
          Before finalizing generated guidance:
          
          1. Verify the selected module path uses the required AVM order above.
          2. Verify AVM+AZD pattern modules were checked first, and fallback moved to AVM resource/utility modules when no pattern module exists.
          3. Verify Terraform guidance follows pattern -> resource -> utility ordering.
          4. Include selected module names and source links in the plan/output for traceability.
          
          ## File Structure
          
          | Requirement | Details |
          |-------------|---------|
          | Location | `./infra/` folder |
          | Entry point | `main.bicep` with `targetScope = 'subscription'` |
          | Parameters | `main.parameters.json` (ARM JSON — see format below) |
          | Modules | `./infra/modules/*.bicep` with `targetScope = 'resourceGroup'` |
          
          ## Parameter File Format
          
          `main.parameters.json` uses ARM JSON syntax. Do **not** use `.bicepparam` syntax (`using`, `param`, `readEnvironmentVariable()`) in this file — `azd` will fail with a JSON parse error.
          
          ```json
          {
            "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {
              "environmentName": { "value": "${AZURE_ENV_NAME}" },
              "location": { "value": "${AZURE_LOCATION}" }
            }
          }
          ```
          
          Use `azd env set` to supply values. During `azd provision`, azd substitutes `${VAR}` placeholders with values from the environment.
          
          ## Naming Convention
          
          > ⚠️ **Before generating any resource name in Bicep, check [Resource naming rules](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules) for that resource type's valid characters, length limits, and uniqueness scope.** Some resources forbid dashes or special characters, require globally unique names, or have short length limits. Adapt the pattern below accordingly.
          
          **Default pattern:** `{resourceAbbreviation}-{name}-{uniqueHash}`
          
          For resources that disallow dashes, omit separators: `{resourceAbbreviation}{name}{uniqueHash}`
          
          - [Resource abbreviations](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) — recommended prefixes per resource type
          
          ```bicep
          var resourceSuffix = take(uniqueString(subscription().id, environmentName, location), 6)
          // Adapt separator/format per resource naming rules
          var defaultName = '${name}-${resourceSuffix}'
          var alphanumericName = replace('${name}${resourceSuffix}', '-', '')
          ```
          
          **Forbidden:** Hard-coded tenant IDs, subscription IDs, resource group names
          
          ## Required Tags
          
          | Tag | Apply To | Value |
          |-----|----------|-------|
          | `azd-env-name` | Resource group | `{environmentName}` |
          | `azd-service-name` | Hosting resources | Service name from azure.yaml |
          
          ## Module Parameters
          
          All modules must accept: `name` (string), `location` (string), `tags` (object)
          
          ## Security
          
          | Rule | Details |
          |------|---------|
          | No secrets | Use Key Vault references |
          | Managed Identity | Least privilege |
          | Diagnostics | Enable logging |
          | API versions | Use latest |
          
          ## Recommended Outputs
          
          `azd` reads `output` values from `main.bicep` and stores UPPERCASE names as environment variables (accessible via `azd env get-values`).
          
          | Output | When |
          |--------|------|
          | `AZURE_RESOURCE_GROUP` | Always (required) |
          | `AZURE_CONTAINER_REGISTRY_ENDPOINT` | If using containers |
          | `AZURE_KEY_VAULT_NAME` | If using secrets |
          | `AZURE_LOG_ANALYTICS_WORKSPACE_ID` | If using monitoring |
          | `API_URL`, `WEB_URL`, etc. | One per service endpoint |
          
          ## Templates
          
          **main.bicep:**
          
          ```bicep
          targetScope = 'subscription'
          
          param environmentName string
          param location string
          
          var resourceSuffix = take(uniqueString(subscription().id, environmentName, location), 6)
          var tags = { 'azd-env-name': environmentName }
          
          resource rg 'Microsoft.Resources/resourceGroups@2023-07-01' = {
            name: 'rg-${environmentName}'
            location: location
            tags: tags
          }
          
          module resources './modules/resources.bicep' = {
            name: 'resources'
            scope: rg
            params: { location: location, tags: tags }
          }
          
          // Outputs — UPPERCASE names become azd env vars
          output AZURE_RESOURCE_GROUP string = rg.name
          output API_URL string = resources.outputs.apiUrl
          ```
          
          **Child module:**
          
          ```bicep
          targetScope = 'resourceGroup'
          
          param name string
          param location string = resourceGroup().location
          param tags object = {}
          
          var resourceSuffix = take(uniqueString(subscription().id, resourceGroup().name, name), 6)
          ```
          
          > ⚠️ **Container resources:** CPU must use `json()` wrapper: `cpu: json('0.5')`, memory as string: `memory: '1Gi'`
          
        • README.md 3.9 KB
          # AZD Recipe
          
          Azure Developer CLI workflow for preparing Azure deployments.
          
          ## When to Use
          
          - New projects, multi-service apps, want `azd up`
          - Need environment management, auto-generated CI/CD
          - Team prefers simplified deployment workflow
          
          > 💡 **Tip:** azd supports both Bicep and Terraform as IaC providers. Choose based on your team's expertise and requirements.
          
          ## IaC Provider Options
          
          | Provider | Use When |
          |----------|----------|
          | **Bicep** (default) | Azure-only, no existing IaC, want simplest setup |
          | **Terraform** | Multi-cloud IaC, existing TF expertise, want azd simplicity |
          
          **For Terraform with azd:** See [terraform.md](terraform.md)
          
          ## Before Generation
          
          **REQUIRED: Research best practices before generating any files.**
          
          ### Check for Existing Codebase Patterns
          
          **⚠️ CRITICAL: For existing codebases with special patterns, use `azd init --from-code -e <environment-name>` instead of manual generation.**
          
          | Pattern | Detection | Action |
          |---------|-----------|--------|
          | **.NET Aspire** | `*.AppHost.csproj` or `Aspire.Hosting` package | Use `azd init --from-code -e <environment-name>` → [aspire.md](../../aspire.md) |
          | **Existing azure.yaml** | `azure.yaml` present | MODIFY mode - update existing config |
          | **New project** | No azure.yaml, no special patterns | Manual generation (steps below) |
          
          > 💡 **Note:** The `-e <environment-name>` flag is **required** when running `azd init --from-code` in non-interactive environments (agents, CI/CD pipelines). Without it, the command will fail with a prompt error.
          
          ### References for Manual Generation
          
          | Artifact | Reference |
          |----------|-----------|
          | azure.yaml | [Schema Guide](azure-yaml.md) |
          | .NET Aspire projects | [Aspire Guide](../../aspire.md) |
          | Terraform with azd | [Terraform Guide](terraform.md) |
          | AZD IAC rules | [IAC Rules](iac-rules.md) |
          | Azure Functions templates | [Templates](../../services/functions/templates/README.md) |
          | Bicep best practices | `mcp_bicep_get_bicep_best_practices` |
          | Bicep resource schema | `mcp_bicep_get_az_resource_type_schema` |
          | Azure Verified Modules | `mcp_bicep_list_avm_metadata` + [AVM module order](iac-rules.md#avm-module-selection-order-mandatory) |
          | Terraform best practices | `mcp_azure_mcp_azureterraformbestpractices` |
          | Dockerfiles | [Docker Guide](docker.md) |
          
          ## Generation Steps
          
          ### For Bicep (default)
          
          | # | Artifact | Reference |
          |---|----------|-----------|
          | 1 | azure.yaml | [Schema Guide](azure-yaml.md) |
          | 2 | Application code | Entry points, health endpoints, config |
          | 3 | Dockerfiles | [Docker Guide](docker.md) (if containerized) |
          | 4 | Infrastructure | `./infra/main.bicep` + modules per [IAC Rules](iac-rules.md) |
          
          ### For Terraform
          
          | # | Artifact | Reference |
          |---|----------|-----------|
          | 1 | azure.yaml with `infra.provider: terraform` | [Terraform Guide](terraform.md) |
          | 2 | Application code | Entry points, health endpoints, config |
          | 3 | Dockerfiles | [Docker Guide](docker.md) (if containerized) |
          | 4 | Terraform files | `./infra/*.tf` per [Terraform Guide](terraform.md) |
          
          ## Outputs
          
          ### For Bicep
          
          | Artifact | Path |
          |----------|------|
          | azure.yaml | `./azure.yaml` |
          | App Code | `src/<service>/*` |
          | Dockerfiles | `src/<service>/Dockerfile` (if containerized) |
          | Infrastructure | `./infra/` (Bicep files) |
          
          ### For Terraform
          
          | Artifact | Path |
          |----------|------|
          | azure.yaml | `./azure.yaml` (with `infra.provider: terraform`) |
          | App Code | `src/<service>/*` |
          | Dockerfiles | `src/<service>/Dockerfile` (if containerized) |
          | Infrastructure | `./infra/` (Terraform files) |
          
          ## References
          
          - [.NET Aspire Projects](../../aspire.md)
          - [azure.yaml Schema](azure-yaml.md)
          - [.NET Aspire Apps](aspire.md)
          - [Terraform with AZD](terraform.md)
          - [Docker Configuration](docker.md)
          - [IAC Rules](iac-rules.md)
          
          ## Next
          
          → Update `.azure/deployment-plan.md` → **azure-validate**
          
        • terraform.md 14 KB
          # AZD with Terraform
          
          Use Azure Developer CLI (azd) with Terraform as the infrastructure provider.
          
          ## When to Use
          
          Choose azd+Terraform when you want:
          - **Terraform's multi-cloud capabilities** with **azd's deployment simplicity**
          - Existing Terraform expertise but want `azd up` convenience
          - Team familiar with Terraform but needs environment management
          - Multi-cloud IaC with Azure-first deployment experience
          
          ## Benefits
          
          | Feature | Pure Terraform | AZD + Terraform |
          |---------|---------------|-----------------|
          | Deploy command | `terraform apply` | `azd up` |
          | Environment management | Manual workspaces | Built-in `azd env` |
          | CI/CD generation | Manual setup | Auto-generated pipelines |
          | Service deployment | Manual scripts | Automatic from azure.yaml |
          | State management | Manual backend setup | Configurable |
          | Multi-cloud | ✅ Yes | ✅ Yes |
          
          ## Configuration
          
          ### 1. azure.yaml Structure
          
          Create `azure.yaml` in project root:
          
          ```yaml
          name: myapp
          metadata:
            template: azd-init
          
          # Specify Terraform as IaC provider
          infra:
            provider: terraform
            path: ./infra
          
          # Define services as usual
          services:
            api:
              project: ./src/api
              language: python
              host: containerapp
              docker:
                path: ./src/api/Dockerfile
            
            web:
              project: ./src/web
              language: js
              host: staticwebapp
              dist: dist
          ```
          
          ### 2. Terraform File Structure
          
          Place Terraform files in `./infra/`:
          
          ```
          infra/
          ├── main.tf              # Main resources
          ├── variables.tf         # Variable definitions
          ├── outputs.tf           # Output values
          ├── provider.tf          # Provider configuration
          └── modules/
              ├── api/
              │   └── main.tf
              └── web/
                  └── main.tf
          ```
          
          ### 3. Provider Configuration
          
          **provider.tf:**
          ```hcl
          terraform {
            required_version = ">= 1.5.0"
          
            required_providers {
              azurerm = {
                source  = "hashicorp/azurerm"
                version = "~> 4.2"
              }
              azurecaf = {
                source  = "aztfmod/azurecaf"
                version = "~> 1.2"
              }
            }
          
            # Optional: Remote state for team collaboration
            backend "azurerm" {
              resource_group_name  = "rg-terraform-state"
              storage_account_name = "tfstate${var.state_suffix}"
              container_name       = "tfstate"
              key                  = "app.terraform.tfstate"
            }
          }
          
          provider "azurerm" {
            features {}
          }
          ```
          
          > **⚠️ IMPORTANT**: For **Azure Functions Flex Consumption**, use azurerm provider **v4.2 or later**:
          > ```hcl
          > terraform {
          >   required_providers {
          >     azurerm = {
          >       source  = "hashicorp/azurerm"
          >       version = "~> 4.2"
          >     }
          >   }
          > }
          > ```
          > See [Terraform Functions patterns](../../services/functions/terraform.md) for Flex Consumption examples.
          
          ### 4. Variables and Outputs
          
          > ⚠️ **WARNING: Use `${VAR}` syntax in `main.tfvars.json`, NOT Go-style `{{ .Env.* }}`**
          >
          > azd's template engine processes `azure.yaml` and service manifests — it does **NOT** interpolate
          > Go-style `{{ .Env.* }}` template variables in `.tfvars.json` or any Terraform variable files.
          > Literal strings like `{{ .Env.AZURE_ENV_NAME }}` will be passed directly to Terraform, causing
          > deployment failures.
          >
          > azd reads `infra/main.tfvars.json`, substitutes `${VAR}` references using its built-in envsubst,
          > and passes the resolved file to Terraform via `-var-file=`. Use this pattern:
          >
          > ```json
          > {
          >     "environment_name": "${AZURE_ENV_NAME}",
          >     "location": "${AZURE_LOCATION}",
          >     "subscription_id": "${AZURE_SUBSCRIPTION_ID}"
          > }
          > ```
          >
          > For additional variables not in `main.tfvars.json`, use **`TF_VAR_*` environment variables**:
          > `azd env set TF_VAR_myvar value`
          
          **variables.tf:**
          ```hcl
          variable "environment_name" {
            type        = string
            description = "Environment name from azd"
          }
          
          variable "location" {
            type        = string
            description = "Azure region"
            default     = "eastus2"
          }
          
          variable "principal_id" {
            type        = string
            description = "User principal ID from azd auth"
            default     = ""
          }
          ```
          
          **outputs.tf:**
          ```hcl
          # Required: Resource group name
          output "AZURE_RESOURCE_GROUP" {
            value = azurerm_resource_group.main.name
          }
          
          # Service-specific outputs
          output "API_URL" {
            value = azurerm_container_app.api.latest_revision_fqdn
          }
          
          output "WEB_URL" {
            value = azurerm_static_web_app.web.default_host_name
          }
          ```
          
          > 💡 **Tip:** Output names in UPPERCASE are automatically set as azd environment variables.
          
          ### 5. Required Tags for azd
          
          **CRITICAL:** Tag hosting resources with service names from azure.yaml:
          
          ```hcl
          resource "azurerm_container_app" "api" {
            name                = "ca-${var.environment_name}-api"
            resource_group_name = azurerm_resource_group.main.name
            
            # Required for azd deploy to find this resource
            tags = merge(var.tags, {
              "azd-service-name" = "api"  # Matches service name in azure.yaml
            })
            
            # ... rest of configuration
          }
          
          resource "azurerm_static_web_app" "web" {
            name                = "swa-${var.environment_name}-web"
            resource_group_name = azurerm_resource_group.main.name
            
            # Required for azd deploy to find this resource
            tags = merge(var.tags, {
              "azd-service-name" = "web"  # Matches service name in azure.yaml
            })
            
            # ... rest of configuration
          }
          ```
          
          > ⚠️ **WARNING:** Without `azd-service-name` tags, `azd deploy` will fail to find deployment targets.
          
          ### 6. Resource Group Tags
          
          Tag the resource group with environment name:
          
          ```hcl
          resource "azurerm_resource_group" "main" {
            name     = "rg-${var.environment_name}"
            location = var.location
            
            tags = {
              "azd-env-name" = var.environment_name
            }
          }
          ```
          
          ## Deployment Workflow
          
          ### Initial Setup
          
          ```bash
          # 1. Create azd environment
          azd env new dev --no-prompt
          
          # 2. Set required variables
          azd env set AZURE_LOCATION eastus2
          
          # 3. Provision infrastructure (runs terraform init, plan, apply)
          azd provision
          
          # 4. Deploy services
          azd deploy
          
          # Or do both with single command
          azd up
          ```
          
          ### Variables and State
          
          **azd environment variables** → **Terraform variables**
          
          azd passes variables to Terraform through `main.tfvars.json` (with `${VAR}` substitution) or
          explicit `TF_VAR_*` environment variables. Define the variable in `variables.tf` and reference
          it in `main.tfvars.json`.
          
          infra/main.tfvars.json — azd substitutes ${VAR} references via envsubst:
          ```json
          {
              "environment_name": "${AZURE_ENV_NAME}",
              "location": "${AZURE_LOCATION}",
              "database_name": "${DATABASE_NAME}"
          }
          ```
          
          variables.tf — value provided via main.tfvars.json or TF_VAR_database_name:
          ```hcl
          variable "database_name" {
            type = string
          }
          ```
          
          For variables not in `main.tfvars.json`, use `TF_VAR_*` environment variables:
          
          ```bash
          azd env set TF_VAR_custom_setting "my-value"
          ```
          
          > ⚠️ **Use `${VAR}` syntax in `main.tfvars.json`, NOT Go-style `{{ .Env.* }}`.** azd substitutes
          > `${VAR}` references using its built-in envsubst. Go-style template variables are only processed
          > in `azure.yaml` and service manifests. See [Variables and Outputs](#4-variables-and-outputs) for details.
          
          **Remote state setup:**
          
          ```bash
          # Create state storage (one-time setup)
          az group create --name rg-terraform-state --location eastus2
          
          az storage account create \
            --name tfstate<unique> \
            --resource-group rg-terraform-state \
            --sku Standard_LRS
          
          az storage container create \
            --name tfstate \
            --account-name tfstate<unique>
          
          # Set backend variables for azd
          azd env set TF_STATE_RESOURCE_GROUP rg-terraform-state
          azd env set TF_STATE_STORAGE_ACCOUNT tfstate<unique>
          ```
          
          ## Generation Steps
          
          When preparing a new azd+Terraform project:
          
          1. **Generate azure.yaml** with `infra.provider: terraform`
          2. **Create Terraform files** in `./infra/`:
             - `main.tf` - Core resources and resource group
             - `variables.tf` - environment_name, location, tags
             - `outputs.tf` - Service URLs and resource names (UPPERCASE)
             - `provider.tf` - azurerm provider + backend config
          3. **Add required tags**:
             - Resource group: `azd-env-name`
             - Hosting resources: `azd-service-name` (matches azure.yaml services)
          4. **Research best practices** - Call `mcp_azure_mcp_azureterraformbestpractices`
          
          ## AVM Terraform Module Priority
          
          For Terraform module selection, enforce this order:
          
          1. AVM Terraform Pattern Modules
          2. AVM Terraform Resource Modules
          3. AVM Terraform Utility Modules
          
          Use `mcp_azure_mcp_documentation` (`azure-documentation`) for current guidance and AVM context first, then use Context7 only as supplemental examples if required. If Context7 is not available, instruct the user to install it:
          
          ```bash
          npx @upstash/context7-mcp@latest
          ```
          
          ## Migration from Pure Terraform
          
          Converting existing Terraform project to use azd:
          
          1. Create `azure.yaml` with services and `infra.provider: terraform`
          2. Move `.tf` files to `./infra/` directory
          3. Add `azd-service-name` tags to hosting resources
          4. Ensure outputs include service URLs in UPPERCASE
          5. Test with `azd provision` and `azd deploy`
          
          ## CI/CD Integration
          
          azd can auto-generate pipelines for Terraform:
          
          ```bash
          # Generate GitHub Actions workflow
          azd pipeline config
          
          # Generate Azure DevOps pipeline
          azd pipeline config --provider azdo
          ```
          
          Generated pipelines will:
          - Install Terraform
          - Run `terraform init`, `plan`, `apply`
          - Use azd authentication
          - Deploy services with `azd deploy`
          
          ## Comparison: azd+Terraform vs Pure Terraform
          
          | Aspect | Pure Terraform | azd + Terraform |
          |--------|---------------|-----------------|
          | **IaC** | Terraform | Terraform |
          | **Provision** | `terraform apply` | `azd provision` (wraps terraform) |
          | **Deploy apps** | Manual scripts | `azd deploy` (automatic) |
          | **Environment mgmt** | Workspaces | `azd env` |
          | **Auth** | Manual az login | `azd auth login` |
          | **CI/CD** | Manual setup | `azd pipeline config` |
          | **Multi-service** | Manual orchestration | Automatic from azure.yaml |
          | **Learning curve** | Medium | Low |
          
          ## When NOT to Use azd+Terraform
          
          Use pure Terraform (without azd) when:
          - Multi-cloud deployment (not Azure-first)
          - Complex Terraform modules/workspaces that conflict with azd conventions
          - Existing complex Terraform CI/CD that's hard to migrate
          - Team has strong Terraform expertise but no bandwidth for azd learning
          
          ## Azure Policy Compliance
          
          Enterprise Azure subscriptions typically enforce security policies. Your Terraform must comply:
          
          ### Storage Account (Required for Functions)
          
          ```hcl
          resource "azurerm_storage_account" "storage" {
            name                            = "stmyapp${random_string.suffix.result}"
            resource_group_name             = azurerm_resource_group.rg.name
            location                        = azurerm_resource_group.rg.location
            account_tier                    = "Standard"
            account_replication_type        = "LRS"
            
            # Azure policy requirements
            allow_nested_items_to_be_public = false   # Disable anonymous blob access
            local_user_enabled              = false   # Disable local users
            shared_access_key_enabled       = false   # RBAC-only, no access keys
          }
          ```
          
          ### Function App with Managed Identity Storage
          
          ```hcl
          provider "azurerm" {
            features {}
            storage_use_azuread = true   # Required when shared_access_key_enabled = false
          }
          
          resource "azurerm_linux_function_app" "function" {
            name                          = "func-myapp"
            resource_group_name           = azurerm_resource_group.rg.name
            location                      = azurerm_resource_group.rg.location
            service_plan_id               = azurerm_service_plan.plan.id
            storage_account_name          = azurerm_storage_account.storage.name
            storage_uses_managed_identity = true   # Use MI instead of access key
            
            identity {
              type = "SystemAssigned"
            }
            
            tags = {
              "azd-service-name" = "api"   # REQUIRED for azd deploy
            }
            
            depends_on = [azurerm_role_assignment.deployer_storage]
          }
          
          # RBAC for deploying user (create function with MI storage)
          resource "azurerm_role_assignment" "deployer_storage" {
            scope                = azurerm_storage_account.storage.id
            role_definition_name = "Storage Blob Data Owner"
            principal_id         = data.azurerm_client_config.current.object_id
          }
          
          # RBAC for function app after creation
          resource "azurerm_role_assignment" "function_storage" {
            scope                = azurerm_storage_account.storage.id
            role_definition_name = "Storage Blob Data Owner"
            principal_id         = azurerm_linux_function_app.function.identity[0].principal_id
          }
          ```
          
          ### Services with Disabled Local Auth
          
          ```hcl
          # Service Bus
          resource "azurerm_servicebus_namespace" "sb" {
            local_auth_enabled = false   # RBAC-only
          }
          
          # Event Hubs
          resource "azurerm_eventhub_namespace" "eh" {
            local_authentication_enabled = false   # RBAC-only
          }
          
          # Cosmos DB
          resource "azurerm_cosmosdb_account" "cosmos" {
            local_authentication_disabled = true   # RBAC-only
          }
          ```
          
          ## Troubleshooting
          
          | Issue | Solution |
          |-------|----------|
          | `resource not found: unable to find a resource tagged with 'azd-service-name'` | Add `azd-service-name` tag to hosting resource in Terraform |
          | `RequestDisallowedByPolicy: shared key access` | Set `shared_access_key_enabled = false` on storage |
          | `RequestDisallowedByPolicy: local auth disabled` | Set `local_auth_enabled = false` on Service Bus |
          | `RequestDisallowedByPolicy: anonymous blob access` | Set `allow_nested_items_to_be_public = false` on storage |
          | `terraform command not found` | Install Terraform CLI: `brew install terraform` or download from terraform.io |
          | State conflicts | Configure remote backend in provider.tf |
          | Variable not passed to Terraform | Ensure variable is set with `azd env set` and defined in variables.tf |
          | Literal `{{ .Env.* }}` in Terraform errors | Use `${VAR}` syntax in `main.tfvars.json`, not Go-style `{{ .Env.* }}`. azd substitutes `${VAR}` references via envsubst |
          | `main.tfvars.json` interpolation failure | Ensure `main.tfvars.json` uses `${VAR}` syntax (e.g., `${AZURE_ENV_NAME}`), not Go-style `{{ .Env.* }}` templates |
          
          ## References
          
          - [Microsoft Docs: Use Terraform with azd](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/use-terraform-for-azd)
          - [azd-starter-terraform template](https://github.com/Azure-Samples/azd-starter-terraform)
          - [Terraform Azure Provider](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs)
          - [Azure CAF Naming](https://registry.terraform.io/providers/aztfmod/azurecaf/latest/docs)
          
      • bicep
        • patterns.md 3.2 KB
          # Bicep Patterns
          
          Common patterns for Bicep infrastructure templates.
          
          ## File Structure
          
          ```
          infra/
          ├── main.bicep              # Entry point (subscription scope)
          ├── main.parameters.json    # Parameter values
          └── modules/
              ├── resources.bicep     # Base resources
              ├── container-app.bicep # Container App module
              └── ...
          ```
          
          ## main.bicep Template
          
          ```bicep
          targetScope = 'subscription'
          
          @minLength(1)
          @maxLength(64)
          param environmentName string
          
          @minLength(1)
          param location string
          
          var tags = { environment: environmentName }
          
          resource rg 'Microsoft.Resources/resourceGroups@2023-07-01' = {
            name: 'rg-${environmentName}'
            location: location
            tags: tags
          }
          
          module resources './modules/resources.bicep' = {
            name: 'resources'
            scope: rg
            params: {
              location: location
              environmentName: environmentName
              tags: tags
            }
          }
          
          output resourceGroupName string = rg.name
          ```
          
          ## main.parameters.json
          
          > ⚠️ **Warning:** This file uses ARM JSON syntax. Do **not** use `.bicepparam` syntax (`using`, `param`, `readEnvironmentVariable()`) in this file — `azd` will fail with a JSON parse error.
          
          ```json
          {
            "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {
              "environmentName": { "value": "${AZURE_ENV_NAME}" },
              "location": { "value": "${AZURE_LOCATION}" }
            }
          }
          ```
          
          Use `azd env set` to supply values at deploy time:
          
          ```bash
          azd env set AZURE_ENV_NAME myapp-1234
          azd env set AZURE_LOCATION eastus2
          ```
          
          ## Naming Convention
          
          ```bicep
          var resourceToken = uniqueString(subscription().id, resourceGroup().id, location)
          
          // Pattern: {prefix}{name}{token}
          // Total ≤32 chars, alphanumeric only
          var kvName = 'kv${environmentName}${resourceToken}'
          var storName = 'stor${resourceToken}'
          
          // Container Registry: alphanumeric only (5-50 chars)
          var acrName = replace('cr${environmentName}${resourceToken}', '-', '')
          ```
          
          ## Security Requirements
          
          | Requirement | Pattern |
          |-------------|---------|
          | No hardcoded secrets | Use Key Vault references |
          | Managed Identity | `identity: { type: 'UserAssigned' }` |
          | HTTPS only | `httpsOnly: true` |
          | TLS 1.2+ | `minTlsVersion: '1.2'` |
          | No public blob access | `allowBlobPublicAccess: false` |
          
          ## Common Modules
          
          ### Log Analytics
          
          ```bicep
          resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
            name: 'log-${resourceToken}'
            location: location
            properties: {
              sku: { name: 'PerGB2018' }
              retentionInDays: 30
            }
          }
          ```
          
          ### Application Insights
          
          ```bicep
          resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
            name: 'appi-${resourceToken}'
            location: location
            kind: 'web'
            properties: {
              Application_Type: 'web'
              WorkspaceResourceId: logAnalytics.id
            }
          }
          ```
          
          ### Key Vault
          
          ```bicep
          resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
            name: 'kv-${resourceToken}'
            location: location
            properties: {
              sku: { family: 'A', name: 'standard' }
              tenantId: subscription().tenantId
              enableRbacAuthorization: true
            }
          }
          ```
          
          
        • README.md 1.4 KB
          # Bicep Recipe
          
          Standalone Bicep workflow (without AZD).
          
          ## When to Use
          
          - IaC-first approach
          - No CLI wrapper needed
          - Direct ARM deployment control
          - Existing Bicep modules to reuse
          - Custom deployment orchestration
          
          ## Before Generation
          
          **REQUIRED: Research best practices before generating any files.**
          
          | Artifact | Research Action |
          |----------|-----------------|
          | Bicep files | Call `mcp_bicep_get_bicep_best_practices` |
          | Bicep modules | Call `mcp_bicep_list_avm_metadata` and follow [AVM module order](../azd/iac-rules.md#avm-module-selection-order-mandatory) |
          | Resource schemas | Use `activate_azure_resource_schema_tools` if needed |
          
          ## Generation Steps
          
          ### 1. Generate Infrastructure
          
          Create Bicep templates in `./infra/`.
          
          → [patterns.md](patterns.md)
          
          **Structure:**
          ```
          infra/
          ├── main.bicep
          ├── main.parameters.json
          └── modules/
              ├── container-app.bicep
              ├── storage.bicep
              └── ...
          ```
          
          ### 2. Generate Dockerfiles (if containerized)
          
          Manual Dockerfile creation required.
          
          ## Output Checklist
          
          | Artifact | Path |
          |----------|------|
          | Main Bicep | `./infra/main.bicep` |
          | Parameters | `./infra/main.parameters.json` |
          | Modules | `./infra/modules/*.bicep` |
          | Dockerfiles | `src/<service>/Dockerfile` |
          
          ## References
          
          - [Bicep Patterns](patterns.md)
          
          ## Next
          
          → Update `.azure/deployment-plan.md` → **azure-validate**
          
      • terraform
        • patterns.md 3.7 KB
          # Terraform Patterns
          
          Common patterns for Terraform Azure deployments.
          
          ## File Structure
          
          ```
          infra/
          ├── main.tf             # Main resources
          ├── variables.tf        # Variable definitions
          ├── outputs.tf          # Output values
          ├── terraform.tfvars    # Variable values
          ├── backend.tf          # State backend
          └── modules/
              └── ...
          ```
          
          ## Provider Configuration
          
          ```hcl
          # backend.tf
          terraform {
            required_version = ">= 1.5.0"
          
            required_providers {
              azurerm = {
                source  = "hashicorp/azurerm"
                version = "~> 3.0"
              }
              azurecaf = {
                source  = "aztfmod/azurecaf"
                version = "~> 1.2"
              }
            }
          
            backend "azurerm" {
              resource_group_name  = "rg-terraform-state"
              storage_account_name = "tfstate<unique>"
              container_name       = "tfstate"
              key                  = "app.terraform.tfstate"
            }
          }
          
          provider "azurerm" {
            features {}
          }
          ```
          
          ## Variables
          
          ```hcl
          # variables.tf
          variable "environment" {
            type        = string
            description = "Environment name"
          }
          
          variable "location" {
            type        = string
            description = "Azure region"
            default     = "eastus2"
          }
          ```
          
          ## Main Configuration
          
          ```hcl
          # main.tf
          resource "azurerm_resource_group" "main" {
            name     = "rg-${var.environment}"
            location = var.location
            tags     = { environment = var.environment }
          }
          
          module "app" {
            source              = "./modules/app"
            resource_group_name = azurerm_resource_group.main.name
            location            = azurerm_resource_group.main.location
            environment         = var.environment
          }
          ```
          
          ## Outputs
          
          ```hcl
          # outputs.tf
          output "resource_group_name" {
            value = azurerm_resource_group.main.name
          }
          
          output "app_url" {
            value = module.app.url
          }
          ```
          
          ## Naming with Azure CAF
          
          ```hcl
          resource "azurecaf_name" "storage" {
            name          = var.environment
            resource_type = "azurerm_storage_account"
            random_length = 5
          }
          
          resource "azurerm_storage_account" "main" {
            name = azurecaf_name.storage.result
            # ...
          }
          ```
          
          ## State Backend Setup
          
          ```bash
          # Create state storage
          az group create --name rg-terraform-state --location eastus2
          
          az storage account create \
            --name tfstate<unique> \
            --resource-group rg-terraform-state \
            --sku Standard_LRS
          
          az storage container create \
            --name tfstate \
            --account-name tfstate<unique>
          ```
          
          ## Security Requirements
          
          | Requirement | Pattern |
          |-------------|---------|
          | No hardcoded secrets | Use Key Vault data sources |
          | Managed Identity | `identity { type = "UserAssigned" }` |
          | State encryption | Azure Storage encryption |
          | State locking | Azure Blob lease |
          
          ## Container Apps with ACR
          
          > **⚠️ Two-Phase Deployment Required.** See [Container Apps Terraform Patterns](../../services/container-apps/terraform.md) for full details.
          
          Use a **public placeholder image** during initial provisioning. Never reference an ACR image that hasn't been built yet. Do **not** use `admin_enabled` on the ACR or add a `registry` block with `username`/`password_secret_name` — use managed identity instead.
          
          ```hcl
          resource "azurerm_container_app" "api" {
            # ...
            template {
              container {
                name   = "api"
                image  = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
                # ...
              }
            }
          
            # Prevent Terraform from reverting post-apply image and registry updates
            lifecycle {
              ignore_changes = [
                template[0].container[0].image,
                registry,
              ]
            }
          }
          ```
          
          After `terraform apply`, build the real image and update the Container App via CLI — see the **azure-deploy** skill's `references/recipes/terraform/README.md` (Container Apps Two-Phase Deployment section).
          
        • README.md 2.6 KB
          # Terraform Recipe
          
          Terraform workflow for Azure deployments.
          
          > **⚠️ IMPORTANT: Consider azd+Terraform First**
          >
          > If you're deploying to Azure, you should **default to [azd with Terraform](../azd/terraform.md)** instead of pure Terraform. azd+Terraform gives you:
          > - Terraform's IaC capabilities
          > - Simple `azd up` deployment workflow
          > - Built-in environment management
          > - Automatic CI/CD pipeline generation
          > - Service orchestration from azure.yaml
          >
          > → **See [azd+Terraform documentation](../azd/terraform.md)** ←
          
          ## When to Use Pure Terraform (Without azd)
          
          Only use pure Terraform workflow when you have specific requirements that prevent using azd:
          
          - **Multi-cloud deployments** where Azure is not the primary target
          - **Complex Terraform modules/workspaces** that are incompatible with azd conventions
          - **Existing Terraform CI/CD** pipelines that are hard to migrate
          - **Organization mandate** for pure Terraform workflow without any wrapper tools
          - **Explicitly requested** by the user to use Terraform without azd
          
          ## When to Use azd+Terraform Instead
          
          Use azd+Terraform (the default) when:
          
          - **Azure-first deployment** (even if you want multi-cloud IaC)
          - Want **`azd up` simplicity** with Terraform IaC
          - **Multi-service apps** needing orchestration
          - Team wants to learn Terraform with a simpler workflow
          
          → See [azd+Terraform documentation](../azd/terraform.md)
          
          ## Before Generation
          
          **REQUIRED: Research best practices before generating any files.**
          
          | Artifact | Research Action |
          |----------|-----------------|
          | Terraform patterns | Call `mcp_azure_mcp_azureterraformbestpractices` |
          | Azure best practices | Call `mcp_azure_mcp_get_azure_bestpractices` |
          
          ## Generation Steps
          
          ### 1. Generate Infrastructure
          
          Create Terraform files in `./infra/`.
          
          → [patterns.md](patterns.md)
          
          **Structure:**
          ```
          infra/
          ├── main.tf
          ├── variables.tf
          ├── outputs.tf
          ├── terraform.tfvars
          ├── backend.tf
          └── modules/
              └── ...
          ```
          
          ### 2. Set Up State Backend
          
          Azure Storage for remote state.
          
          ### 3. Generate Dockerfiles (if containerized)
          
          Manual Dockerfile creation required.
          
          ## Output Checklist
          
          | Artifact | Path |
          |----------|------|
          | Main config | `./infra/main.tf` |
          | Variables | `./infra/variables.tf` |
          | Outputs | `./infra/outputs.tf` |
          | Values | `./infra/terraform.tfvars` |
          | Backend | `./infra/backend.tf` |
          | Modules | `./infra/modules/` |
          | Dockerfiles | `src/<service>/Dockerfile` |
          
          ## References
          
          - [Terraform Patterns](patterns.md)
          
          ## Next
          
          → Update `.azure/deployment-plan.md` → **azure-validate**
          
    • runtimes
      • nodejs.md 5.9 KB
        # Node.js/Express Production Configuration for Azure
        
        Configure Express/Node.js applications for production deployment on Azure Container Apps and App Service.
        
        ## Required Production Settings
        
        ### 1. Trust Proxy (CRITICAL)
        
        Azure load balancers and reverse proxies sit in front of your app. Without trust proxy, you'll get wrong client IPs, HTTPS detection failures, and cookie issues.
        
        ```javascript
        const app = express();
        
        // REQUIRED for Azure - trust the Azure load balancer
        app.set('trust proxy', 1);  // Trust first proxy
        
        // Or trust all proxies (less secure but simpler)
        app.set('trust proxy', true);
        ```
        
        ### 2. Cookie Configuration
        
        Azure's infrastructure requires specific cookie settings:
        
        ```javascript
        app.use(session({
          secret: process.env.SESSION_SECRET,
          resave: false,
          saveUninitialized: false,
          cookie: {
            secure: process.env.NODE_ENV === 'production',  // HTTPS only in prod
            sameSite: 'lax',  // Required for Azure
            httpOnly: true,
            maxAge: 24 * 60 * 60 * 1000  // 24 hours
          }
        }));
        ```
        
        **Key settings:**
        - `sameSite: 'lax'` — Required for cookies through Azure's proxy
        - `secure: true` — Only in production (HTTPS)
        - `httpOnly: true` — Prevent XSS attacks
        
        ### 3. Health Check Endpoint
        
        Azure Container Apps and App Service check your app's health:
        
        ```javascript
        app.get('/health', (req, res) => {
          res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() });
        });
        ```
        
        **Configure in Container Apps:**
        ```bash
        az containerapp update \
          --name APP \
          --resource-group RG \
          --health-probe-path /health \
          --health-probe-interval 30
        ```
        
        ### 4. Port Configuration
        
        Azure sets the port via environment variable:
        
        ```javascript
        const port = process.env.PORT || process.env.WEBSITES_PORT || 3000;
        app.listen(port, '0.0.0.0', () => {
          console.log(`Server running on port ${port}`);
        });
        ```
        
        **Important:** Bind to `0.0.0.0`, not `localhost` or `127.0.0.1`.
        
        ### 5. Environment Detection
        
        ```javascript
        const isProduction = process.env.NODE_ENV === 'production';
        const isAzure = process.env.WEBSITE_SITE_NAME || process.env.CONTAINER_APP_NAME;
        
        if (isProduction || isAzure) {
          app.set('trust proxy', 1);
        }
        ```
        
        ---
        
        ## Complete Production Configuration
        
        ```javascript
        // app.js - Production-ready Express configuration for Azure
        const express = require('express');
        const session = require('express-session');
        
        const app = express();
        const isProduction = process.env.NODE_ENV === 'production';
        
        // Trust Azure load balancer
        if (isProduction) {
          app.set('trust proxy', 1);
        }
        
        // Security headers
        app.use((req, res, next) => {
          res.setHeader('X-Content-Type-Options', 'nosniff');
          res.setHeader('X-Frame-Options', 'DENY');
          next();
        });
        
        // JSON parsing
        app.use(express.json());
        app.use(express.urlencoded({ extended: true }));
        
        // Session (if using)
        app.use(session({
          secret: process.env.SESSION_SECRET || 'dev-secret-change-in-prod',
          resave: false,
          saveUninitialized: false,
          cookie: {
            secure: isProduction,
            sameSite: 'lax',
            httpOnly: true,
            maxAge: 24 * 60 * 60 * 1000
          }
        }));
        
        // Health check
        app.get('/health', (req, res) => {
          res.status(200).json({ status: 'ok' });
        });
        
        // Your routes here
        app.get('/', (req, res) => {
          res.json({ message: 'Hello from Azure!' });
        });
        
        // Error handler
        app.use((err, req, res, next) => {
          console.error(err.stack);
          res.status(500).json({ error: isProduction ? 'Internal error' : err.message });
        });
        
        // Start server
        const port = process.env.PORT || 3000;
        app.listen(port, '0.0.0.0', () => {
          console.log(`Server running on port ${port}`);
        });
        ```
        
        ---
        
        ## Dockerfile for Azure
        
        ```dockerfile
        FROM node:20-alpine
        
        WORKDIR /app
        
        # Install dependencies first (better caching)
        COPY package*.json ./
        RUN npm ci --only=production
        
        # Copy app
        COPY . .
        
        # Set production environment
        ENV NODE_ENV=production
        
        # Expose port (Azure uses PORT env var)
        EXPOSE 3000
        
        # Health check
        HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
          CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
        
        # Start app
        CMD ["node", "app.js"]
        ```
        
        ---
        
        ## Common Issues
        
        ### Cookies Not Setting
        
        **Symptom:** Session lost between requests
        
        **Fix:**
        1. Add `app.set('trust proxy', 1)`
        2. Set `sameSite: 'lax'` in cookie config
        3. Set `secure: true` only if using HTTPS
        
        ### Wrong Client IP
        
        **Symptom:** `req.ip` returns Azure internal IP
        
        **Fix:** `app.set('trust proxy', 1);`
        
        ### HTTPS Redirect Loop
        
        **Symptom:** Infinite redirects when forcing HTTPS
        
        **Fix:**
        ```javascript
        const TRUSTED_HOST = process.env.APP_PUBLIC_HOSTNAME;
        
        app.use((req, res, next) => {
          if (req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV === 'production') {
            if (!TRUSTED_HOST) return next();
            return res.redirect(`https://${TRUSTED_HOST}${req.originalUrl}`);
          }
          next();
        });
        ```
        
        ### Health Check Failing
        
        **Symptom:** Container restarts repeatedly
        
        **Fix:**
        1. Ensure `/health` endpoint returns 200
        2. Check app starts within startup probe timeout
        3. Verify port matches container configuration
        
        ---
        
        ## Environment Variables
        
        > ⚠️ **Important distinction**: `azd env set` vs Application Environment Variables
        >
        > **`azd env set`** sets variables for the **azd provisioning process**, NOT application runtime. These are used by azd and Bicep during deployment.
        >
        > **Application environment variables** must be configured via:
        > 1. **Bicep templates** — Define in the resource's `env` property
        > 2. **Azure CLI** — Use `az containerapp update --set-env-vars`
        > 3. **azure.yaml** — Use the `env` section in service configuration
        
        **Azure CLI:**
        ```bash
        az containerapp update \
          --name APP \
          --resource-group RG \
          --set-env-vars \
            NODE_ENV=production \
            SESSION_SECRET=your-secret-here \
            PORT=3000
        ```
        
        **azure.yaml:**
        ```yaml
        services:
          api:
            host: containerapp
            env:
              NODE_ENV: production
              PORT: "3000"
        ```
        
        **Bicep:**
        ```bicep
        env: [
          { name: 'NODE_ENV', value: 'production' }
          { name: 'SESSION_SECRET', secretRef: 'session-secret' }
        ]
        ```
        
    • sdk
      • azd-deployment.md 792 B
        # Azure Developer CLI — Quick Reference
        
        > Condensed from **azd-deployment**. Full patterns (Bicep modules,
        > hooks, RBAC post-provision, service discovery, idempotent deploys)
        > in the **azd-deployment** plugin skill if installed.
        
        ## Install
        curl -fsSL https://aka.ms/install-azd.sh | bash
        
        ## Quick Start
        ```bash
        azd auth login
        azd init
        azd up    # provision + build + deploy
        ```
        
        ## Best Practices
        - Always use remoteBuild: true — local builds fail on ARM Macs deploying to AMD64
        - Bicep outputs auto-populate .azure/<env>/.env — don't manually edit
        - Use azd env set for secrets — not main.parameters.json defaults
        - Service tags (azd-service-name) are required for azd to find Container Apps
        - Use `|| true` in hooks — prevent RBAC "already exists" errors from failing deploy
        
      • azure-appconfiguration-java.md 1.5 KB
        # App Configuration — Java SDK Quick Reference
        
        > Condensed from **azure-appconfiguration-java**. Full patterns (feature flags,
        > secret references, snapshots, async client, conditional requests)
        > in the **azure-appconfiguration-java** plugin skill if installed.
        
        ## Install
        ```xml
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-data-appconfiguration</artifactId>
            <version>1.8.0</version>
        </dependency>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-identity</artifactId>
        </dependency>
        ```
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```java
        import com.azure.data.appconfiguration.ConfigurationClientBuilder;
        import com.azure.identity.DefaultAzureCredentialBuilder;
        var client = new ConfigurationClientBuilder()
            .credential(new DefaultAzureCredentialBuilder().build())
            .endpoint(System.getenv("AZURE_APPCONFIG_ENDPOINT"))
            .buildClient();
        ```
        
        ## Best Practices
        - Use labels — separate configurations by environment (Dev, Staging, Production)
        - Use snapshots — create immutable snapshots for releases
        - Feature flags — use for gradual rollouts and A/B testing
        - Secret references — store sensitive values in Key Vault
        - Conditional requests — use ETags for optimistic concurrency
        - Read-only protection — lock critical production settings
        - Use Entra ID — preferred over connection strings
        - Async client — use for high-throughput scenarios
        
      • azure-appconfiguration-py.md 1.1 KB
        # App Configuration — Python SDK Quick Reference
        
        > Condensed from **azure-appconfiguration-py**. Full patterns (feature flags,
        > snapshots, read-only settings, async client, labels)
        > in the **azure-appconfiguration-py** plugin skill if installed.
        
        ## Install
        pip install azure-appconfiguration azure-identity
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```python
        from azure.appconfiguration import AzureAppConfigurationClient
        from azure.identity import DefaultAzureCredential
        client = AzureAppConfigurationClient(base_url="https://<name>.azconfig.io", credential=DefaultAzureCredential())
        ```
        
        ## Best Practices
        - Use labels for environment separation (dev, staging, prod)
        - Use key prefixes for logical grouping (app:database:*, app:cache:*)
        - Make production settings read-only to prevent accidental changes
        - Create snapshots before deployments for rollback capability
        - Use Entra ID instead of connection strings in production
        - Refresh settings periodically in long-running applications
        - Use feature flags for gradual rollouts and A/B testing
        
      • azure-appconfiguration-ts.md 1.2 KB
        # App Configuration — TypeScript SDK Quick Reference
        
        > Condensed from **azure-appconfiguration-ts**. Full patterns (provider,
        > dynamic refresh, Key Vault references, feature flags, snapshots)
        > in the **azure-appconfiguration-ts** plugin skill if installed.
        
        ## Install
        npm install @azure/app-configuration @azure/identity
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```typescript
        import { AppConfigurationClient } from "@azure/app-configuration";
        import { DefaultAzureCredential } from "@azure/identity";
        const client = new AppConfigurationClient(process.env.AZURE_APPCONFIG_ENDPOINT!, new DefaultAzureCredential());
        ```
        
        ## Best Practices
        - Use provider for apps — @azure/app-configuration-provider for runtime config
        - Use low-level for management — @azure/app-configuration for CRUD operations
        - Enable refresh for dynamic configuration updates
        - Use labels to separate configurations by environment
        - Use snapshots for immutable release configurations
        - Sentinel pattern — use a sentinel key to trigger full refresh
        - RBAC roles — App Configuration Data Reader for read-only access
        
      • azure-identity-dotnet.md 941 B
        # Authentication — .NET SDK Quick Reference
        
        > Condensed from **azure-identity-dotnet**. Full patterns (ASP.NET DI,
        > sovereign clouds, brokered auth, certificate credentials)
        > in the **azure-identity-dotnet** plugin skill if installed.
        
        ## Install
        dotnet add package Azure.Identity
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```csharp
        using Azure.Identity;
        var credential = new DefaultAzureCredential();
        ```
        
        ## Best Practices
        - Use DefaultAzureCredential for **local development only**. In production, use deterministic credentials (ManagedIdentityCredential) — see [auth-best-practices.md](../auth-best-practices.md)
        - Reuse credential instances — single instance shared across clients
        - Configure retry policies for credential operations
        - Enable logging with AzureEventSourceListener for debugging auth issues
        
      • azure-identity-java.md 1.2 KB
        # Authentication — Java SDK Quick Reference
        
        > Condensed from **azure-identity-java**. Full patterns (workload identity,
        > certificate auth, device code, sovereign clouds)
        > in the **azure-identity-java** plugin skill if installed.
        
        ## Install
        ```xml
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-identity</artifactId>
            <version>1.15.0</version>
        </dependency>
        ```
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```java
        import com.azure.identity.DefaultAzureCredentialBuilder;
        var credential = new DefaultAzureCredentialBuilder().build();
        ```
        
        ## Best Practices
        - Use DefaultAzureCredential for **local development only** (CLI, PowerShell, VS Code). In production, use ManagedIdentityCredential — see [auth-best-practices.md](../auth-best-practices.md)
        - Managed identity in production — no secrets to manage, automatic rotation
        - Azure CLI for local dev — run `az login` before running your app
        - Least privilege — grant only required permissions to service principals
        - Token caching — enabled by default, reduces auth round-trips
        - Environment variables — use for CI/CD, not hardcoded secrets
        
      • azure-identity-py.md 1.1 KB
        # Authentication — Python SDK Quick Reference
        
        > Condensed from **azure-identity-py**. Full patterns (async,
        > ChainedTokenCredential, token caching, all credential types)
        > in the **azure-identity-py** plugin skill if installed.
        
        ## Install
        ```bash
        pip install azure-identity
        ```
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```python
        from azure.identity import DefaultAzureCredential
        credential = DefaultAzureCredential()
        ```
        
        ## Best Practices
        - Use DefaultAzureCredential for **local development only** (CLI, PowerShell, VS Code). In production, use ManagedIdentityCredential — see [auth-best-practices.md](../auth-best-practices.md)
        - Never hardcode credentials — use environment variables or managed identity
        - Prefer managed identity in production Azure deployments
        - Use ChainedTokenCredential when you need a custom credential order
        - Close async credentials explicitly or use context managers
        - Set AZURE_CLIENT_ID env var for user-assigned managed identities
        - Exclude unused credentials to speed up authentication
        
      • azure-identity-ts.md 1.1 KB
        # Authentication — TypeScript SDK Quick Reference
        
        > Condensed from **azure-identity-ts**. Full patterns (sovereign clouds,
        > device code flow, custom credentials, bearer token provider)
        > in the **azure-identity-ts** plugin skill if installed.
        
        ## Install
        npm install @azure/identity
        
        ## Quick Start
        
        > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../auth-best-practices.md) for production patterns.
        
        ```typescript
        import { DefaultAzureCredential } from "@azure/identity";
        const credential = new DefaultAzureCredential();
        ```
        
        ## Best Practices
        - Use DefaultAzureCredential for **local development only** (CLI, PowerShell, VS Code). In production, use ManagedIdentityCredential — see [auth-best-practices.md](../auth-best-practices.md)
        - Never hardcode credentials — use environment variables or managed identity
        - Prefer managed identity — no secrets to manage in production
        - Scope credentials appropriately — use user-assigned identity for multi-tenant scenarios
        - Handle token refresh — Azure SDK handles this automatically
        - Use ChainedTokenCredential for custom fallback scenarios
        
    • services
      • aks
        • addons.md 1015 B
          # AKS - Add-ons
          
          ## Container Monitoring
          
          ```bicep
          addonProfiles: {
            omsagent: {
              enabled: true
              config: {
                logAnalyticsWorkspaceResourceID: logAnalytics.id
              }
            }
          }
          ```
          
          ## Azure CNI Networking
          
          ```bicep
          networkProfile: {
            networkPlugin: 'azure'
            networkPolicy: 'calico'
          }
          ```
          
          ## Azure Key Vault Provider
          
          ```bicep
          addonProfiles: {
            azureKeyvaultSecretsProvider: {
              enabled: true
              config: {
                enableSecretRotation: 'true'
              }
            }
          }
          ```
          
          ## Application Gateway Ingress Controller
          
          ```bicep
          addonProfiles: {
            ingressApplicationGateway: {
              enabled: true
              config: {
                applicationGatewayId: appGateway.id
              }
            }
          }
          ```
          
          ## Add-ons Summary
          
          | Add-on | Purpose |
          |--------|---------|
          | omsagent | Container Insights monitoring |
          | azureKeyvaultSecretsProvider | Mount Key Vault secrets as volumes |
          | ingressApplicationGateway | Application Gateway as ingress controller |
          | azurepolicy | Azure Policy for Kubernetes |
          
        • bicep.md 1.7 KB
          # AKS - Bicep Patterns
          
          ## Cluster Resource
          
          ```bicep
          resource aks 'Microsoft.ContainerService/managedClusters@2023-07-01' = {
            name: '${resourcePrefix}-aks-${uniqueHash}'
            location: location
            identity: {
              type: 'SystemAssigned'
            }
            properties: {
              dnsPrefix: '${resourcePrefix}-aks'
              kubernetesVersion: '1.28'
              agentPoolProfiles: [
                {
                  name: 'default'
                  count: 3
                  vmSize: 'Standard_DS2_v2'
                  mode: 'System'
                  osType: 'Linux'
                  enableAutoScaling: true
                  minCount: 1
                  maxCount: 5
                }
              ]
              networkProfile: {
                networkPlugin: 'azure'
                serviceCidr: '10.0.0.0/16'
                dnsServiceIP: '10.0.0.10'
              }
            }
          }
          ```
          
          ## ACR Pull Role Assignment
          
          ```bicep
          resource acrPullRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(aks.id, containerRegistry.id, 'acrpull')
            scope: containerRegistry
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
              principalId: aks.properties.identityProfile.kubeletidentity.objectId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          ## Node Pool Configuration
          
          ### System Pool (Required)
          
          ```bicep
          {
            name: 'system'
            count: 3
            vmSize: 'Standard_DS2_v2'
            mode: 'System'
            osType: 'Linux'
          }
          ```
          
          ### User Pool (Workloads)
          
          ```bicep
          {
            name: 'workload'
            count: 2
            vmSize: 'Standard_DS4_v2'
            mode: 'User'
            osType: 'Linux'
            enableAutoScaling: true
            minCount: 1
            maxCount: 10
          }
          ```
          
          ## Workload Identity
          
          ```bicep
          properties: {
            oidcIssuerProfile: {
              enabled: true
            }
            securityProfile: {
              workloadIdentity: {
                enabled: true
              }
            }
          }
          ```
          
        • manifests.md 1.4 KB
          # AKS - Kubernetes Manifests
          
          ## Deployment
          
          ```yaml
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: my-service
          spec:
            replicas: 3
            selector:
              matchLabels:
                app: my-service
            template:
              metadata:
                labels:
                  app: my-service
              spec:
                containers:
                - name: my-service
                  image: myacr.azurecr.io/my-service:latest
                  ports:
                  - containerPort: 8080
                  resources:
                    requests:
                      memory: "128Mi"
                      cpu: "100m"
                    limits:
                      memory: "256Mi"
                      cpu: "500m"
                  livenessProbe:
                    httpGet:
                      path: /health
                      port: 8080
                    initialDelaySeconds: 10
                  readinessProbe:
                    httpGet:
                      path: /ready
                      port: 8080
                    initialDelaySeconds: 5
          ```
          
          ## Service
          
          ```yaml
          apiVersion: v1
          kind: Service
          metadata:
            name: my-service
          spec:
            selector:
              app: my-service
            ports:
            - port: 80
              targetPort: 8080
            type: ClusterIP
          ```
          
          ## Ingress
          
          ```yaml
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          metadata:
            name: my-ingress
            annotations:
              kubernetes.io/ingress.class: azure/application-gateway
          spec:
            rules:
            - host: myapp.example.com
              http:
                paths:
                - path: /
                  pathType: Prefix
                  backend:
                    service:
                      name: my-service
                      port:
                        number: 80
          ```
          
        • README.md 1.1 KB
          # Azure Kubernetes Service (AKS)
          
          Full Kubernetes orchestration for complex containerized workloads.
          
          ## When to Use
          
          - Complex microservices requiring Kubernetes orchestration
          - Teams with Kubernetes expertise
          - Workloads needing fine-grained infrastructure control
          - Multi-container pods with sidecars
          - Custom networking requirements
          - Hybrid/multi-cloud Kubernetes strategies
          
          ## Service Type in azure.yaml
          
          ```yaml
          services:
            my-service:
              host: aks
              project: ./src/my-service
              docker:
                path: ./Dockerfile
              k8s:
                deploymentPath: ./k8s
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | Container Registry | Image storage |
          | Log Analytics Workspace | Monitoring |
          | Virtual Network | Network isolation (optional) |
          | Key Vault | Secrets management |
          
          ## Node Pool Types
          
          | Pool | Purpose |
          |------|---------|
          | System | Cluster infrastructure, 3 nodes minimum |
          | User | Application workloads, auto-scaling |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [K8s Manifests](manifests.md)
          - [Add-ons](addons.md)
          
      • app-insights
        • README.md 880 B
          # App Insights
          
          Azure Application Insights for telemetry, monitoring, and APM.
          
          ## When to Add
          
          - User wants observability/monitoring
          - User mentions telemetry, tracing, or logging
          - Production apps needing health visibility
          
          ## Implementation
          
          > **→ Invoke the `appinsights-instrumentation` skill**
          >
          > This skill has detailed guides for:
          > - Auto-instrumentation (ASP.NET Core on App Service)
          > - Manual instrumentation (Node.js, Python, C#)
          > - Bicep templates and CLI scripts
          
          ## Quick Reference
          
          | Aspect | Value |
          |--------|-------|
          | Resource | `Microsoft.Insights/components` |
          | Depends on | Log Analytics Workspace |
          | SKU | PerGB2018 (consumption-based) |
          
          ## Architecture Notes
          
          - Create in same resource group as the app
          - Connect to centralized Log Analytics Workspace
          - Use connection string (not instrumentation key) for new apps
          
      • app-service
        • templates
          • recipes
            • auth
              • source
                • dotnet.md 1.3 KB
                  # Auth Recipe — C# (.NET) — REFERENCE ONLY
                  
                  ## Microsoft Identity Web (ASP.NET Core)
                  
                  ### NuGet Packages
                  
                  ```xml
                  <PackageReference Include="Microsoft.Identity.Web" Version="3.*" />
                  ```
                  
                  ### Program.cs (additions)
                  
                  Add these lines — do NOT replace the existing file:
                  
                  ```csharp
                  using Microsoft.Identity.Web;
                  
                  // Add after builder creation
                  builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);
                  builder.Services.AddAuthorization();
                  
                  // Add after app build
                  app.UseAuthentication();
                  app.UseAuthorization();
                  
                  // Protected endpoint
                  app.MapGet("/api/me", [Authorize] (HttpContext ctx) =>
                  {
                      var name = ctx.User.FindFirst("name")?.Value;
                      return Results.Ok(new { name });
                  });
                  ```
                  
                  ### appsettings.json
                  
                  ```json
                  {
                    "AzureAd": {
                      "Instance": "https://login.microsoftonline.com/",
                      "TenantId": "<tenant-id>",
                      "ClientId": "<client-id>",
                      "Audience": "api://<client-id>"
                    }
                  }
                  ```
                  
                  > ⚠️ The `Audience` must match the Application ID URI of the app registration (typically `api://<client-id>`). Set `AZURE_CLIENT_ID` and `AZURE_TENANT_ID` as app settings in Azure rather than hardcoding them.
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `Program.cs` | Add authentication middleware + protected endpoints |
                  | `appsettings.json` | Add AzureAd configuration section |
                  | `*.csproj` | Add Microsoft.Identity.Web NuGet package |
                  
                • nodejs.md 2.5 KB
                  # Auth Recipe — Node.js (Express) — REFERENCE ONLY
                  
                  ## JWT Validation with jsonwebtoken + jwks-rsa
                  
                  ### npm Packages
                  
                  ```bash
                  npm install jsonwebtoken jwks-rsa
                  ```
                  
                  ### Auth Middleware
                  
                  Add `middleware/auth.js`:
                  
                  ```javascript
                  const jwt = require("jsonwebtoken");
                  const jwksClient = require("jwks-rsa");
                  
                  const client = jwksClient({
                    jwksUri: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}/discovery/v2.0/keys`,
                    cache: true,
                    rateLimit: true,
                  });
                  
                  // Use APP_ID_URI if set (e.g., "api://<client-id>"); fall back to CLIENT_ID
                  const AUDIENCE = process.env.AZURE_APP_ID_URI || process.env.AZURE_CLIENT_ID;
                  
                  function authMiddleware(req, res, next) {
                    const token = req.headers.authorization?.split(" ")[1];
                    if (!token) return res.status(401).json({ error: "No token" });
                  
                    const decoded = jwt.decode(token, { complete: true });
                    if (!decoded || !decoded.header || !decoded.header.kid) {
                      return res.status(401).json({ error: "Invalid token" });
                    }
                  
                    client.getSigningKey(decoded.header.kid, (err, key) => {
                      if (err || !key) {
                        return res.status(401).json({ error: "Invalid token" });
                      }
                  
                      jwt.verify(
                        token,
                        key.getPublicKey(),
                        {
                          algorithms: ["RS256"],
                          audience: AUDIENCE,
                          issuer: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}/v2.0`,
                        },
                        (err, payload) => {
                          if (err) return res.status(401).json({ error: "Invalid token" });
                          req.user = payload;
                          next();
                        }
                      );
                    });
                  }
                  
                  module.exports = { authMiddleware };
                  ```
                  
                  > ⚠️ The `aud` claim in Entra ID tokens is often the Application ID URI (`api://<client-id>`), not the raw client ID. Set `AZURE_APP_ID_URI` in app settings to match your app registration's exposed API URI.
                  
                  ### Protected Endpoint
                  
                  Add to `src/index.js`:
                  
                  ```javascript
                  const { authMiddleware } = require("./middleware/auth");
                  
                  app.get("/api/me", authMiddleware, (req, res) => {
                    res.json({ name: req.user?.name, oid: req.user?.oid });
                  });
                  ```
                  
                  ## App Settings Required
                  
                  | Setting | Value |
                  |---------|-------|
                  | `AZURE_TENANT_ID` | Entra tenant ID |
                  | `AZURE_CLIENT_ID` | App registration client ID |
                  | `AZURE_APP_ID_URI` | Application ID URI (e.g., `api://<client-id>`) — optional, defaults to CLIENT_ID |
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `middleware/auth.js` | Create — JWT validation middleware |
                  | `src/index.js` | Modify — add protected routes |
                  | `package.json` | Modify — add jsonwebtoken, jwks-rsa |
                  
                • python.md 2.2 KB
                  # Auth Recipe — Python (FastAPI) — REFERENCE ONLY
                  
                  ## JWT Validation with PyJWT
                  
                  ### Requirements
                  
                  Add to `requirements.txt`:
                  
                  ```
                  PyJWT[crypto]>=2.8
                  cryptography
                  fastapi
                  uvicorn
                  ```
                  
                  ### Token Validation Middleware
                  
                  Add `auth.py`:
                  
                  ```python
                  import os
                  from fastapi import Depends, HTTPException
                  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
                  import jwt
                  from jwt import PyJWKClient
                  
                  security = HTTPBearer()
                  TENANT_ID = os.environ["AZURE_TENANT_ID"]
                  CLIENT_ID = os.environ["AZURE_CLIENT_ID"]
                  # Use APP_ID_URI if set (e.g., "api://<client-id>"); fall back to CLIENT_ID
                  AUDIENCE = os.environ.get("AZURE_APP_ID_URI", CLIENT_ID)
                  JWKS_URL = f"https://login.microsoftonline.com/{TENANT_ID}/discovery/v2.0/keys"
                  
                  jwks_client = PyJWKClient(JWKS_URL)
                  
                  async def validate_token(creds: HTTPAuthorizationCredentials = Depends(security)):
                      try:
                          signing_key = jwks_client.get_signing_key_from_jwt(creds.credentials)
                          payload = jwt.decode(
                              creds.credentials,
                              signing_key.key,
                              algorithms=["RS256"],
                              audience=AUDIENCE,
                              issuer=f"https://login.microsoftonline.com/{TENANT_ID}/v2.0",
                          )
                          return payload
                      except jwt.InvalidTokenError:
                          raise HTTPException(status_code=401, detail="Invalid token")
                  ```
                  
                  > ⚠️ The `aud` claim in Entra ID tokens is often the Application ID URI (`api://<client-id>`), not the raw client ID. Set `AZURE_APP_ID_URI` in app settings to match your app registration's exposed API URI.
                  
                  ### Protected Endpoint
                  
                  Add to `main.py`:
                  
                  ```python
                  from auth import validate_token
                  
                  @app.get("/api/me")
                  async def me(user=Depends(validate_token)):
                      return {"name": user.get("name"), "oid": user.get("oid")}
                  ```
                  
                  ## App Settings Required
                  
                  | Setting | Value |
                  |---------|-------|
                  | `AZURE_TENANT_ID` | Entra tenant ID |
                  | `AZURE_CLIENT_ID` | App registration client ID |
                  | `AZURE_APP_ID_URI` | Application ID URI (e.g., `api://<client-id>`) — optional, defaults to CLIENT_ID |
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `auth.py` | Create — JWT validation middleware |
                  | `main.py` | Modify — add protected endpoints |
                  | `requirements.txt` | Modify — add PyJWT, cryptography |
                  
              • README.md 3.1 KB
                # Entra ID / Easy Auth Recipe — REFERENCE ONLY
                
                Adds authentication and authorization to an App Service base template using Microsoft Entra ID.
                
                ## Overview
                
                This recipe configures authentication for App Service apps using either Easy Auth (built-in authentication) or MSAL SDK-based authentication. Easy Auth requires zero code changes; MSAL gives full control.
                
                ## Integration Type
                
                | Aspect | Value |
                |--------|-------|
                | **Provider** | Microsoft Entra ID (Azure AD) |
                | **Method** | Easy Auth (built-in) or MSAL SDK |
                | **Protocols** | OpenID Connect, OAuth 2.0 |
                | **Token validation** | Automatic (Easy Auth) or middleware (MSAL) |
                
                ## Option A: Easy Auth (Recommended for most apps)
                
                Zero-code authentication built into App Service. Handles login, token management, and session cookies.
                
                ### Bicep Configuration
                
                > 💡 Call `mcp_bicep_get_az_resource_type_schema` with resource type `Microsoft.Web/sites/config` to validate properties before generating this resource.
                
                ```bicep
                resource authSettings 'Microsoft.Web/sites/config@2023-12-01' = {
                  parent: webApp
                  name: 'authsettingsV2'
                  properties: {
                    globalValidation: {
                      requireAuthentication: true
                      unauthenticatedClientAction: 'RedirectToLoginPage'
                    }
                    identityProviders: {
                      azureActiveDirectory: {
                        enabled: true
                        registration: {
                          openIdIssuer: 'https://login.microsoftonline.com/${tenant().tenantId}/v2.0'
                          clientId: appRegistration.properties.appId
                        }
                        validation: {
                          defaultAuthorizationPolicy: {
                            allowedApplications: []
                          }
                        }
                      }
                    }
                    login: {
                      tokenStore: {
                        enabled: true
                      }
                    }
                  }
                }
                ```
                
                ### App Registration
                
                > 💡 Call `mcp_bicep_get_az_resource_type_schema` with resource type `Microsoft.Graph/applications` to validate properties before generating this resource. The `microsoftGraphV1_0` extension is required — declare it at the top of the Bicep file.
                
                ```bicep
                extension microsoftGraphV1_0
                
                resource appRegistration 'Microsoft.Graph/applications@v1.0' = {
                  displayName: '${name}-app'
                  web: {
                    redirectUris: [
                      'https://${webApp.properties.defaultHostName}/.auth/login/aad/callback'
                    ]
                  }
                }
                ```
                
                ## Option B: MSAL SDK (Full control)
                
                Use when you need custom token validation, API-only auth, or multi-tenant support.
                
                | Language | Source File |
                |----------|-------------|
                | C# (ASP.NET Core) | [source/dotnet.md](source/dotnet.md) |
                | Python (FastAPI) | [source/python.md](source/python.md) |
                | Node.js (Express) | [source/nodejs.md](source/nodejs.md) |
                
                ## App Settings
                
                | Setting | Value | Purpose |
                |---------|-------|---------|
                | `AZURE_TENANT_ID` | Entra tenant ID | Identity provider |
                | `AZURE_CLIENT_ID` | App registration client ID | Application identity |
                
                ## References
                
                - [Easy Auth overview](https://learn.microsoft.com/en-us/azure/app-service/overview-authentication-authorization)
                - [Microsoft Identity Web](https://learn.microsoft.com/en-us/entra/msal/dotnet/microsoft-identity-web/)
                - [Configure Entra ID auth](https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad)
                
            • cosmos
              • source
                • dotnet.md 1.3 KB
                  # Cosmos DB Recipe — C# (.NET) — REFERENCE ONLY
                  
                  ## Cosmos DB SDK Setup
                  
                  ### NuGet Packages
                  
                  ```xml
                  <PackageReference Include="Microsoft.Azure.Cosmos" Version="3.*" />
                  <PackageReference Include="Azure.Identity" Version="1.13.*" />
                  ```
                  
                  ### Program.cs (additions)
                  
                  Add these lines — do NOT replace the existing file:
                  
                  ```csharp
                  using Azure.Identity;
                  using Microsoft.Azure.Cosmos;
                  
                  builder.Services.AddSingleton(_ =>
                  {
                      var endpoint = builder.Configuration["COSMOS_ENDPOINT"];
                      return new CosmosClient(endpoint, new DefaultAzureCredential());
                  });
                  ```
                  
                  ### CRUD Endpoints
                  
                  Add to `Program.cs` after `app` is built:
                  
                  ```csharp
                  app.MapGet("/api/items", async (CosmosClient cosmos) =>
                  {
                      var container = cosmos
                          .GetDatabase(Environment.GetEnvironmentVariable("COSMOS_DATABASE_NAME"))
                          .GetContainer(Environment.GetEnvironmentVariable("COSMOS_CONTAINER_NAME"));
                      var query = container.GetItemQueryIterator<dynamic>("SELECT * FROM c");
                      var results = new List<dynamic>();
                      while (query.HasMoreResults)
                          results.AddRange(await query.ReadNextAsync());
                      return Results.Ok(results);
                  });
                  ```
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `Program.cs` | Add CosmosClient registration + endpoints |
                  | `*.csproj` | Add Microsoft.Azure.Cosmos NuGet package |
                  
                • nodejs.md 1.2 KB
                  # Cosmos DB Recipe — Node.js — REFERENCE ONLY
                  
                  ## Cosmos SDK Setup
                  
                  ### npm Packages
                  
                  ```bash
                  npm install @azure/cosmos @azure/identity
                  ```
                  
                  ### Database Module
                  
                  Create `src/cosmosClient.js`:
                  
                  ```javascript
                  const { CosmosClient } = require("@azure/cosmos");
                  const { DefaultAzureCredential } = require("@azure/identity");
                  
                  const client = new CosmosClient({
                    endpoint: process.env.COSMOS_ENDPOINT,
                    aadCredentials: new DefaultAzureCredential(),
                  });
                  
                  const container = client
                    .database(process.env.COSMOS_DATABASE_NAME)
                    .container(process.env.COSMOS_CONTAINER_NAME);
                  
                  module.exports = { container };
                  ```
                  
                  ### CRUD Endpoints
                  
                  Add to `src/index.js`:
                  
                  ```javascript
                  const { container } = require("./cosmosClient");
                  
                  app.get("/api/items", async (req, res) => {
                    const { resources } = await container.items.readAll().fetchAll();
                    res.json(resources);
                  });
                  
                  app.post("/api/items", async (req, res) => {
                    const { resource } = await container.items.create(req.body);
                    res.status(201).json(resource);
                  });
                  ```
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `src/cosmosClient.js` | Create — Cosmos client + container reference |
                  | `src/index.js` | Modify — add CRUD endpoints |
                  | `package.json` | Modify — add @azure/cosmos, @azure/identity |
                  
                • python.md 1.1 KB
                  # Cosmos DB Recipe — Python — REFERENCE ONLY
                  
                  ## Cosmos SDK Setup
                  
                  ### Requirements
                  
                  Add to `requirements.txt`:
                  
                  ```
                  azure-cosmos>=4.7
                  azure-identity
                  ```
                  
                  ### Database Module
                  
                  Create `cosmos_client.py`:
                  
                  ```python
                  import os
                  from azure.cosmos import CosmosClient
                  from azure.identity import DefaultAzureCredential
                  
                  credential = DefaultAzureCredential()
                  client = CosmosClient(os.environ["COSMOS_ENDPOINT"], credential)
                  database = client.get_database_client(os.environ["COSMOS_DATABASE_NAME"])
                  container = database.get_container_client(os.environ["COSMOS_CONTAINER_NAME"])
                  ```
                  
                  ### CRUD Endpoints
                  
                  Add to `main.py`:
                  
                  ```python
                  from cosmos_client import container
                  
                  @app.get("/api/items")
                  async def list_items():
                      items = list(container.read_all_items())
                      return items
                  
                  @app.post("/api/items", status_code=201)
                  async def create_item(item: dict):
                      return container.create_item(body=item)
                  ```
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `cosmos_client.py` | Create — Cosmos client + container reference |
                  | `main.py` | Modify — add CRUD endpoints |
                  | `requirements.txt` | Modify — add azure-cosmos, azure-identity |
                  
              • README.md 3.6 KB
                # Cosmos DB Recipe — REFERENCE ONLY
                
                Adds Azure Cosmos DB (NoSQL) integration to an App Service base template.
                
                ## Overview
                
                This recipe composes with a Web API or Web App base template to add Cosmos DB data access. It provides the IaC delta (Cosmos account, database, container, RBAC) and per-language source code using the Cosmos DB SDK.
                
                ## Integration Type
                
                | Aspect | Value |
                |--------|-------|
                | **Database** | Azure Cosmos DB for NoSQL |
                | **Auth** | Managed identity (DefaultAzureCredential) |
                | **SDK** | Microsoft.Azure.Cosmos (.NET), @azure/cosmos (Node.js), azure-cosmos (Python) |
                | **Hosting** | App Service (from base template) |
                | **Local Auth** | Disabled (`disableLocalAuth: true`) — RBAC-only |
                
                ## Composition Steps
                
                Apply these steps AFTER `azd init -t <base-template>`:
                
                | # | Step | Details |
                |---|------|---------|
                | 1 | **Add IaC module** | Add Cosmos DB Bicep module to `infra/app/` |
                | 2 | **Wire into main** | Add module reference in `main.bicep` |
                | 3 | **Add app settings** | Add Cosmos endpoint + database + container settings |
                | 4 | **Add source code** | Add Cosmos client setup from `source/{lang}.md` |
                | 5 | **Add packages** | Add Cosmos SDK + Azure Identity packages |
                
                ## App Settings
                
                | Setting | Value | Purpose |
                |---------|-------|---------|
                | `COSMOS_ENDPOINT` | `https://{account}.documents.azure.com:443/` | Cosmos account endpoint |
                | `COSMOS_DATABASE_NAME` | `app-db` | Target database |
                | `COSMOS_CONTAINER_NAME` | `items` | Target container |
                
                ### Bicep App Settings Block
                
                ```bicep
                appSettings: [
                  { name: 'COSMOS_ENDPOINT', value: cosmos.outputs.endpoint }
                  { name: 'COSMOS_DATABASE_NAME', value: cosmos.outputs.databaseName }
                  { name: 'COSMOS_CONTAINER_NAME', value: cosmos.outputs.containerName }
                ]
                ```
                
                > **Note:** No connection string or key is needed. The SDK uses `DefaultAzureCredential` which automatically resolves to the app's managed identity in Azure.
                
                ## RBAC Roles Required
                
                | Role | GUID | Scope | Purpose |
                |------|------|-------|---------|
                | **Cosmos DB Account Reader** | `fbdf93bf-df7d-467e-a4d2-9458aa1360c8` | Cosmos account | Read account metadata |
                | **Cosmos DB Built-in Data Contributor** | `00000000-0000-0000-0000-000000000002` | Cosmos account (SQL role) | Read/write data |
                
                > **Important:** Cosmos DB uses its own SQL RBAC system (`sqlRoleAssignments`) for data plane operations, not standard Azure RBAC.
                
                ## Networking (when VNET_ENABLED=true)
                
                | Component | Details |
                |-----------|---------|
                | **Private endpoint** | Cosmos account → App Service VNet subnet |
                | **Private DNS zone** | `privatelink.documents.azure.com` |
                
                ## Resources Created
                
                | Resource | Type | Purpose |
                |----------|------|---------|
                | Cosmos DB Account | `Microsoft.DocumentDB/databaseAccounts` | Serverless NoSQL database |
                | SQL Database | `databaseAccounts/sqlDatabases` | Application database |
                | Container | `sqlDatabases/containers` | Data container with partition key |
                | Role Assignment | `Microsoft.Authorization/roleAssignments` | Control plane access |
                | SQL Role Assignment | `databaseAccounts/sqlRoleAssignments` | Data plane access |
                
                ## Source Code Examples
                
                | Language | Source File |
                |----------|-------------|
                | C# (.NET) | [source/dotnet.md](source/dotnet.md) |
                | Python | [source/python.md](source/python.md) |
                | Node.js | [source/nodejs.md](source/nodejs.md) |
                
                ## References
                
                - [Cosmos DB + App Service tutorial](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/tutorial-dotnet-web-app)
                - [Passwordless Cosmos DB connections](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-dotnet-get-started)
                - [Cosmos DB RBAC](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac)
                
            • redis
              • source
                • dotnet.md 1.6 KB
                  # Redis Recipe — C# (.NET) — REFERENCE ONLY
                  
                  ## ASP.NET Core Distributed Cache Setup
                  
                  ### NuGet Packages
                  
                  ```xml
                  <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.*" />
                  <PackageReference Include="Azure.Identity" Version="1.13.*" />
                  ```
                  
                  ### Program.cs (additions)
                  
                  Add these lines — do NOT replace the existing file:
                  
                  ```csharp
                  using Azure.Identity;
                  using StackExchange.Redis;
                  
                  var redisHost = builder.Configuration["REDIS_HOST"];
                  var configOptions = ConfigurationOptions.Parse($"{redisHost}:6380");
                  configOptions.Ssl = true;
                  configOptions.AbortOnConnectFail = false;
                  
                  // ConfigureForAzureWithTokenCredentialAsync handles automatic token renewal
                  await configOptions.ConfigureForAzureWithTokenCredentialAsync(
                      new DefaultAzureCredential());
                  
                  builder.Services.AddStackExchangeRedisCache(options =>
                  {
                      options.ConfigurationOptions = configOptions;
                      options.InstanceName = "app:";
                  });
                  ```
                  
                  > 💡 `ConfigureForAzureWithTokenCredentialAsync` manages token acquisition and renewal automatically — no manual token refresh required.
                  
                  ### Usage
                  
                  ```csharp
                  using Microsoft.Extensions.Caching.Distributed;
                  
                  app.MapGet("/api/cached", async (IDistributedCache cache) =>
                  {
                      var value = await cache.GetStringAsync("my-key");
                      if (value is null)
                      {
                          value = "computed-value";
                          await cache.SetStringAsync("my-key", value,
                              new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) });
                      }
                      return Results.Ok(new { value });
                  });
                  ```
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `Program.cs` | Add Redis cache registration |
                  | `*.csproj` | Add NuGet packages |
                  
                • nodejs.md 2.2 KB
                  # Redis Recipe — Node.js — REFERENCE ONLY
                  
                  ## ioredis with Token Refresh
                  
                  ### npm Packages
                  
                  ```bash
                  npm install ioredis @azure/identity
                  ```
                  
                  ### Cache Module
                  
                  Create `src/cache.js`:
                  
                  ```javascript
                  const Redis = require("ioredis");
                  const { DefaultAzureCredential } = require("@azure/identity");
                  
                  const credential = new DefaultAzureCredential();
                  const TOKEN_SCOPE = "https://redis.azure.com/.default";
                  // Redis ACL auth requires both username and token as password
                  const REDIS_USERNAME = "default";
                  const TOKEN_REFRESH_MARGIN = 5 * 60 * 1000; // 5 minutes in ms
                  
                  let _client = null;
                  let _tokenExpiry = 0;
                  
                  async function getToken() {
                    const tok = await credential.getToken(TOKEN_SCOPE);
                    _tokenExpiry = tok.expiresOnTimestamp;
                    return tok.token;
                  }
                  
                  async function getRedisClient() {
                    const now = Date.now();
                    if (_client && now < _tokenExpiry - TOKEN_REFRESH_MARGIN) {
                      return _client;
                    }
                  
                    // Token expired or about to expire — create a new client with a fresh token
                    if (_client) {
                      _client.disconnect();
                      _client = null;
                    }
                  
                    const token = await getToken();
                    _client = new Redis({
                      host: process.env.REDIS_HOST,
                      port: parseInt(process.env.REDIS_PORT || "6380"),
                      tls: { servername: process.env.REDIS_HOST },
                      username: REDIS_USERNAME,
                      password: token,
                      lazyConnect: true,
                    });
                    await _client.connect();
                    return _client;
                  }
                  
                  module.exports = { getRedisClient };
                  ```
                  
                  > ⚠️ Entra ID tokens expire in ~1 hour. `getRedisClient()` recreates the connection with a fresh token when the current one is within 5 minutes of expiry. Always call `getRedisClient()` before each operation rather than caching the client reference long-term.
                  
                  ### Usage
                  
                  ```javascript
                  const { getRedisClient } = require("./cache");
                  
                  app.get("/api/cached", async (req, res) => {
                    const client = await getRedisClient();
                    let value = await client.get("my-key");
                    if (!value) {
                      value = "computed-value";
                      await client.setex("my-key", 300, value); // TTL 5 minutes
                    }
                    res.json({ value });
                  });
                  ```
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `src/cache.js` | Create — Redis client with token refresh |
                  | `src/index.js` | Modify — use getRedisClient() |
                  | `package.json` | Modify — add ioredis, @azure/identity |
                  
                • python.md 2.9 KB
                  # Redis Recipe — Python — REFERENCE ONLY
                  
                  ## Redis Client with Token Refresh
                  
                  ### Requirements
                  
                  Add to `requirements.txt`:
                  
                  ```
                  redis>=5.0
                  azure-identity
                  ```
                  
                  ### Cache Module
                  
                  Create `cache.py`:
                  
                  ```python
                  import os
                  import time
                  import threading
                  import redis
                  from azure.identity import DefaultAzureCredential
                  
                  # Redis ACL auth requires both username and token as password
                  _REDIS_USERNAME = "default"
                  _TOKEN_SCOPE = "https://redis.azure.com/.default"
                  _TOKEN_REFRESH_MARGIN = 300  # Refresh 5 minutes before expiry
                  
                  _credential = DefaultAzureCredential()
                  _lock = threading.RLock()  # reentrant — get_cache() calls _get_token() while holding the lock
                  _token_cache: dict = {}
                  
                  
                  def _get_token():
                      """Return a valid Entra ID token, refreshing if within expiry margin."""
                      with _lock:
                          now = time.time()
                          if not _token_cache or now >= _token_cache["expires_on"] - _TOKEN_REFRESH_MARGIN:
                              tok = _credential.get_token(_TOKEN_SCOPE)
                              _token_cache["token"] = tok.token
                              _token_cache["expires_on"] = tok.expires_on
                          return _token_cache["token"]
                  
                  
                  def create_redis_client() -> redis.Redis:
                      """Create a Redis client. Token is refreshed on each call via _get_token()."""
                      return redis.Redis(
                          host=os.environ["REDIS_HOST"],
                          port=int(os.environ.get("REDIS_PORT", 6380)),
                          ssl=True,
                          username=_REDIS_USERNAME,
                          password=_get_token(),
                          decode_responses=True,
                      )
                  
                  
                  def get_cache() -> redis.Redis:
                      """Lazy/refreshing accessor — call this from request handlers instead of holding a module-level client.
                      Recreates the client when the cached token is within the refresh margin and closes the prior one."""
                      global _client
                      with _lock:
                          now = time.time()
                          if _client is None or now >= _token_cache.get("expires_on", 0) - _TOKEN_REFRESH_MARGIN:
                              old = _client
                              _client = create_redis_client()
                              if old is not None:
                                  try:
                                      old.close()
                                  except Exception:
                                      pass
                          return _client
                  
                  
                  _client: redis.Redis | None = None
                  ```
                  
                  > ⚠️ Entra ID tokens expire in ~1 hour. The `_get_token()` helper refreshes proactively 5 minutes before expiry, and `get_cache()` recreates the underlying client so in-flight pool connections pick up the new token. Always call `get_cache()` from request handlers — never cache the client at module scope across the token lifetime.
                  
                  ### Usage
                  
                  ```python
                  from cache import get_cache
                  
                  def get_cached(key: str):
                      cache = get_cache()
                      value = cache.get(key)
                      if value is None:
                          value = "computed-value"
                          cache.setex(key, 300, value)  # TTL 5 minutes
                      return value
                  ```
                  
                  ## Files to Modify
                  
                  | File | Action |
                  |------|--------|
                  | `cache.py` | Create — Redis client with token refresh |
                  | `main.py` | Modify — use cache client |
                  | `requirements.txt` | Modify — add redis, azure-identity |
                  
              • README.md 3.3 KB
                # Azure Cache for Redis Recipe — REFERENCE ONLY
                
                Adds Azure Cache for Redis integration to an App Service base template.
                
                ## Overview
                
                This recipe composes with a Web API or Web App base template to add distributed caching with Azure Cache for Redis. Uses managed identity for passwordless access.
                
                ## Integration Type
                
                | Aspect | Value |
                |--------|-------|
                | **Service** | Azure Cache for Redis |
                | **Auth** | Managed identity (Entra ID access policy) |
                | **SKU** | Basic C0 (dev) / Standard C1+ (production) |
                | **Protocol** | Redis 6.0+ with TLS |
                | **Local Auth** | Disabled — Entra ID only |
                
                ## Composition Steps
                
                Apply these steps AFTER `azd init -t <base-template>`:
                
                | # | Step | Details |
                |---|------|---------|
                | 1 | **Add IaC module** | Add Redis Bicep module to `infra/app/` |
                | 2 | **Wire into main** | Add module reference in `main.bicep` |
                | 3 | **Add app settings** | Add Redis host name + port settings |
                | 4 | **Add source code** | Add cache client setup from examples below |
                | 5 | **Add packages** | Add Redis client packages |
                
                ## App Settings
                
                | Setting | Value | Purpose |
                |---------|-------|---------|
                | `REDIS_HOST` | `{name}.redis.cache.windows.net` | Redis host name |
                | `REDIS_PORT` | `6380` | TLS port |
                
                ### Bicep App Settings Block
                
                ```bicep
                appSettings: [
                  { name: 'REDIS_HOST', value: redis.outputs.hostName }
                  { name: 'REDIS_PORT', value: '6380' }
                ]
                ```
                
                > **Note:** With managed identity, no access key is needed. The Redis client uses `DefaultAzureCredential` to obtain a token.
                
                ## RBAC Roles Required
                
                | Role | GUID | Scope | Purpose |
                |------|------|-------|---------|
                | **Redis Cache Contributor** | `e0f68234-74aa-48ed-b826-c38b57376e17` | Redis cache | Manage cache |
                
                > For data plane access with Entra ID, configure the Redis access policy to grant the managed identity `Data Owner` or `Data Contributor` access.
                
                ## Resources Created
                
                | Resource | Type | Purpose |
                |----------|------|---------|
                | Redis Cache | `Microsoft.Cache/redis` | Distributed cache |
                | Access Policy | `Microsoft.Cache/redis/accessPolicyAssignments` | MI data access |
                | Private Endpoint | `Microsoft.Network/privateEndpoints` | VNet-only access (conditional) |
                
                ## Source Code Examples
                
                | Language | Source File |
                |----------|-------------|
                | C# (ASP.NET Core) | [source/dotnet.md](source/dotnet.md) |
                | Python | [source/python.md](source/python.md) |
                | Node.js | [source/nodejs.md](source/nodejs.md) |
                
                > ⚠️ **Token expiry:** Entra ID access tokens expire in ~1 hour. The C# example uses `ConfigureForAzureWithTokenCredentialAsync` which handles automatic token renewal. Python and Node.js examples require a connection factory pattern — see the source files for a refresh-capable implementation.
                
                ## Networking (when VNET_ENABLED=true)
                
                | Component | Details |
                |-----------|---------|
                | **Private endpoint** | Redis → App Service VNet subnet |
                | **Private DNS zone** | `privatelink.redis.cache.windows.net` |
                
                ## References
                
                - [Azure Cache for Redis overview](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-overview)
                - [Use Entra ID with Redis](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-azure-active-directory-for-authentication)
                - [Redis + App Service tutorial](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-web-app-howto)
                
            • sql
              • source
                • dotnet.md 2.9 KB
                  # SQL Database — C# (.NET) — REFERENCE ONLY
                  
                  ## Entity Framework Core Setup
                  
                  Add EF Core with Azure SQL and managed identity support to an ASP.NET Core app.
                  
                  ### NuGet Packages
                  
                  ```xml
                  <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.*" />
                  <PackageReference Include="Azure.Identity" Version="1.13.*" />
                  <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.*" />
                  ```
                  
                  ### DbContext
                  
                  Create `Data/AppDbContext.cs`:
                  
                  ```csharp
                  using Microsoft.EntityFrameworkCore;
                  
                  namespace MyApp.Data;
                  
                  public class AppDbContext : DbContext
                  {
                      public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
                  
                      public DbSet<TodoItem> TodoItems => Set<TodoItem>();
                  }
                  
                  public class TodoItem
                  {
                      public int Id { get; set; }
                      public string Title { get; set; } = string.Empty;
                      public bool IsComplete { get; set; }
                  }
                  ```
                  
                  ### Program.cs (additions)
                  
                  Add these lines to `Program.cs` — do NOT replace the file:
                  
                  ```csharp
                  using Microsoft.EntityFrameworkCore;
                  using MyApp.Data;
                  
                  // Add after builder creation
                  var connectionString = builder.Configuration.GetConnectionString("AZURE_SQL")
                      ?? builder.Configuration["AZURE_SQL_CONNECTION_STRING"];
                  
                  builder.Services.AddDbContext<AppDbContext>(options =>
                      options.UseSqlServer(connectionString));
                  
                  // Add health check for SQL
                  builder.Services.AddHealthChecks()
                      .AddDbContextCheck<AppDbContext>();
                  
                  // After app build — map health checks so App Service probes reflect SQL connectivity
                  app.MapHealthChecks("/health");
                  ```
                  
                  ### API Endpoints
                  
                  Add to `Program.cs` after `app` is built:
                  
                  ```csharp
                  app.MapGet("/api/todos", async (AppDbContext db) =>
                      await db.TodoItems.ToListAsync());
                  
                  app.MapGet("/api/todos/{id}", async (int id, AppDbContext db) =>
                      await db.TodoItems.FindAsync(id) is TodoItem todo
                          ? Results.Ok(todo)
                          : Results.NotFound());
                  
                  app.MapPost("/api/todos", async (TodoItem todo, AppDbContext db) =>
                  {
                      db.TodoItems.Add(todo);
                      await db.SaveChangesAsync();
                      return Results.Created($"/api/todos/{todo.Id}", todo);
                  });
                  ```
                  
                  ### appsettings.json
                  
                  ```json
                  {
                    "ConnectionStrings": {
                      "AZURE_SQL": "Server=localhost;Database=myapp;Trusted_Connection=true;"
                    }
                  }
                  ```
                  
                  > In production, the `AZURE_SQL_CONNECTION_STRING` app setting from Azure overrides this with the managed identity connection string.
                  
                  ### EF Migrations (postprovision hook)
                  
                  Create `infra/scripts/setup-db.sh`:
                  
                  ```bash
                  #!/bin/bash
                  dotnet ef database update --project src/api
                  ```
                  
                  ## Files to Add
                  
                  | File | Action |
                  |------|--------|
                  | `Data/AppDbContext.cs` | Create — DbContext + entity models |
                  | `Program.cs` | Modify — add DbContext registration + endpoints |
                  | `appsettings.json` | Modify — add ConnectionStrings section |
                  
                  ## Common Patterns
                  
                  - Always use `AddHealthChecks().AddDbContextCheck<>()` for SQL health monitoring
                  - Use `AsNoTracking()` for read-only queries to improve performance
                  - Apply migrations via postprovision hook, not at app startup
                  
                • nodejs.md 3 KB
                  # SQL Database — Node.js — REFERENCE ONLY
                  
                  ## Prisma + Azure SQL Setup
                  
                  ### npm Packages
                  
                  ```bash
                  npm install prisma @prisma/client mssql
                  npm install -D prisma
                  npx prisma init
                  ```
                  
                  ### Prisma Schema
                  
                  Update `prisma/schema.prisma`:
                  
                  ```prisma
                  datasource db {
                    provider = "sqlserver"
                    url      = env("DATABASE_URL")
                  }
                  
                  generator client {
                    provider = "prisma-client-js"
                  }
                  
                  model TodoItem {
                    id         Int     @id @default(autoincrement())
                    title      String  @db.NVarChar(200)
                    isComplete Boolean @default(false)
                  }
                  ```
                  
                  ### Database Client
                  
                  Create `src/db.js`:
                  
                  ```javascript
                  const { PrismaClient } = require("@prisma/client");
                  
                  const prisma = new PrismaClient();
                  module.exports = { prisma };
                  ```
                  
                  ### API Endpoints
                  
                  Add to `src/index.js`:
                  
                  ```javascript
                  const { prisma } = require("./db");
                  
                  app.get("/api/todos", async (req, res) => {
                    const todos = await prisma.todoItem.findMany();
                    res.json(todos);
                  });
                  
                  app.post("/api/todos", async (req, res) => {
                    const body = req.body || {};
                    const title = typeof body.title === "string" ? body.title.trim() : "";
                    if (!title) {
                      return res.status(400).json({ error: "title is required and must be a non-empty string" });
                    }
                    const isComplete = body.isComplete === undefined ? false : body.isComplete;
                    if (typeof isComplete !== "boolean") {
                      return res.status(400).json({ error: "isComplete must be a boolean" });
                    }
                    const todo = await prisma.todoItem.create({ data: { title, isComplete } });
                    res.status(201).json(todo);
                  });
                  
                  app.get("/api/todos/:id", async (req, res) => {
                    const todo = await prisma.todoItem.findUnique({
                      where: { id: parseInt(req.params.id) },
                    });
                    if (!todo) return res.status(404).json({ error: "Not found" });
                    res.json(todo);
                  });
                  ```
                  
                  ### Connection String
                  
                  **Azure (managed identity):**
                  
                  Set as app setting in Bicep — the format Prisma uses for SQL Server:
                  ```
                  sqlserver://<server>.database.windows.net:1433;database=<db>;authentication=ActiveDirectoryMsi;clientId=<mi-client-id>
                  ```
                  
                  ```bicep
                  { name: 'DATABASE_URL', value: 'sqlserver://${sqlServer.properties.fullyQualifiedDomainName}:1433;database=${sqlDatabase.name};authentication=ActiveDirectoryMsi;clientId=${managedIdentity.properties.clientId}' }
                  ```
                  
                  **Local development (SQL auth):**
                  
                  ```bash
                  # .env (local only — never commit)
                  DATABASE_URL="sqlserver://localhost:1433;database=myapp;user=<username>;password=<your-strong-password>;trustServerCertificate=true"
                  ```
                  
                  ### EF Migrations (postprovision hook)
                  
                  Create `infra/scripts/setup-db.sh`:
                  
                  ```bash
                  #!/bin/bash
                  npx prisma migrate deploy --schema src/prisma/schema.prisma
                  ```
                  
                  ## Files to Add
                  
                  | File | Action |
                  |------|--------|
                  | `prisma/schema.prisma` | Create — data model definitions |
                  | `src/db.js` | Create — Prisma client instance |
                  | `src/index.js` | Modify — add CRUD endpoints |
                  | `package.json` | Modify — add prisma, @prisma/client |
                  
                  ## Common Patterns
                  
                  - Use `prisma.todoItem.findMany()` for read-only queries
                  - Use `prisma.$disconnect()` in shutdown hooks for clean teardown
                  - Run `npx prisma generate` after schema changes to regenerate the client
                  
                • python.md 4.4 KB
                  # SQL Database — Python — REFERENCE ONLY
                  
                  ## SQLAlchemy Setup
                  
                  Add SQLAlchemy with Azure SQL and managed identity support to a FastAPI app.
                  
                  ### Requirements
                  
                  Add to `requirements.txt`:
                  
                  ```
                  sqlalchemy>=2.0
                  pyodbc
                  azure-identity
                  fastapi
                  uvicorn
                  ```
                  
                  ### Database Configuration
                  
                  Create `database.py`:
                  
                  ```python
                  import os
                  import struct
                  from sqlalchemy import create_engine, event
                  from sqlalchemy.orm import sessionmaker, DeclarativeBase
                  from azure.identity import ManagedIdentityCredential
                  
                  
                  class Base(DeclarativeBase):
                      pass
                  
                  
                  def create_db_engine():
                      """Create SQLAlchemy engine using managed identity or connection string."""
                      conn_str = os.environ.get("AZURE_SQL_CONNECTION_STRING")
                      if conn_str:
                          # Local dev: AZURE_SQL_CONNECTION_STRING is a SQLAlchemy URL
                          # e.g. mssql+pyodbc://sa:password@localhost/myapp?driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes
                          return create_engine(conn_str)
                  
                      # Azure: use managed identity token (refreshed per-connection so pool stays valid past 1 hour)
                      server = os.environ["AZURE_SQL_SERVER"]
                      database = os.environ["AZURE_SQL_DATABASE"]
                      client_id = os.environ.get("AZURE_CLIENT_ID")
                      credential = ManagedIdentityCredential(client_id=client_id) if client_id else ManagedIdentityCredential()
                  
                      odbc_conn = (
                          f"DRIVER={{ODBC Driver 18 for SQL Server}};"
                          f"SERVER={server};"
                          f"DATABASE={database};"
                          f"Encrypt=yes;TrustServerCertificate=no;"
                      )
                      engine = create_engine(
                          "mssql+pyodbc://",
                          connect_args={"odbc_connect": odbc_conn},
                      )
                  
                      @event.listens_for(engine, "do_connect")
                      def provide_token(dialect, conn_rec, cargs, cparams):
                          # Fetch a fresh token for every new physical connection — pool refills won't fail after 1h
                          token = credential.get_token("https://database.windows.net/.default")
                          token_bytes = token.token.encode("utf-16-le")
                          token_struct = struct.pack(f"<I{len(token_bytes)}s", len(token_bytes), token_bytes)
                          cparams["attrs_before"] = {1256: token_struct}  # SQL_COPT_SS_ACCESS_TOKEN
                  
                      return engine
                  
                  
                  engine = create_db_engine()
                  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
                  ```
                  
                  ### Models
                  
                  Create `models.py`:
                  
                  ```python
                  from sqlalchemy import Column, Integer, String, Boolean
                  from database import Base
                  
                  class TodoItem(Base):
                      __tablename__ = "todo_items"
                  
                      id = Column(Integer, primary_key=True, index=True)
                      title = Column(String(200), nullable=False)
                      is_complete = Column(Boolean, default=False)
                  ```
                  
                  ### API Endpoints
                  
                  Add to `main.py` — do NOT replace existing routes:
                  
                  ```python
                  from fastapi import Depends, HTTPException
                  from sqlalchemy.orm import Session
                  from database import SessionLocal, engine, Base
                  from models import TodoItem
                  from pydantic import BaseModel
                  
                  Base.metadata.create_all(bind=engine)
                  
                  def get_db():
                      db = SessionLocal()
                      try:
                          yield db
                      finally:
                          db.close()
                  
                  class TodoCreate(BaseModel):
                      title: str
                      is_complete: bool = False
                  
                  @app.get("/api/todos")
                  def list_todos(db: Session = Depends(get_db)):
                      return db.query(TodoItem).all()
                  
                  @app.get("/api/todos/{todo_id}")
                  def get_todo(todo_id: int, db: Session = Depends(get_db)):
                      todo = db.query(TodoItem).filter(TodoItem.id == todo_id).first()
                      if not todo:
                          raise HTTPException(status_code=404, detail="Not found")
                      return todo
                  
                  @app.post("/api/todos", status_code=201)
                  def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
                      db_todo = TodoItem(**todo.model_dump())
                      db.add(db_todo)
                      db.commit()
                      db.refresh(db_todo)
                      return db_todo
                  ```
                  
                  ### Local Development
                  
                  For local development without managed identity, use SQL authentication:
                  
                  ```python
                  # .env (local only — never commit)
                  AZURE_SQL_CONNECTION_STRING=mssql+pyodbc://<username>:<password>@localhost/myapp?driver=ODBC+Driver+18+for+SQL+Server&TrustServerCertificate=yes
                  ```
                  
                  ## Files to Add
                  
                  | File | Action |
                  |------|--------|
                  | `database.py` | Create — engine, session, managed identity token |
                  | `models.py` | Create — SQLAlchemy ORM models |
                  | `main.py` | Modify — add CRUD endpoints + DB dependency |
                  | `requirements.txt` | Modify — add sqlalchemy, pyodbc, azure-identity |
                  
                  ## Common Patterns
                  
                  - Use `Depends(get_db)` for session lifecycle management
                  - Use `Base.metadata.create_all()` only for dev; use Alembic migrations in production
                  - Never store SQL passwords in app settings — use managed identity tokens
                  
              • README.md 3.5 KB
                # SQL Database Recipe — REFERENCE ONLY
                
                Adds Azure SQL Database integration to an App Service base template.
                
                ## Overview
                
                This recipe composes with a Web API or Web App base template to add Azure SQL Database connectivity. It provides the IaC delta (SQL Server, database, firewall, RBAC) and per-language source code using EF Core, Prisma, or SQLAlchemy.
                
                ## Integration Type
                
                | Aspect | Value |
                |--------|-------|
                | **Database** | Azure SQL Database (Serverless or Provisioned) |
                | **Auth** | Managed identity (passwordless) |
                | **ORM** | EF Core (.NET), Prisma (Node.js), SQLAlchemy (Python) |
                | **Hosting** | App Service (from base template) |
                | **Local Auth** | Disabled in Azure (Entra ID only); local dev may use SQL auth |
                
                ## Composition Steps
                
                Apply these steps AFTER `azd init -t <base-template>`:
                
                | # | Step | Details |
                |---|------|---------|
                | 1 | **Add IaC module** | Add SQL Server + Database Bicep module to `infra/app/` |
                | 2 | **Wire into main** | Add module reference in `main.bicep` |
                | 3 | **Add app settings** | Add SQL connection string (managed identity) |
                | 4 | **Add source code** | Add ORM models, DbContext/client setup from `source/{lang}.md` |
                | 5 | **Add packages** | Add ORM and SQL client packages |
                | 6 | **Run migrations** | Add postprovision hook for DB schema setup |
                
                ## App Settings
                
                | Setting | Value | Purpose |
                |---------|-------|---------|
                | `AZURE_SQL_CONNECTION_STRING` | `Server=tcp:{server}.database.windows.net;Database={db};Authentication=Active Directory Managed Identity;User Id={clientId};` | Passwordless SQL connection |
                
                ### Bicep App Settings Block
                
                ```bicep
                appSettings: [
                  {
                    name: 'AZURE_SQL_CONNECTION_STRING'
                    value: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Managed Identity;User Id=${managedIdentity.properties.clientId};Encrypt=True;TrustServerCertificate=False;'
                  }
                ]
                ```
                
                > **Note:** The `Authentication=Active Directory Managed Identity` setting tells the SQL client to use the app's managed identity. No passwords are stored.
                
                ## RBAC Roles Required
                
                | Role | GUID | Scope | Purpose |
                |------|------|-------|---------|
                | **SQL DB Contributor** | `9b7fa17d-e63e-47b0-bb0a-15c516ac86ec` | SQL Server | Manage database |
                | **Directory Readers** | `88d8e3e3-8f55-4a1e-953a-9b9898b8876b` | Entra ID | Read directory for MI auth |
                
                > **Important:** Data plane access uses SQL-level roles (`db_datareader`, `db_datawriter`), assigned via a postprovision script that runs `ALTER ROLE` statements.
                
                ## Resources Created
                
                | Resource | Type | Purpose |
                |----------|------|---------|
                | SQL Server | `Microsoft.Sql/servers` | Logical SQL server |
                | SQL Database | `Microsoft.Sql/servers/databases` | Application database |
                | Firewall Rule | `Microsoft.Sql/servers/firewallRules` | Allow Azure services |
                | Entra Admin | `Microsoft.Sql/servers/administrators` | Set MI as admin |
                
                ## Files
                
                | Path | Description |
                |------|-------------|
                | [source/dotnet.md](source/dotnet.md) | C# EF Core integration |
                | [source/python.md](source/python.md) | Python SQLAlchemy integration |
                | [source/nodejs.md](source/nodejs.md) | Node.js Prisma integration |
                
                ## References
                
                - [Azure SQL passwordless connections](https://learn.microsoft.com/en-us/azure/azure-sql/database/azure-sql-passwordless-migration)
                - [EF Core with Azure SQL](https://learn.microsoft.com/en-us/ef/core/providers/sql-server/)
                - [Tutorial: App Service with SQL](https://learn.microsoft.com/en-us/azure/app-service/tutorial-dotnetcore-sqldb-app)
                
            • composition.md 4.8 KB
              # Composition Algorithm — REFERENCE ONLY
              
              Step-by-step algorithm for composing a base App Service template with an integration recipe.
              
              > **This is the authoritative process. Follow it exactly.**
              
              ## Algorithm
              
              ```
              INPUT:
                - language:    dotnet | typescript | javascript | python | java
                - scenario:    web-api | web-app
                - integration: none | sql | cosmos | auth | redis
                - iac:         bicep | terraform
              
              OUTPUT:
                - Complete project directory ready for `azd up`
              ```
              
              ### Step 1: Fetch Base Template
              
              ```bash
              # Determine template by scenario + language
              IF scenario == 'web-api':
                TEMPLATE = web_api_templates[language]    # See web-api.md
              ELSE IF scenario == 'web-app':
                TEMPLATE = web_app_templates[language]    # See web-app.md
              
              # Non-interactive init
              ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
              # PowerShell: $ENV_NAME = "$(Split-Path -Leaf (Get-Location) | ForEach-Object { $_.ToLower() -replace '[ _]','-' })-dev"
              azd init -t $TEMPLATE -e "$ENV_NAME" --no-prompt
              ```
              
              ### Step 2: Check if Recipe Needed
              
              ```
              IF integration IN [none]:
                → DONE. Base template is complete.
              
              IF integration IN [sql, cosmos, redis, auth]:
                → Full recipe. Continue to Step 3.
              ```
              
              ### Step 3: Add IaC Module (for full recipes only)
              
              **Bicep:**
              1. Read recipe's `README.md` for the Bicep module file
              2. Copy module into `infra/app/`
              3. Add module reference in `infra/main.bicep`:
                 ```bicep
                 module sqlServer './app/sql.bicep' = {
                   name: 'sqlServer'
                   scope: rg
                   params: {
                     name: name
                     location: location
                     tags: tags
                     appServicePrincipalId: web.outputs.SERVICE_WEB_IDENTITY_PRINCIPAL_ID
                   }
                 }
                 ```
              4. If VNET_ENABLED, add the network module for private endpoints
              
              **Terraform:**
              1. Copy recipe `.tf` files into `infra/`
              2. Merge app settings into web app resource block
              3. Networking uses `count = var.vnet_enabled ? 1 : 0`
              
              ### Step 4: Add App Settings
              
              Read the recipe's `README.md` for required app settings. Add them to the web app config.
              
              > **CRITICAL: Managed Identity Configuration**
              >
              > For service bindings, prefer User Assigned Managed Identity (UAMI).
              > Always include connection settings that reference managed identity, not passwords:
              >
              > ⚠️ **Connection-string format is language-specific.** The example below is **.NET / ADO.NET** format. For other stacks, use the per-language env vars documented in `recipes/sql/source/{language}.md`:
              >
              > | Language | Env var(s) | Format |
              > |---|---|---|
              > | .NET | `AZURE_SQL_CONNECTION_STRING` | `Server=...;Authentication=Active Directory Managed Identity;User Id=<clientId>;` (ADO.NET) |
              > | Python | `AZURE_SQL_SERVER`, `AZURE_SQL_DATABASE`, `AZURE_CLIENT_ID` | Code obtains MI access token and passes via ODBC `attrs_before` |
              > | Node.js | `DATABASE_URL` | `sqlserver://<host>:1433;database=<db>;authentication=ActiveDirectoryMsi;clientId=<clientId>` (Prisma) |
              
              > ```bicep
              > appSettings: [
              >   { name: 'AZURE_SQL_CONNECTION_STRING', value: 'Server=${sqlServer.properties.fullyQualifiedDomainName};Database=${dbName};Authentication=Active Directory Managed Identity;User Id=${managedIdentity.properties.clientId};' }
              > ]
              > ```
              
              ### Step 5: Add Source Code Integration
              
              1. Read `recipes/{integration}/source/{language}.md`
              2. Add the integration code (service client, middleware, configuration)
              3. Add package dependencies (NuGet, npm, pip, Maven)
              
              > ⛔ **Do NOT replace the main entry point file** (Program.cs, app.py, index.js).
              > Recipes ADD integration code alongside the existing application code.
              
              ### Step 6: Update azure.yaml (if needed)
              
              Some recipes require hooks:
              ```yaml
              hooks:
                postprovision:
                  posix:
                    shell: sh
                    run: ./infra/scripts/setup-db.sh
                  windows:
                    shell: pwsh
                    run: ./infra/scripts/setup-db.ps1
              ```
              
              ### Step 7: Validate and Deploy
              
              **Required Environment Setup:**
              ```bash
              azd env set AZURE_LOCATION eastus2
              ```
              
              **Deployment (two-phase recommended):**
              ```bash
              azd provision --no-prompt     # Create resources + RBAC assignments
              sleep 60                       # Wait for RBAC propagation
              azd deploy --no-prompt        # Deploy code (RBAC now active)
              ```
              
              > **CRITICAL: Never store database passwords in app settings.**
              > The correct approach is managed identity with passwordless connections.
              
              ## Critical Rules
              
              1. **NEVER synthesize Bicep or Terraform from scratch** — always start from base template IaC
              2. **Do not restructure or replace base IaC files** — only ADD recipe modules alongside them
              3. **ALWAYS use recipe RBAC role GUIDs** — never let the LLM guess role IDs
              4. **ALWAYS use `--no-prompt`** — the agent must never elicit user input during azd commands
              5. **ALWAYS include a health check endpoint** at `/health`
              6. **ALWAYS use managed identity** — no connection strings with passwords
              7. **ALWAYS tag the App Service** with `azd-service-name` matching `azure.yaml`
              
            • README.md 2.8 KB
              # App Service Template Recipes — REFERENCE ONLY
              
              Composable IaC + source code modules that extend the Web API or Web App base template to support specific Azure service integrations.
              
              ## Architecture
              
              ```
              Base Template (per language/scenario, from AZD gallery)
                     │
                     ├── Source code (REST API or full-stack web app)
                     ├── IaC (App Service Plan, App Service, App Insights, UAMI, RBAC)
                     └── AZD config (azure.yaml, parameters)
              
                     + Recipe (per integration)
                     │
                     ├── Source code delta (service client, middleware, config)
                     ├── IaC delta (new resource + RBAC + networking modules)
                     └── App settings delta
                     │
                     = Complete deployable project → `azd up`
              ```
              
              ## Available Recipes
              
              | Recipe | IaC Delta? | Source Delta? | Status |
              |--------|-----------|--------------|--------|
              | [sql](sql/README.md) | ✅ SQL Server + DB + firewall + RBAC | ✅ EF Core (.NET), SQLAlchemy (Python), Prisma (Node.js) · ⏳ Spring Data JPA (Java): planned | ✅ Available |
              | [cosmos](cosmos/README.md) | ✅ Cosmos account + DB + container + RBAC + PE | ✅ Cosmos SDK (.NET, Python, Node.js) · ⏳ Spring Data Cosmos (Java): planned | ✅ Available |
              | [auth](auth/README.md) | ✅ App registration + Easy Auth config | ✅ MSAL / Identity middleware (.NET, Python, Node.js) · ⏳ Spring Security (Java): planned | ✅ Available |
              | [redis](redis/README.md) | ✅ Redis cache + RBAC + PE | ✅ Distributed cache client (.NET, Python, Node.js) · ⏳ Spring Session (Java): planned | ✅ Available |
              
              ## How It Works
              
              ### Step 1: Fetch Base Template
              
              ```bash
              # Pick template by language + scenario (see selection.md)
              azd init -t <template> -e "$ENV_NAME" --no-prompt
              ```
              
              ### Step 2: Apply Recipe
              
              The skill reads the recipe's README.md for:
              - **IaC module files** to copy into `infra/`
              - **App settings** to add to web app configuration
              - **RBAC roles** with exact GUIDs (never generated by LLM)
              - **Source code** to add alongside existing application code
              - **Networking** to add private endpoints (conditional on VNET_ENABLED)
              
              ### Step 3: Wire Into Base
              
              **Bicep:** Add `module` reference in `main.bicep`, pass `appServicePrincipalId`
              **Terraform:** Copy `.tf` file into `infra/`, merge locals into web app settings
              
              ### Step 4: Deploy
              
              ```bash
              azd env set AZURE_LOCATION eastus2
              azd provision --no-prompt
              sleep 60
              azd deploy --no-prompt
              ```
              
              ## Design Principles
              
              | Principle | Why |
              |-----------|-----|
              | **Never synthesize base IaC** | Always use proven AZD template repos |
              | **Never modify base; only extend** | Recipes are additive — no risk of breaking core |
              | **Recipes own their RBAC** | Exact role GUIDs, no LLM guessing |
              | **Managed identity by default** | No passwords or connection strings in app settings |
              | **Health checks required** | Every app must expose `/health` |
              
          • selection.md 2.8 KB
            # Template Selection Decision Tree — REFERENCE ONLY
            
            **CRITICAL**: Check for specific scenario indicators IN ORDER before defaulting to Web API.
            
            **Architecture**: All deployments start from a base template per language/scenario. Integrations are applied as [composable recipes](recipes/README.md) on top of the base. See [composition.md](recipes/composition.md) for the merge algorithm.
            
            Cross-reference with [App Service overview](https://learn.microsoft.com/en-us/azure/app-service/overview) and [official AZD gallery templates](https://azure.github.io/awesome-azd/?tags=appservice).
            
            ```
            1. Is this a full-stack web app with server-side rendering?
               Indicators: Razor Pages, MVC views, Next.js SSR, Nuxt, Django templates,
                           server-rendered HTML, .cshtml, EJS/Pug, Jinja2
               └─► YES → web-app base template (see [web-app.md](web-app.md))
            
            2. Is this a background worker or WebJob?
               Indicators: WebJob, IHostedService, BackgroundService, worker,
                           queue processor, scheduled task (no HTTP endpoints)
               └─► YES → Use Worker Service template (out of scope — see Functions or Container Apps)
            
            3. Does it use Azure SQL or PostgreSQL?
               Indicators: DbContext, EF Core, Prisma, SQLAlchemy, connection string,
                           Microsoft.EntityFrameworkCore, @prisma/client, psycopg2
               └─► YES → Web API base + sql recipe (IaC + RBAC + source)
               Recipe: recipes/sql/ ✅ Available
            
            4. Does it use Cosmos DB?
               Indicators: CosmosClient, @azure/cosmos, azure-cosmos, CosmosDBConnection,
                           Microsoft.Azure.Cosmos, container.read_item
               └─► YES → Web API base + cosmos recipe (IaC + RBAC + source)
               Recipe: recipes/cosmos/ ✅ Available
            
            5. Does it require authentication?
               Indicators: [Authorize], Microsoft.Identity.Web, passport-azure-ad,
                           msal, EasyAuth, /.auth/login, authsettingsV2
               └─► YES → base + auth recipe (Easy Auth or MSAL config)
               Recipe: recipes/auth/ ✅ Available
            
            6. Does it use Redis caching?
               Indicators: IDistributedCache, StackExchange.Redis, ioredis, redis,
                           azure-cache-redis, aioredis
               └─► YES → base + redis recipe (IaC + RBAC + source)
               Recipe: recipes/redis/ ✅ Available
            
            7. DEFAULT → Web API base template by runtime (see [web-api.md](web-api.md))
            ```
            
            ## Base Templates
            
            | Scenario | Template Reference |
            |----------|-------------------|
            | Full-stack web app with server-side rendering | [web-app.md](web-app.md) |
            | REST API / headless backend (default) | [web-api.md](web-api.md) |
            
            ## Recipe Types
            
            | Type | IaC Delta? | Examples |
            |------|-----------|----------|
            | **Full recipe** | Yes — Bicep module + RBAC + networking | sql, cosmos, redis, auth |
            | **Source-only** | No — only application code patterns | health checks |
            
            ## Critical Rules
            
            > See [Critical Rules](recipes/composition.md#critical-rules) in composition.md (canonical).
            
          • web-api.md 4.9 KB
            # Web API Base Template — REFERENCE ONLY
            
            Default template for REST API workloads on Azure App Service. Use when no specific integration is detected or as the base for recipe composition.
            
            ## Templates by Runtime
            
            | Runtime | AZD Template | Framework |
            |---------|-------------|-----------|
            | C# (.NET) | `azd init -t todo-csharp` | ASP.NET Core Minimal API |
            | Node.js (JS) | `azd init -t todo-nodejs-mongo` | Express.js |
            | Node.js (TS) | `azd init -t todo-nodejs-mongo` | Express.js (TypeScript) |
            | Python | `azd init -t todo-python-mongo` | FastAPI / Flask |
            | Java | `azd init -t todo-java-mongo` | Spring Boot |
            
            **Browse all:** [Awesome AZD App Service](https://azure.github.io/awesome-azd/?tags=appservice)
            
            > ⚠️ The AZD templates above include Mongo/Cosmos by default. When using as a pure Web API base, strip the database layer before applying a recipe:
            > - Remove `infra/app/db.bicep` (or equivalent database module)
            > - Remove Cosmos/Mongo module references from `infra/main.bicep`
            > - Remove Cosmos/Mongo packages from the application source
            >
            > Then apply the appropriate [recipe](recipes/README.md) for your data store.
            
            ## Project Structure
            
            ```
            project-root/
            ├── azure.yaml              # AZD service config
            ├── infra/
            │   ├── main.bicep           # Main orchestrator
            │   ├── main.parameters.json
            │   └── app/
            │       ├── web.bicep         # App Service + Plan
            │       └── web-network.bicep # VNet integration (conditional)
            └── src/
                └── api/                  # Application source
            ```
            
            ## App Service Plan Configuration
            
            ```bicep
            resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
              name: '${name}-plan'
              location: location
              tags: tags
              sku: {
                name: 'B1'              // Dev/test — use P1v3+ for production
              }
              properties: {
                reserved: true           // Required for Linux
              }
            }
            ```
            
            | Environment | Recommended SKU | Notes |
            |-------------|----------------|-------|
            | Dev/Test | B1 | Basic tier, no autoscale |
            | Production | P1v3 | Premium v3, autoscale, VNet integration |
            | High-scale | P2v3 / P3v3 | Higher CPU/memory |
            
            ## Health Check Endpoint
            
            > **CRITICAL**: Always include a health check endpoint. App Service uses it for instance monitoring.
            
            Configure in App Service:
            ```bicep
            properties: {
              siteConfig: {
                healthCheckPath: '/health'
              }
            }
            ```
            
            ### Health check by language
            
            **C# (ASP.NET Core)**
            ```csharp
            app.MapGet("/health", () => Results.Ok(new { status = "healthy" }));
            ```
            
            **Node.js (Express)**
            ```javascript
            app.get('/health', (req, res) => {
              res.json({ status: 'healthy' });
            });
            ```
            
            **Python (FastAPI)**
            ```python
            @app.get("/health")
            async def health():
                return {"status": "healthy"}
            ```
            
            **Java (Spring Boot)**
            ```java
            @GetMapping("/health")
            public Map<String, String> health() {
                return Map.of("status", "healthy");
            }
            ```
            
            ## Dockerfile Reference
            
            All App Service deployments can use container deployment. Each runtime should include a Dockerfile:
            
            **Python (FastAPI)**
            ```dockerfile
            FROM python:3.12-slim
            WORKDIR /app
            COPY requirements.txt .
            RUN pip install --no-cache-dir -r requirements.txt
            COPY . .
            EXPOSE 8000
            CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
            ```
            
            **Node.js**
            ```dockerfile
            FROM node:20-slim
            WORKDIR /app
            COPY package*.json ./
            RUN npm ci --production
            COPY . .
            EXPOSE 3000
            CMD ["node", "src/index.js"]
            ```
            
            ## App Settings (Managed Identity)
            
            > 💡 Call `mcp_bicep_get_az_resource_type_schema` with resource type `Microsoft.Web/sites` to validate properties before generating this resource.
            
            ```bicep
            resource webApp 'Microsoft.Web/sites@2023-12-01' = {
              properties: {
                siteConfig: {
                  appSettings: [
                    { name: 'WEBSITE_HEALTHCHECK_MAXPINGFAILURES', value: '3' }
                    { name: 'SCM_DO_BUILD_DURING_DEPLOYMENT', value: 'true' }
                  ]
                }
              }
              identity: {
                type: 'UserAssigned'
                userAssignedIdentities: {
                  '${managedIdentity.id}': {}
                }
              }
              tags: {
                'azd-service-name': 'api'
              }
            }
            ```
            
            > ⚠️ **Without `azd-service-name` tag, `azd deploy` fails with:**
            > `resource not found: unable to find a resource tagged with 'azd-service-name: api'`
            >
            > The tag value must match the `services.<name>` key in `azure.yaml`. Use `api` for Web API services.
            
            ## Deployment
            
            ```bash
            ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
            azd init -t <template> -e "$ENV_NAME" --no-prompt
            azd env set AZURE_LOCATION eastus2
            azd up --no-prompt
            ```
            
            **PowerShell:**
            ```powershell
            $ENV_NAME = "$(Split-Path -Leaf (Get-Location) | ForEach-Object { $_.ToLower() -replace '[ _]','-' })-dev"
            azd init -t <template> -e $ENV_NAME --no-prompt
            azd env set AZURE_LOCATION eastus2
            azd up --no-prompt
            ```
            
            ## References
            
            - [App Service overview](https://learn.microsoft.com/en-us/azure/app-service/overview)
            - [App Service best practices](https://learn.microsoft.com/en-us/azure/app-service/app-service-best-practices)
            - [Health check](https://learn.microsoft.com/en-us/azure/app-service/monitor-instances-health-check)
            
          • web-app.md 4.9 KB
            # Full-Stack Web App Template — REFERENCE ONLY
            
            Template for server-side rendered web applications on Azure App Service. Use for MVC, Razor Pages, Next.js SSR, Django, or React+API patterns.
            
            ## Templates by Pattern
            
            | Pattern | AZD Template | Framework |
            |---------|-------------|-----------|
            | React + C# API | `azd init -t todo-csharp` | React SPA + ASP.NET Core API |
            | React + Node.js API | `azd init -t todo-nodejs-mongo` | React SPA + Express API |
            | React + Python API | `azd init -t todo-python-mongo` | React SPA + FastAPI |
            | React + Java API | `azd init -t todo-java-mongo` | React SPA + Spring Boot |
            
            **Browse all:** [Awesome AZD](https://azure.github.io/awesome-azd/?tags=appservice)
            
            > 💡 **Tip:** For static frontends with API backends, consider Azure Static Web Apps instead. Use App Service when you need full server-side rendering.
            
            ## Architecture Patterns
            
            ### Pattern A: Single App Service (API + static files)
            
            ```
            App Service (Linux)
            ├── /api/*    → Backend routes
            └── /*        → Static files (React/Vue build output)
            ```
            
            Best for: Simple apps, MVPs, Razor Pages, Django with templates.
            
            ### Pattern B: Separate frontend + backend
            
            ```
            App Service (frontend) ← React/Next.js SSR
                  │
                  └──► App Service (backend) ← REST API
            ```
            
            Best for: Independent scaling, team separation, microservices.
            
            ## Project Structure (Single App)
            
            ```
            project-root/
            ├── azure.yaml
            ├── infra/
            │   ├── main.bicep
            │   └── app/
            │       └── web.bicep
            └── src/
                ├── api/              # Backend
                │   ├── Program.cs    # or app.py / index.js
                │   └── ...
                └── web/              # Frontend (built output served as static)
                    ├── package.json
                    └── src/
            ```
            
            ## azure.yaml (multi-service)
            
            ```yaml
            name: my-web-app
            services:
              web:
                project: ./src/web
                host: appservice
                language: js
                dist: build
              api:
                project: ./src/api
                host: appservice
                language: csharp
            ```
            
            ## Server-Side Rendering Examples
            
            ### ASP.NET Core MVC / Razor Pages
            
            ```csharp
            var builder = WebApplication.CreateBuilder(args);
            builder.Services.AddControllersWithViews();
            var app = builder.Build();
            app.UseStaticFiles();
            app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
            app.MapGet("/health", () => Results.Ok(new { status = "healthy" }));
            app.Run();
            ```
            
            ### Next.js (SSR on App Service)
            
            ```javascript
            // next.config.js
            module.exports = {
              output: 'standalone',  // Required for App Service deployment
            };
            ```
            
            Startup command in App Service:
            ```bash
            node server.js
            ```
            
            ### Django
            
            ```python
            # settings.py
            ALLOWED_HOSTS = ['.azurewebsites.net', 'localhost']
            STATIC_ROOT = BASE_DIR / 'staticfiles'
            CSRF_TRUSTED_ORIGINS = ['https://*.azurewebsites.net']
            ```
            
            ### Express + React (served from build)
            
            ```javascript
            const express = require('express');
            const path = require('path');
            const app = express();
            
            app.use(express.json());
            app.use('/api', apiRouter);
            app.use(express.static(path.join(__dirname, '../web/build')));
            app.get('/health', (req, res) => res.json({ status: 'healthy' }));
            app.get('*', (req, res) => {
              res.sendFile(path.join(__dirname, '../web/build/index.html'));
            });
            
            app.listen(process.env.PORT || 3000);
            ```
            
            ## App Service Configuration
            
            ```bicep
            resource webApp 'Microsoft.Web/sites@2023-12-01' = {
              properties: {
                siteConfig: {
                  linuxFxVersion: 'NODE|24-lts'  // or DOTNETCORE|10.0, PYTHON|3.14
                  healthCheckPath: '/health'
                  appCommandLine: ''              // Custom startup command if needed
                  appSettings: [
                    { name: 'SCM_DO_BUILD_DURING_DEPLOYMENT', value: 'true' }
                    { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~24' }
                  ]
                }
              }
              tags: {
                'azd-service-name': 'web'
              }
            }
            ```
            
            ## Startup Commands by Runtime
            
            | Runtime | Startup Command | Notes |
            |---------|----------------|-------|
            | ASP.NET Core | (auto-detected) | No command needed |
            | Node.js | `node server.js` or `npm start` | Set via `appCommandLine` |
            | Python (Django) | `gunicorn myapp.wsgi` | Use gunicorn for production |
            | Python (FastAPI) | `uvicorn main:app --host 0.0.0.0` | Use uvicorn for ASGI |
            | Next.js SSR | `node server.js` | Use `output: 'standalone'` |
            
            ## Deployment
            
            ```bash
            ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
            azd init -t <template> -e "$ENV_NAME" --no-prompt
            azd env set AZURE_LOCATION eastus2
            azd up --no-prompt
            ```
            
            **PowerShell:**
            ```powershell
            $ENV_NAME = "$(Split-Path -Leaf (Get-Location) | ForEach-Object { $_.ToLower() -replace '[ _]','-' })-dev"
            azd init -t <template> -e $ENV_NAME --no-prompt
            azd env set AZURE_LOCATION eastus2
            azd up --no-prompt
            ```
            
            ## References
            
            - [Deploy to App Service](https://learn.microsoft.com/en-us/azure/app-service/quickstart-dotnetcore)
            - [Node.js on App Service](https://learn.microsoft.com/en-us/azure/app-service/quickstart-nodejs)
            - [Python on App Service](https://learn.microsoft.com/en-us/azure/app-service/quickstart-python)
            
        • bicep.md 4.3 KB
          # App Service Bicep Patterns
          
          ## Linux vs Windows
          
          > ⚠️ **Warning:** `linuxFxVersion` and `reserved: true` are **Linux-only** properties. Setting `linuxFxVersion` on a Windows App Service Plan causes a deployment error: `LinuxFxVersion cannot be set for non-Linux App Service plans`. Omit both for Windows plans.
          
          | Property | Linux | Windows |
          |----------|-------|---------|
          | Plan `reserved` | `true` | omit (defaults to `false`) |
          | Site `kind` | `'app,linux'` | `'app'` |
          | Site `linuxFxVersion` | e.g. `'NODE\|20-lts'` | omit |
          | Site `WEBSITE_NODE_DEFAULT_VERSION` | omit | e.g. `'~20'` |
          
          > 💡 **Tip:** When both frontend and API use Node.js, prefer a **single Linux App Service Plan** for both. This avoids mixed-platform complexity and keeps all services on one plan.
          
          ## Linux App Service (Recommended for Node.js)
          
          > ⚠️ **REQUIRED: `azd-service-name` tag** — The `tags` property MUST include `union(tags, { 'azd-service-name': serviceName })` so that `azd deploy` can locate the resource. Without this tag, `azd deploy` fails with `resource not found: unable to find a resource tagged with 'azd-service-name: web'`.
          
          ```bicep
          resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-plan-${uniqueHash}'
            location: location
            kind: 'linux'
            sku: {
              name: 'B1'
              tier: 'Basic'
            }
            properties: {
              reserved: true  // Required for Linux
            }
          }
          
          resource webApp 'Microsoft.Web/sites@2022-09-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            kind: 'app,linux'
            tags: union(tags, { 'azd-service-name': serviceName })  // REQUIRED for azd deploy
            properties: {
              serverFarmId: appServicePlan.id
              siteConfig: {
                linuxFxVersion: 'NODE|24-lts'
                alwaysOn: true
                healthCheckPath: '/health'
                appSettings: [
                  {
                    name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
                    value: 'true'  // Required for source-based Node.js deploys (azd deploy)
                  }
                  {
                    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
                    value: applicationInsights.properties.ConnectionString
                  }
                  {
                    name: 'ApplicationInsightsAgent_EXTENSION_VERSION'
                    value: '~3'
                  }
                ]
              }
              httpsOnly: true
            }
            identity: {
              type: 'SystemAssigned'
            }
          }
          ```
          
          ## Windows App Service
          
          Use when the runtime requires Windows (e.g. .NET Framework) or when explicitly requested.
          
          ```bicep
          resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-plan-${uniqueHash}'
            location: location
            sku: {
              name: 'B1'
              tier: 'Basic'
            }
            // Do NOT set reserved: true for Windows
          }
          
          resource webApp 'Microsoft.Web/sites@2022-09-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            kind: 'app'
            tags: union(tags, { 'azd-service-name': serviceName })  // REQUIRED for azd deploy
            properties: {
              serverFarmId: appServicePlan.id
              siteConfig: {
                // Do NOT set linuxFxVersion for Windows plans
                alwaysOn: true
                healthCheckPath: '/health'
                appSettings: [
                  {
                    name: 'SCM_DO_BUILD_DURING_DEPLOYMENT'
                    value: 'true'  // Required for source-based Node.js deploys (azd deploy)
                  }
                  {
                    name: 'WEBSITE_NODE_DEFAULT_VERSION'
                    value: '~20'
                  }
                  {
                    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
                    value: applicationInsights.properties.ConnectionString
                  }
                  {
                    name: 'ApplicationInsightsAgent_EXTENSION_VERSION'
                    value: '~3'
                  }
                ]
              }
              httpsOnly: true
            }
            identity: {
              type: 'SystemAssigned'
            }
          }
          ```
          
          ## Node.js Build Configuration
          
          > ⚠️ **Warning:** For source-based Node.js deployments to App Service (e.g. `azd deploy`), you **must** set `SCM_DO_BUILD_DURING_DEPLOYMENT=true` so App Service runs `npm install` during deployment. Without this, the app fails at runtime with `Cannot find module` errors.
          
          This setting is already included in both the Linux and Windows examples above.
          
          ## Key Vault Integration
          
          Reference secrets from Key Vault:
          
          ```bicep
          appSettings: [
            {
              name: 'DATABASE_URL'
              value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-url)'
            }
          ]
          ```
          
        • custom-domains.md 4.8 KB
          # App Service Custom Domains and Managed TLS
          
          ## Prerequisites
          
          | Requirement | Details |
          |------------|---------|
          | SKU tier | Basic (B1) or higher |
          | DNS access | Ability to create CNAME, A, and TXT records |
          | Domain ownership | Verified via TXT record |
          
          ## DNS Configuration
          
          ### Subdomain (CNAME)
          
          | Record Type | Name | Value |
          |------------|------|-------|
          | CNAME | `www` | `<app-name>.azurewebsites.net` |
          | TXT | `asuid.www` | `<verification-id>` |
          
          ### Apex / Root Domain (A Record)
          
          | Record Type | Name | Value |
          |------------|------|-------|
          | A | `@` | `<app-ip-address>` |
          | TXT | `asuid` | `<verification-id>` |
          
          Get the verification ID and IP address:
          
          ```bash
          # Get verification ID
          az webapp show -n $APP -g $RG --query "customDomainVerificationId" -o tsv
          
          # Get IP address (for A records)
          az webapp show -n $APP -g $RG --query "inboundIpAddress" -o tsv
          ```
          
          > 💡 **Tip:** Prefer CNAME records for subdomains. For apex domains, consider using an Azure DNS alias record to avoid hardcoding IP addresses that may change.
          
          ## Bind Custom Domain via CLI
          
          ```bash
          # Add custom domain
          az webapp config hostname add -n $APP -g $RG --hostname www.contoso.com
          
          # Create managed certificate (free)
          az webapp config ssl create -n $APP -g $RG --hostname www.contoso.com
          
          # Capture certificate thumbprint
          THUMBPRINT=$(az webapp config ssl list -n $APP -g $RG \
            --query "[?contains(hostNames, 'www.contoso.com')].thumbprint | [0]" -o tsv)
          
          # Bind the certificate
          az webapp config ssl bind -n $APP -g $RG \
            --certificate-thumbprint $THUMBPRINT --ssl-type SNI
          ```
          
          ## Bicep — Custom Domain with Managed Certificate
          
          ```bicep
          resource customDomain 'Microsoft.Web/sites/hostNameBindings@2022-09-01' = {
            parent: webApp
            name: 'www.contoso.com'
            properties: {
              siteName: webApp.name
              hostNameType: 'Verified'
              sslState: 'Disabled' // enable after cert is created
            }
          }
          
          resource managedCert 'Microsoft.Web/certificates@2022-09-01' = {
            name: 'www.contoso.com'
            location: location
            properties: {
              serverFarmId: appServicePlan.id
              canonicalName: 'www.contoso.com'
            }
            dependsOn: [customDomain]
          }
          ```
          
          Then run a follow-up Bicep deployment to enable SNI and bind the managed certificate to the hostname:
          
          ```bicep
          resource managedCert 'Microsoft.Web/certificates@2022-09-01' existing = {
            name: 'www.contoso.com'
          }
          
          resource customDomainTlsBinding 'Microsoft.Web/sites/hostNameBindings@2022-09-01' = {
            parent: webApp
            name: 'www.contoso.com'
            properties: {
              siteName: webApp.name
              hostNameType: 'Verified'
              sslState: 'SniEnabled'
              thumbprint: managedCert.properties.thumbprint
            }
          }
          ```
          
          > ⚠️ **Warning:** Managed certificate creation requires the DNS records to be in place first. The hostname binding must exist before requesting the certificate.
          
          ## Terraform — Custom Domain with Managed Certificate
          
          ```hcl
          resource "azurerm_app_service_custom_hostname_binding" "domain" {
            hostname            = "www.contoso.com"
            app_service_name    = azurerm_linux_web_app.app.name
            resource_group_name = azurerm_resource_group.rg.name
          }
          
          resource "azurerm_app_service_managed_certificate" "cert" {
            custom_hostname_binding_id = azurerm_app_service_custom_hostname_binding.domain.id
          }
          
          resource "azurerm_app_service_certificate_binding" "binding" {
            hostname_binding_id = azurerm_app_service_custom_hostname_binding.domain.id
            certificate_id      = azurerm_app_service_managed_certificate.cert.id
            ssl_state           = "SniEnabled"
          }
          ```
          
          ## TLS Options
          
          | Option | Cost | Renewal | Use Case |
          |--------|------|---------|----------|
          | App Service Managed Certificate | Free | Auto-renewed | Standard custom domains |
          | App Service Certificate (purchased) | ~$70/yr | Auto-renewed | Extended validation, wildcard |
          | Bring your own certificate | Varies | Manual | Enterprise PKI, specific CA |
          
          ### Enforce HTTPS Only
          
          ```bicep
          resource webApp 'Microsoft.Web/sites@2022-09-01' = {
            name: appName
            location: location
            properties: {
              httpsOnly: true
              // ...
            }
          }
          ```
          
          ```hcl
          resource "azurerm_linux_web_app" "app" {
            name                = var.app_name
            # ...
            https_only          = true
          }
          ```
          
          ## Minimum TLS Version
          
          ```bash
          # Set minimum TLS version to 1.2
          az webapp config set -n $APP -g $RG --min-tls-version 1.2
          ```
          
          ```bicep
          siteConfig: {
            minTlsVersion: '1.2'
          }
          ```
          
          > ⚠️ **Warning:** TLS 1.0 and 1.1 are deprecated. Always set minimum TLS version to 1.2 for production workloads.
          
          ## Troubleshooting
          
          | Issue | Cause | Fix |
          |-------|-------|-----|
          | Domain verification fails | Missing TXT record | Add `asuid` TXT record and wait for DNS propagation |
          | Certificate creation fails | DNS not yet propagated | Wait 5-15 min for propagation; verify with `nslookup` |
          | SSL binding error | SKU too low | Upgrade to Basic (B1) or higher |
          | Managed cert not renewing | DNS record changed | Verify CNAME/A record still points to the app |
          
        • deployment-slots.md 4.6 KB
          # App Service Deployment Slots
          
          Zero-downtime deployments using staging slots.
          
          ## Basic Staging Slot
          
          ```bicep
          resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = {
            parent: webApp
            name: 'staging'
            location: location
            properties: {
              serverFarmId: appServicePlan.id
            }
          }
          ```
          
          ## Slot Requirements — App Service
          
          | SKU Tier | Slots Supported |
          |----------|-----------------|
          | Free/Shared | 0 |
          | Basic | 0 |
          | Standard | 5 |
          | Premium | 20 |
          
          ## Slot Requirements — Azure Functions
          
          > ⚠️ Slot support for Azure Functions varies by OS and hosting plan.
          
          | Hosting Plan | OS | Slots Supported |
          |---|---|---|
          | Flex Consumption (FC1) | Linux | ❌ 0 |
          | Consumption (Y1) | **Windows** | ✅ 1 staging slot |
          | Consumption (Y1) | Linux | ❌ 0 |
          | Elastic Premium (EP1-EP3) | Windows or Linux | ✅ 20 slots |
          | Dedicated (Standard+) | Windows or Linux | ✅ 5–20 slots |
          
          > 💡 **For Azure Functions requiring deployment slots:**
          > - **Windows Consumption (Y1) supports 1 staging slot** — this is a supported platform capability.
          >   If you need it, use it. See the Bicep example below.
          > - Recommendation for new projects: prefer **Elastic Premium (EP1+)** (no cold starts, VNet integration)
          >   or a **Dedicated plan (Standard+)**. Y1 cold starts can affect slot swap warm-up reliability.
          > - **Linux Consumption and Flex Consumption do not support deployment slots.**
          
          ### Windows Consumption Function App with Staging Slot (Bicep)
          
          ```bicep
          resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-funcplan-${uniqueHash}'
            location: location
            sku: { name: 'Y1', tier: 'Dynamic' }
            // No 'reserved: true' — Windows Consumption
          }
          
          resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            kind: 'functionapp'  // Windows (no 'linux' suffix)
            identity: { type: 'SystemAssigned' }
            properties: {
              serverFarmId: functionAppPlan.id
              httpsOnly: true
              siteConfig: {
                appSettings: [
                  { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' }
                  { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
                  { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
                  { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-prod' }
                  { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString }
                ]
              }
            }
          }
          
          // Staging slot — only 1 staging slot supported on Consumption
          resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = {
            parent: functionApp
            name: 'staging'
            location: location
            kind: 'functionapp'
            properties: {
              serverFarmId: functionAppPlan.id
              siteConfig: {
                appSettings: [
                  { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' }
                  { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
                  { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
                  { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-staging' }  // MUST differ from production
                  { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString }
                ]
              }
            }
          }
          ```
          
          > ⚠️ `WEBSITE_CONTENTSHARE` **must be unique per slot** on Windows Consumption — each slot needs its own file share.
          > Use slot-sticky settings (via `slotConfigNames`) for `WEBSITE_CONTENTSHARE` and `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`
          > so these values do not swap with production.
          
          ## Deployment Flow
          
          1. Deploy to staging slot
          2. Warm up and test staging
          3. Swap staging with production
          4. Rollback by swapping again if needed
          
          ## Slot Settings
          
          Configure settings that should not swap:
          
          ```bicep
          resource slotConfigNames 'Microsoft.Web/sites/config@2022-09-01' = {
            parent: webApp
            name: 'slotConfigNames'
            properties: {
              appSettingNames: [
                'APPLICATIONINSIGHTS_CONNECTION_STRING'
              ]
            }
          }
          ```
          
        • networking.md 6.6 KB
          # App Service Networking
          
          VNet integration, Private Endpoints, Access Restrictions, and Hybrid Connections.
          
          ## Feature Availability by SKU
          
          | Feature | Free | Basic | Standard | Premium | Isolated |
          |---------|:-:|:-:|:-:|:-:|:-:|
          | VNet integration (outbound) | ❌ | ✅ | ✅ | ✅ | ✅ (native) |
          | Private Endpoints (inbound) | ❌ | ✅ | ✅ | ✅ | ✅ |
          | Access Restrictions | ✅ | ✅ | ✅ | ✅ | ✅ |
          | Hybrid Connections | ❌ | 5 | 25 | 200 | 200 |
          | Access to service-endpoint-protected resources | ❌ | ✅ | ✅ | ✅ | ✅ |
          > Note: Service endpoints are configured on VNets/subnets and downstream services (e.g., Storage, SQL). App Service accesses them via VNet integration rather than enabling service endpoints directly on the app.
          
          ## VNet Integration (Outbound)
          
          Routes outbound traffic from the app through a VNet subnet, enabling access to private resources (databases, storage, VMs).
          
          ### Subnet Requirements
          
          | Requirement | Value |
          |------------|-------|
          | Minimum subnet size | `/26` (64 addresses) recommended |
          | Delegation | `Microsoft.Web/serverFarms` |
          | Dedicated | One subnet per App Service plan |
          
          ### Bicep — VNet Integration
          
          ```bicep
          resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = {
            parent: vnet
            name: 'app-service-subnet'
            properties: {
              addressPrefix: '10.0.1.0/26'
              delegations: [
                {
                  name: 'Microsoft.Web.serverFarms'
                  properties: { serviceName: 'Microsoft.Web/serverFarms' }
                }
              ]
            }
          }
          
          resource webApp 'Microsoft.Web/sites@2024-11-01' = {
            name: appName
            location: location
            properties: {
              serverFarmId: appServicePlan.id
              virtualNetworkSubnetId: subnet.id
              outboundVnetRouting: {
                allTraffic: true // route all outbound through VNet
              }
            }
          }
          ```
          
          ### CLI - VNet Integration
          
          ```bash
          # Configure virtual network integration 
          az webapp vnet-integration add --resource-group RG --name APP --vnet VNET --subnet SUBNET
          
          # Update app configuration to route all outbound traffic through the virtual network integration
          az resource update --resource-group RG --name APP --resource-type "Microsoft.Web/sites" --set properties.outboundVnetRouting.allTraffic=true
          ```
          
          
          > 💡 **Tip:** Set `outboundVnetRouting.allTraffic: true` to route ALL outbound traffic through the VNet. Without this, only RFC1918 traffic is routed through the VNet.
          
          ## Private Endpoints (Inbound)
          
          Expose the app on a private IP address within your VNet. Public access can be disabled entirely.
          
          ### Bicep — Private Endpoint
          
          ```bicep
          resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-11-01' = {
            name: '${appName}-pe'
            location: location
            properties: {
              subnet: { id: privateEndpointSubnet.id }
              privateLinkServiceConnections: [
                {
                  name: '${appName}-connection'
                  properties: {
                    privateLinkServiceId: webApp.id
                    groupIds: ['sites']
                  }
                }
              ]
            }
          }
          
          resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
            name: 'privatelink.azurewebsites.net'
            location: 'global'
          }
          
          resource dnsLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
            parent: privateDnsZone
            name: '${vnet.name}-link'
            location: 'global'
            properties: {
              virtualNetwork: { id: vnet.id }
              registrationEnabled: false
            }
          }
          
          resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = {
            parent: privateEndpoint
            name: 'default'
            properties: {
              privateDnsZoneConfigs: [
                {
                  name: 'webapp-dns-zone'
                  properties: {
                    privateDnsZoneId: privateDnsZone.id
                  }
                }
              ]
            }
          }
          ```
          
          ### CLI - Private Endpoint
          
          ```bash
          # Retrieve web app resource id
          id=$(az webapp show --name APP --resource-group RG --query id --output tsv)
          
          # Create Private Endpoint
          az network private-endpoint create --connection-name CONNECTIONNAME --name private-endpoint --private-connection-resource-id $id --resource-group RG --subnet SUBNET --group-id sites --vnet-name VNET
          
          # Create Private DNS Zone
          az network private-dns zone create --resource-group RG --name "privatelink.azurewebsites.net"
          
          # Link the DNS Zone to virtual network
          az network private-dns link vnet create --resource-group RG --zone-name "privatelink.azurewebsites.net" --name dns-link --virtual-network VNET --registration-enabled false
          
          ```
          
          > ⚠️ **Warning:** Private Endpoints require Basic (B1+) or higher tier. The private DNS zone `privatelink.azurewebsites.net` must be linked to the VNet for name resolution.
          
          ## Access Restrictions
          
          Control inbound access with IP-based or service-tag rules. Available on all SKUs.
          
          ### Bicep — Access Restrictions
          
          ```bicep
          siteConfig: {
            ipSecurityRestrictions: [
              {
                name: 'allow-office'
                priority: 100
                action: 'Allow'
                ipAddress: '203.0.113.0/24'
              }
              {
                name: 'deny-all'
                priority: 2147483647
                action: 'Deny'
                ipAddress: 'Any'
              }
            ]
            scmIpSecurityRestrictionsUseMain: true
          }
          ```
          
          ### CLI - Access Restrictions
          
          ```bash
          # Add restriction to allow traffic from set range used by the office
          az webapp config access-restriction add --resource-group RG --name APP --rule-name 'allow-office' --action Allow --ip-address 203.0.113.0/24 --priority 100
          
          # Add restriction to deny access from any other address range
          az webapp config access-restriction add --resource-group RG --name APP --rule-name 'deny-all' --action Deny --ip-address Any --priority 2147483647
          
          # Set SCM Site (Kudu) to use same access restrictions as main site
          az webapp config access-restriction set -g RG -n APP --use-same-restrictions-for-scm-site true
          ```
          
          > 💡 **Tip:** Always restrict the SCM/Kudu site too. Use `scmIpSecurityRestrictionsUseMain: true` to inherit main site rules, or define separate SCM rules.
          
          ## Hybrid Connections
          
          Connect to on-premises resources without VPN. Requires Basic tier or higher. Uses Hybrid Connection Manager (HCM) agent on-premises relaying through Azure Relay.
          
          > ⚠️ **Warning:** Each Hybrid Connection maps to a single host:port endpoint. Basic tier supports 5; Standard tier supports 25; Premium/Isolated support 200.
          
          ## Troubleshooting
          
          | Issue | Cause | Fix |
          |-------|-------|-----|
          | Cannot reach private DB | VNet integration not enabled | Enable VNet integration; check `outboundVnetRouting.allTraffic` |
          | DNS resolution fails | Private DNS zone not linked | Link `privatelink.*` DNS zone to VNet |
          | Access restriction not working | Priority ordering wrong | Lower numbers = higher priority; check rule order |
          | Hybrid Connection timeout | HCM not running | Verify HCM service status on-premises |
          | Outbound traffic blocked | NSG rules on subnet | Allow outbound to required services in NSG |
          
        • README.md 2.4 KB
          # Azure App Service
          
          Hosting patterns and best practices for Azure App Service.
          
          ## When to Use
          
          - Traditional web applications
          - REST APIs without containerization
          - .NET, Node.js, Python, Java, PHP applications
          - When Docker is not required/desired
          - When built-in deployment slots are needed
          
          ## Service Type in azure.yaml
          
          ```yaml
          services:
            my-web:
              host: appservice
              project: ./src/my-web
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | App Service Plan | Compute hosting |
          | Application Insights | Monitoring |
          | Key Vault | Secrets (optional) |
          
          ## Runtime Stacks
          
          > 💡 **Tip:** Prefer Linux App Service Plans for Node.js, Python, and Java. Use Windows only when explicitly required (e.g. .NET Framework). See [Bicep Patterns](bicep.md) for Linux vs Windows configuration.
          
          ### Linux (recommended)
          
          | Language | linuxFxVersion |
          |----------|----------------|
          | Node.js 24 | `NODE\|24-lts` |
          | Node.js 22 | `NODE\|22-lts` |
          | Python 3.14 | `PYTHON\|3.14` |
          | .NET 10 | `DOTNETCORE\|10.0` |
          | Java 21 | `JAVA\|21-java21` |
          
          ### Windows
          
          | Language | Setting |
          |----------|---------|
          | Node.js | `WEBSITE_NODE_DEFAULT_VERSION: '~24'` (app setting) |
          | .NET 10 | Built-in (no extra config) |
          
          ## SKU Selection
          
          | SKU | Use Case |
          |-----|----------|
          | F1/D1 | Development/testing (free/shared) |
          | B1-B3 | Small production, basic features |
          | S1-S3 | Production with auto-scale, slots |
          | P1v3-P3v3 | High-performance production |
          
          ## Health Checks
          
          Always configure health check path:
          
          ```bicep
          siteConfig: {
            healthCheckPath: '/health'
          }
          ```
          
          Endpoint should return 200 OK when healthy.
          
          ## Templates
          
          For App Service templates with composable recipes (SQL, Cosmos DB, Auth, Redis), see [Template Selection](templates/selection.md).
          
          ## Common Data Backends
          
          When pairing App Service with a data layer, load the corresponding service references:
          
          | Data Service | Reference                                 |
          | ------------ | ----------------------------------------- |
          | Azure SQL    | [SQL Database](../sql-database/README.md) |
          | Cosmos DB    | [Cosmos DB](../cosmos-db/README.md)       |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Deployment Slots](deployment-slots.md)
          - [Auto-Scaling](scaling.md)
          - [Networking](networking.md)
          - [SKU Selection](sku-selection.md)
          - [Custom Domains](custom-domains.md)
          
        • scaling.md 1.5 KB
          # App Service Auto-scaling
          
          ## Basic Auto-scale Configuration
          
          ```bicep
          resource autoScale 'Microsoft.Insights/autoscalesettings@2022-10-01' = {
            name: '${webApp.name}-autoscale'
            location: location
            properties: {
              targetResourceUri: appServicePlan.id
              enabled: true
              profiles: [
                {
                  name: 'Auto scale'
                  capacity: {
                    minimum: '1'
                    maximum: '10'
                    default: '1'
                  }
                  rules: [
                    {
                      metricTrigger: {
                        metricName: 'CpuPercentage'
                        metricResourceUri: appServicePlan.id
                        timeGrain: 'PT1M'
                        statistic: 'Average'
                        timeWindow: 'PT5M'
                        timeAggregation: 'Average'
                        operator: 'GreaterThan'
                        threshold: 70
                      }
                      scaleAction: {
                        direction: 'Increase'
                        type: 'ChangeCount'
                        value: '1'
                        cooldown: 'PT5M'
                      }
                    }
                  ]
                }
              ]
            }
          }
          ```
          
          ## Common Metrics
          
          | Metric | Use Case |
          |--------|----------|
          | CpuPercentage | CPU-bound workloads |
          | MemoryPercentage | Memory-intensive apps |
          | HttpQueueLength | Request queue depth |
          | Requests | Request volume |
          
          ## Recommendations
          
          | Workload | Min | Max | Metric |
          |----------|-----|-----|--------|
          | Production API | 2 | 10 | CPU + Requests |
          | Dev/Test | 1 | 3 | CPU |
          | High-traffic | 3 | 20 | HTTP Queue |
          
          ## SKU Requirements
          
          Auto-scaling requires **Standard (S1+)** or **Premium** tier.
          
        • sku-selection.md 4.7 KB
          # App Service SKU Selection
          
          ## SKU Comparison Matrix
          
          | Feature | Free (F1) | Basic (B1-B3) | Standard (S1-S3) | Premium (P0v3-P3v3 and P1Mv3-P5Mv3;P0v4-P3v4 and P1Mv4-P5Mv4) | Isolated (I1v2-I6v2) |
          |---------|:-:|:-:|:-:|:-:|:-:|
          | **Custom domains** | ❌ | ✅ | ✅ | ✅ | ✅ |
          | **TLS/SSL bindings** | ❌ | ✅ (SNI) | ✅ (SNI + IP) | ✅ (SNI + IP) | ✅ (SNI + IP) |
          | **Deployment slots** | ❌ | ❌ | 5 | 20 | 20 |
          | **Auto-scale** | ❌ | ❌ | ✅ (10 inst.) | ✅ (30 inst.) | ✅ (100 inst.) |
          | **VNet integration** | ❌ | ✅ | ✅ | ✅ | ✅ (ASE is in VNet) |
          | **Private endpoints** | ❌ | ✅ | ✅ | ✅ | ✅ |
          | **Always On** | ❌ | ✅ | ✅ | ✅ | ✅ |
          | **Backup/Restore** | ❌ | ❌ | ✅ | ✅ | ✅ |
          | **Hybrid Connections** | ❌ | 5 | 25 | 200 | 200 |
          | **Traffic Manager** | ✅ | ✅ | ✅ | ✅ | ✅ |
          | **SLA** | None | None | 99.95% | 99.95% | 99.95% |
          
          ## Pricing Overview
          
          | SKU | vCPU | RAM | Storage | Approx. Monthly Cost |
          |-----|------|-----|---------|---------------------|
          | F1 | Shared | 1 GB | 1 GB | Free |
          | B1 | 1 | 1.75 GB | 10 GB | ~$55 |
          | B2 | 2 | 3.5 GB | 10 GB | ~$110 |
          | S1 | 1 | 1.75 GB | 50 GB | ~$73 |
          | S2 | 2 | 3.5 GB | 50 GB | ~$146 |
          | P1v3 | 2 | 8 GB | 250 GB | ~$138 |
          | P2v3 | 4 | 16 GB | 250 GB | ~$276 |
          | P3v3 | 8 | 32 GB | 250 GB | ~$552 |
          | I1v2 | 2 | 8 GB | 1 TB | ~$460 |
          
          > 💡 **Tip:** Figures are representative for **Windows OS** in **Central US**, **as of 2026-04**. Prices vary by region, OS, and offer. Use the [Azure Pricing Calculator](https://azure.microsoft.com/pricing/calculator/) for exact figures.
          
          ### Save by using Reserved Instances and Savings Plans
          
          Cost savings can be made on Premium V3, Premium V4 and Isolated V2 plans by committing to reserved instances for 1 or 3 year terms, details can found at [https://learn.microsoft.com/azure/cost-management-billing/reservations/prepay-app-service](https://learn.microsoft.com/azure/cost-management-billing/reservations/prepay-app-service).
          
          Alternatively cost savings can be made using [Azure Savings plans](https://learn.microsoft.com/en-us/azure/cost-management-billing/savings-plan/).
          
          ## Decision Criteria
          
          ```
          Production workload?
          ├─ No → Free (F1) or Basic (B1) for dev/test
          └─ Yes
             Need deployment slots, auto-scale, or backups?
             ├─ No → Basic (B1-B3) if budget-constrained (supports VNet integration and Private Endpoints)
             └─ Yes
                    Need network isolation (dedicated ASE)?
                    ├─ Yes → Isolated (I1v2+)
                    └─ No
                       Need more than 5 deployment slots or more than 10 instances?
                       ├─ Yes → Premium (P1v3+)
                       └─ No → Standard (S1-S3) with Private Endpoints and VNet integration
          ```
          
          ## Feature Unlock Summary
          
          Key features unlocked at each tier:
          
          | Upgrade Path | Features Gained |
          |-------------|-----------------|
          | Free → Basic | Custom domains, TLS/SSL, Always On, VNet Integration, Private Endpoints, Hybrid Connections (5) |
          | Basic → Standard | Deployment slots, auto-scale, backups |
          | Standard → Premium | More slots (20), higher scale (30 inst.) |
          | Premium → Isolated | Full network isolation (ASE), dedicated infrastructure |
          
          ## Bicep — App Service Plan with SKU
          
          ```bicep
          resource appServicePlan 'Microsoft.Web/serverfarms@2025-03-01' = {
            name: planName
            location: location
            sku: {
              name: 'P1v3'
              tier: 'PremiumV3'
              capacity: 2 // number of instances
            }
            kind: 'linux'
            properties: {
              reserved: true // required for Linux
            }
          }
          ```
          
          ## Terraform — App Service Plan with SKU
          
          ```hcl
          resource "azurerm_service_plan" "plan" {
            name                = var.plan_name
            location            = azurerm_resource_group.rg.location
            resource_group_name = azurerm_resource_group.rg.name
            os_type             = "Linux"
            sku_name            = "P1v3"
          }
          ```
          
          ## Scaling Within a Tier
          
          Scale up (change SKU) vs scale out (add instances):
          
          | Strategy | When to Use | How |
          |----------|-------------|-----|
          | Scale up | App needs more CPU/RAM | Change SKU (e.g., S1 → S2) |
          | Scale out | Handle more concurrent load | Increase instance count or enable auto-scale |
          
          > ⚠️ **Warning:** Scaling from one tier family to another (e.g., Standard to Premium) may cause a brief restart. Schedule changes during low-traffic windows.
          
          ## Recommendations by Workload
          
          | Workload | Recommended SKU | Reason |
          |----------|----------------|--------|
          | Personal blog / prototype | F1 or B1 | Minimal cost, no SLA needed |
          | Team dev/test | B1-B2 | Always On, custom domain |
          | Production API | S1-S3 (P0v3/P0v4+ for higher scale/perf) | Auto-scale, slots, VNet |
          | Enterprise with compliance | P1v3+/P1v4+ | Private endpoints, 20 slots, 30 instances |
          | Regulated / multi-tenant SaaS | I1v2+ | Full network isolation |
          
      • container-apps
        • bicep.md 4.9 KB
          # Container Apps Bicep Patterns
          
          > **⚠️ Container Registry Naming:** If using Azure Container Registry, names must be alphanumeric only (5-50 characters). Use `replace()` to remove hyphens: `replace('cr${environmentName}${resourceSuffix}', '-', '')`
          
          > **⚠️ Two-Phase Deployment (Mandatory):** To avoid a circular dependency when scoping the AcrPull role assignment to a Bicep module, use the two-phase pattern below:
          > - **Phase 1:** Deploy ACR and Container App with a public placeholder image and **no** `registries` block.
          > - **Phase 2:** Deploy the AcrPull role assignment as a **separate module** using outputs from Phase 1.
          >
          > The Bicep template does **not** need a `registries` block. `azd deploy` handles the registry/identity link by calling `az containerapp registry set --server <acr-server> --identity system` via the Azure API before updating the container image. If you are not using AZD, you must run this command manually before switching the image to an ACR-hosted image; otherwise the app will hit image pull failures.
          
          ## Phase 1: Container App Module (No Registry Link)
          
          ```bicep
          // Placeholder image allows provisioning before app image exists in ACR.
          // No registries block in Bicep — azd deploy configures the registry/identity link
          // (az containerapp registry set --identity system) and updates the image via the Azure API.
          // If not using AZD, run that command manually before switching to an ACR-hosted image.
          param containerImageName string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
          
          resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
            name: appName
            location: location
            identity: {
              type: 'SystemAssigned'
            }
            properties: {
              environmentId: containerAppsEnvironment.id
              configuration: {
                ingress: {
                  external: true
                  targetPort: 8080
                  transport: 'auto'
                }
                // No registries block in Bicep. azd deploy sets the registry/identity link via
                // 'az containerapp registry set --identity system' before pushing the real image.
                // Without this step (or its manual equivalent), the app will fail to pull from ACR.
              }
              template: {
                containers: [
                  {
                    name: serviceName
                    image: containerImageName
                    resources: {
                      cpu: json('0.5')
                      memory: '1Gi'
                    }
                  }
                ]
              }
            }
          }
          
          output systemAssignedMIPrincipalId string = containerApp.identity.principalId
          ```
          
          ## Phase 2: AcrPull Role Assignment Module (acr-pull-role.bicep)
          
          Place this in a **separate module file** so neither the ACR module nor the Container App module depends on it, eliminating the circular dependency.
          
          ```bicep
          // acr-pull-role.bicep
          param acrName string
          param principalId string
          
          resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' existing = {
            name: acrName
          }
          
          resource acrPullRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(containerRegistry.id, principalId, 'acrpull')
            scope: containerRegistry
            properties: {
              roleDefinitionId: subscriptionResourceId(
                'Microsoft.Authorization/roleDefinitions',
                '7f951dda-4ed3-4680-a7ca-43fe172d538d'
              )
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          > 💡 **Tip:** Always set `principalType: 'ServicePrincipal'` for managed identities. This avoids a Graph API lookup and speeds up role assignment propagation.
          
          ## Wiring Phase 1 and Phase 2 in main.bicep
          
          ```bicep
          // Phase 1: ACR and Container App — neither module depends on the role assignment
          module containerRegistry './modules/container-registry.bicep' = {
            name: 'containerRegistry'
            scope: rg
            params: { /* ... */ }
          }
          
          module api './modules/container-app.bicep' = {
            name: 'api'
            scope: rg
            params: {
              containerImageName: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
              // No registries param in Bicep — azd deploy configures the registry/identity link
              // and updates the image via the Azure API after provisioning.
              /* ... */
            }
          }
          
          // Phase 2: Role assignment depends on outputs of both Phase 1 modules,
          // but neither Phase 1 module depends on this — no circular dependency.
          module acrPullRole './modules/acr-pull-role.bicep' = {
            name: 'acrPullRole'
            scope: rg
            params: {
              acrName: containerRegistry.outputs.name
              principalId: api.outputs.systemAssignedMIPrincipalId
            }
          }
          ```
          
          ## Container Apps Environment
          
          ```bicep
          resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
            name: '${resourcePrefix}-env'
            location: location
            properties: {
              appLogsConfiguration: {
                destination: 'log-analytics'
                logAnalyticsConfiguration: {
                  customerId: logAnalyticsWorkspace.properties.customerId
                  sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
                }
              }
            }
          }
          ```
          
        • day2-operations.md 5.1 KB
          # Container Apps Day-2 Operations
          
          Operational tasks for running Container Apps in production: restart, exec, logs, environment updates, and secret rotation.
          
          ## Restart and Lifecycle
          
          | Action | Command |
          |--------|---------|
          | Restart active revision | `az containerapp revision restart -n $APP -g $RG --revision $REV` |
          | Scale to zero (stop) | `az containerapp update -n $APP -g $RG --min-replicas 0 --max-replicas <current-max>` |
          | Resume (restore scaling) | `az containerapp update -n $APP -g $RG --min-replicas <previous-min> --max-replicas <current-max>` |
          | List replicas | `az containerapp replica list -n $APP -g $RG --revision $REV` |
          
          > 💡 **Tip:** Restarting a revision replaces all running replicas gracefully. No new revision is created.
          
          ## Exec into a Container
          
          Open a shell inside a running replica for debugging:
          
          ```bash
          # Interactive shell
          az containerapp exec -n $APP -g $RG --command /bin/sh
          
          # Target a specific replica and container
          az containerapp exec -n $APP -g $RG \
            --replica $REPLICA_NAME \
            --container $CONTAINER_NAME \
            --command /bin/sh
          ```
          
          > ⚠️ **Warning:** Exec sessions are for debugging only. Changes to the container filesystem are lost on restart.
          
          ## Log Streaming
          
          ### Real-time Logs
          
          ```bash
          # Stream system logs
          az containerapp logs show -n $APP -g $RG --type system --follow
          
          # Stream application (console) logs
          az containerapp logs show -n $APP -g $RG --type console --follow
          
          # Filter to a specific revision or replica
          az containerapp logs show -n $APP -g $RG \
            --type console --revision $REV --follow
          ```
          
          ### Log Analytics (KQL)
          
          Query historical logs via the Container Apps environment's Log Analytics workspace:
          
          ```kql
          // Azure Monitor destination (new environments — default)
          ContainerAppConsoleLogs
          | where ContainerAppName == "my-app"
          | where TimeGenerated > ago(1h)
          | project TimeGenerated, Log, RevisionName
          | order by TimeGenerated desc
          
          // Log Analytics destination (legacy environments)
          // ContainerAppConsoleLogs_CL
          // | where ContainerAppName_s == "my-app"
          // | project TimeGenerated, Log_s, RevisionName_s
          ```
          
          ## Environment Variable Updates
          
          Updating environment variables creates a new revision:
          
          ```bash
          # Set or update env vars
          az containerapp update -n $APP -g $RG \
            --set-env-vars "DB_HOST=newhost.postgres.database.azure.com" \
                           "CACHE_TTL=300"
          
          # Remove an env var
          az containerapp update -n $APP -g $RG \
            --remove-env-vars "OLD_SETTING"
          ```
          
          ### Bicep — Env Vars with Secret References
          
          ```bicep
          configuration: {
            secrets: [
              { name: 'db-password', value: dbPassword }  // or use keyVaultUrl + identity
            ]
          }
          template: {
            containers: [
              {
                name: 'api'
                image: '${acrName}.azurecr.io/api:latest'
                env: [
                  { name: 'DB_HOST', value: dbHost }
                  { name: 'DB_PASSWORD', secretRef: 'db-password' }
                ]
              }
            ]
          }
          ```
          
          ## Secret Management
          
          ### Create and Update Secrets
          
          ```bash
          # Add a secret (use Key Vault references in production — avoid plaintext secrets)
          az containerapp secret set -n $APP -g $RG \
            --secrets "db-password=<secret-value>"
          
          # Reference a Key Vault secret (managed identity required)
          az containerapp secret set -n $APP -g $RG \
            --secrets "db-password=keyvaultref:https://myvault.vault.azure.net/secrets/db-pwd,identityref:/subscriptions/.../userAssignedIdentities/my-id"
          ```
          
          > ⚠️ **Warning:** Avoid passing plaintext secrets on the command line — they may appear in shell history and process listings. Prefer Key Vault references. If you must use plaintext, use shell substitution like `--secrets "key=$(cat secret.txt)"` to avoid literals on the command line.
          
          > 💡 **Tip:** Use Key Vault references instead of plain-text secrets. The Container App pulls the latest value on each new revision or replica start.
          
          ### Secret Rotation Workflow
          
          1. Update the secret value in Key Vault
          2. Create a new revision to pick up the updated value:
             ```bash
             az containerapp revision copy -n $APP -g $RG
             ```
          3. Verify the new revision is healthy
          4. Shift traffic to the new revision
          
          > ⚠️ **Warning:** Existing replicas do NOT hot-reload Key Vault references. A new revision or replica restart is required.
          
          ## Health Monitoring
          
          | Check | How |
          |-------|-----|
          | Revision health | `az containerapp revision list -n $APP -g $RG -o table` |
          | Replica status | `az containerapp replica list -n $APP -g $RG --revision $REV` |
          | System logs | `az containerapp logs show -n $APP -g $RG --type system` |
          | Metrics | Azure Monitor → Container Apps → Requests, Replicas, CPU, Memory |
          
          ## Common Troubleshooting
          
          | Symptom | Likely Cause | Remediation |
          |---------|-------------|-------------|
          | Replica crash loop | App startup failure | Check console logs; exec into container |
          | 0 replicas running | Scale-to-zero + no traffic | Set `minReplicas: 1` or send a request |
          | Env var not updating | Old revision still serving traffic | Verify the latest revision exists, then update ingress traffic weights or route to `latestRevision` |
          | Secret value stale | Key Vault ref not refreshed | Create or verify the refreshed revision, then shift traffic to that revision |
          | High memory/CPU | Resource limits too low | Update `resources.cpu` / `resources.memory` |
          
        • environment.md 1.3 KB
          # Container Apps Environment Variables
          
          ## Standard Environment Variables
          
          ```bicep
          env: [
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: applicationInsights.properties.ConnectionString
            }
            {
              name: 'AZURE_CLIENT_ID'
              value: managedIdentity.properties.clientId
            }
          ]
          ```
          
          ## Secret References (Key Vault)
          
          Use secrets for sensitive values:
          
          ```bicep
          configuration: {
            secrets: [
              {
                name: 'database-url'
                keyVaultUrl: 'https://myvault.vault.azure.net/secrets/database-url'
                identity: managedIdentity.id
              }
            ]
          }
          
          template: {
            containers: [
              {
                env: [
                  {
                    name: 'DATABASE_URL'
                    secretRef: 'database-url'
                  }
                ]
              }
            ]
          }
          ```
          
          ## Common Variables
          
          | Variable | Source | Notes |
          |----------|--------|-------|
          | `APPLICATIONINSIGHTS_CONNECTION_STRING` | App Insights | Telemetry |
          | `AZURE_CLIENT_ID` | Managed Identity | SDK auth |
          | `DATABASE_URL` | Key Vault secret | Connection string |
          | `REDIS_URL` | Key Vault secret | Cache connection |
          
          ## Best Practices
          
          - Never hardcode secrets in Bicep
          - Use Key Vault references for all sensitive values
          - Use Managed Identity for authentication
          - Set `AZURE_CLIENT_ID` for SDK-based auth
          
        • health-probes.md 1.2 KB
          # Container Apps Health Probes
          
          Always configure health probes for production workloads.
          
          ## Liveness Probe
          
          Detects if container is alive. Failure triggers restart.
          
          ```bicep
          probes: [
            {
              type: 'liveness'
              httpGet: {
                path: '/health'
                port: 8080
              }
              initialDelaySeconds: 10
              periodSeconds: 30
              failureThreshold: 3
            }
          ]
          ```
          
          ## Readiness Probe
          
          Detects if container is ready to receive traffic.
          
          ```bicep
          probes: [
            {
              type: 'readiness'
              httpGet: {
                path: '/ready'
                port: 8080
              }
              initialDelaySeconds: 5
              periodSeconds: 10
              failureThreshold: 3
            }
          ]
          ```
          
          ## Startup Probe
          
          For slow-starting containers. Delays other probes until startup succeeds.
          
          ```bicep
          probes: [
            {
              type: 'startup'
              httpGet: {
                path: '/health'
                port: 8080
              }
              initialDelaySeconds: 0
              periodSeconds: 10
              failureThreshold: 30  // 30 * 10s = 5 min max startup
            }
          ]
          ```
          
          ## Recommendations
          
          | Probe | Path | Initial Delay | Period |
          |-------|------|---------------|--------|
          | Liveness | `/health` | 10s | 30s |
          | Readiness | `/ready` | 5s | 10s |
          | Startup | `/health` | 0s | 10s |
          
        • networking.md 5.1 KB
          # Container Apps Networking
          
          VNet integration, ingress configuration, custom domains, and TLS for Container Apps.
          
          ## Ingress Modes
          
          | Mode | Visibility | Use Case |
          |------|-----------|----------|
          | External | Internet-accessible | Public APIs, web apps |
          | Internal | Not internet-accessible; reachable within the environment and VNet (if VNet-injected) | Microservices, back-end APIs |
          | Disabled | No HTTP ingress | Background workers, queue processors |
          
          ### Bicep — External Ingress
          
          ```bicep
          configuration: {
            ingress: {
              external: true
              targetPort: 8080
              transport: 'auto'
              allowInsecure: false
            }
          }
          ```
          
          ### Bicep — Internal Ingress
          
          ```bicep
          configuration: {
            ingress: {
              external: false
              targetPort: 8080
            }
          }
          ```
          
          > 💡 **Tip:** Internal apps get a `*.internal.<env-default-domain>` FQDN. This is accessible from within the Container Apps environment and, when the environment is VNet-injected, also from the VNet.
          
          ## VNet Integration
          
          Container Apps run inside an environment that can be injected into a VNet subnet.
          
          ### Subnet Requirements
          
          | Requirement | Workload Profiles (default) | Consumption-only (legacy) |
          |------------|---------------------------|--------------------------|
          | Minimum subnet size | `/27` (32 addresses) | `/23` (512 addresses) |
          | Delegation | `Microsoft.App/environments` | `Microsoft.App/environments` |
          | Dedicated | Subnet must be exclusive to the Container Apps environment | Same |
          
          ### Bicep — VNet-Integrated Environment
          
          ```bicep
          resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = {
            parent: vnet
            name: 'container-apps-subnet'
            properties: {
              addressPrefix: '10.0.16.0/27'
              delegations: [
                {
                  name: 'Microsoft.App.environments'
                  properties: { serviceName: 'Microsoft.App/environments' }
                }
              ]
            }
          }
          
          resource env 'Microsoft.App/managedEnvironments@2024-03-01' = {
            name: envName
            location: location
            properties: {
              vnetConfiguration: {
                infrastructureSubnetId: subnet.id
                internal: false // true for internal-only environment
              }
            }
          }
          ```
          
          > ⚠️ **Warning:** VNet configuration is set at environment creation and cannot be changed afterward. Plan your network topology before creating the environment.
          
          ## Custom Domains
          
          ### Steps
          
          1. Add a CNAME or A record pointing to the Container App's FQDN or static IP
          2. Bind the custom domain to the Container App
          3. Configure a managed or custom TLS certificate
          
          ```bash
          # Register custom domain (hostname only — no cert provisioned yet)
          az containerapp hostname add -n $APP -g $RG --hostname app.contoso.com
          
          # Bind managed certificate (provisions and attaches TLS cert)
          az containerapp hostname bind -n $APP -g $RG \
            --hostname app.contoso.com \
            --environment $ENV_NAME \
            --validation-method CNAME
          ```
          
          ### DNS Configuration
          
          | Record Type | Name | Value |
          |------------|------|-------|
          | CNAME | `app.contoso.com` | `<app-name>.<region>.azurecontainerapps.io` |
          | TXT (verification) | `asuid.app.contoso.com` | `<verification-id>` |
          | A (apex domain) | `contoso.com` | Environment static IP |
          
          > 💡 **Tip:** Use `az containerapp show -n $APP -g $RG --query properties.configuration.ingress.fqdn` to get the target FQDN for DNS records.
          
          ## TLS Configuration
          
          ### Managed Certificates
          
          Azure automatically provisions and renews TLS certificates for custom domains — no manual cert management required.
          
          > ⚠️ **Prerequisites:** Managed certificates require the app to be externally reachable with valid **public** DNS (CNAME or HTTP validation). They do **not** work for internal environments or apps behind private DNS. For private/internal scenarios, bring your own certificate via `az containerapp ssl upload`.
          
          ## IP Restrictions
          
          > ⚠️ **Warning:** IP restriction rules are evaluated in **array order** (first match wins). The `priority` field does not exist in the Container Apps API — order your rules carefully in the array.
          
          Allow-only rules implicitly deny all traffic not matching any rule. Deny-only rules implicitly allow all other traffic.
          
          ```bicep
          configuration: {
            ingress: {
              external: true
              targetPort: 8080
              ipSecurityRestrictions: [
                {
                  name: 'allow-office'
                  action: 'Allow'
                  ipAddressRange: '203.0.113.0/24'
                  description: 'Office network'
                }
                {
                  name: 'allow-vpn'
                  action: 'Allow'
                  ipAddressRange: '198.51.100.0/24'
                  description: 'VPN gateway'
                }
              ]
            }
          }
          ```
          
          ## Network Topology Summary
          
          | Topology | Environment `internal` | Ingress `external` | Access |
          |----------|----------------------|-------------------|--------|
          | Public app | `false` | `true` | Internet + VNet |
          | Internal microservice | `false` | `false` | Same environment; VNet if environment is VNet-injected |
          | Fully private (VNet-wide) | `true` | `true` | VNet only (no public IP); accessible from anywhere in the VNet |
          | Fully private (env-only) | `true` | `false` | VNet only (no public IP); accessible only within the Container Apps environment |
          
          > ⚠️ **Warning:** An internal environment has no public IP. You need VPN, ExpressRoute, or a jump box to reach apps in an internal environment.
          
        • README.md 1.3 KB
          # Azure Container Apps
          
          Serverless container hosting for microservices, APIs, and background workers.
          
          ## When to Use
          
          - Microservices and APIs
          - Background processing workers
          - Event-driven applications
          - Web applications (server-rendered)
          - Any containerized workload that doesn't need full Kubernetes
          
          ## Service Type in azure.yaml
          
          ```yaml
          services:
            my-api:
              host: containerapp
              project: ./src/my-api
              docker:
                path: ./Dockerfile
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | Container Apps Environment | Hosting environment |
          | Container Registry | Image storage |
          | Log Analytics Workspace | Logging |
          | Application Insights | Monitoring |
          
          ## Common Configurations
          
          | Workload Type | Ingress | Min Replicas | Scaling |
          |---------------|---------|--------------|---------|
          | API Service | External | 1 (avoid cold starts) | HTTP-based |
          | Background Worker | None | 0 (scale to zero) | Queue-based |
          | Web Application | External | 1 | HTTP-based |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Terraform Patterns](terraform.md)
          - [Scaling Patterns](scaling.md)
          - [Health Probes](health-probes.md)
          - [Environment Variables](environment.md)
          - [Day-2 Operations](day2-operations.md)
          - [Networking & Ingress](networking.md)
          - [Revisions & Traffic Splitting](revisions.md)
          
        • revisions.md 4.9 KB
          # Container Apps Revision Management
          
          Revisions are immutable snapshots of a Container App version. Use them for blue/green deployments, canary releases, and instant rollback.
          
          ## Revision Modes
          
          | Mode | Behavior | Use Case |
          |------|----------|----------|
          | `Single` | New revision replaces old immediately | Simple apps, dev/test |
          | `Multiple` | Multiple revisions run simultaneously with traffic splitting | Production blue/green, canary |
          
          ## Setting Revision Mode (Bicep)
          
          ```bicep
          resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
            name: appName
            location: location
            properties: {
              configuration: {
                activeRevisionsMode: 'Multiple'
                ingress: {
                  external: true
                  targetPort: 8080
                  traffic: [
                    { latestRevision: true, weight: 100 }
                  ]
                }
              }
            }
          }
          ```
          
          > 💡 **Tip:** In Bicep/ARM deployments, you typically can't predictably target a specific new revision name. Use `latestRevision: true` in Bicep for initial deployment, then configure traffic splitting or labels via CLI after the new revision is created.
          
          > ⚠️ **Warning:** For blue/green workflows, you must first pin traffic to a named revision before deploying a new one. With `latestRevision: true, weight: 100`, new revisions automatically receive all traffic — there is no validation window.
          
          ## Traffic Splitting Patterns
          
          ### Blue/Green Deployment
          
          Pin traffic to the current revision, deploy a new one, validate, then switch:
          
          ```bash
          # Deploy new revision and capture its name from the update output
          NEW_REV=$(az containerapp update -n $APP -g $RG --image $NEW_IMAGE \
            --query properties.latestRevisionName -o tsv)
          
          # Test the new revision directly via its revision-specific URL
          az containerapp revision list -n $APP -g $RG -o table
          
          # Switch 100% traffic to the new revision
          az containerapp ingress traffic set -n $APP -g $RG \
            --revision-weight "$NEW_REV=100"
          ```
          
          ### Canary Release
          
          Gradually shift traffic to validate the new revision under load:
          
          | Phase | Current | Canary | Duration |
          |-------|---------|--------|----------|
          | 1 | 90% | 10% | 15 min |
          | 2 | 50% | 50% | 30 min |
          | 3 | 0% | 100% | — |
          
          ```bash
          # List revisions to identify current stable and new canary
          az containerapp revision list -n $APP -g $RG -o table
          
          STABLE_REV=<stable-revision-name>
          CANARY_REV=<canary-revision-name>
          
          az containerapp ingress traffic set -n $APP -g $RG \
            --revision-weight "$STABLE_REV=90" "$CANARY_REV=10"
          ```
          
          ### Label-Based Routing
          
          Use labels instead of revision names for stable references:
          
          ```bash
          # Assign labels
          az containerapp revision label add -n $APP -g $RG \
            --label stable --revision "$APP--v1"
          az containerapp revision label add -n $APP -g $RG \
            --label canary --revision "$APP--v2"
          
          # Route by label
          az containerapp ingress traffic set -n $APP -g $RG \
            --label-weight stable=80 canary=20
          ```
          
          > 💡 **Tip:** Label-based routing lets you swap revision targets without changing traffic rules.
          
          ## Rollback
          
          Revert instantly by redirecting all traffic to a known-good revision (e.g., one labeled `stable`):
          
          ```bash
          # List active revisions and identify the known-good one
          az containerapp revision list -n $APP -g $RG -o table
          
          # Roll back using label-based routing (preferred)
          az containerapp ingress traffic set -n $APP -g $RG \
            --label-weight stable=100
          
          # Or roll back to a specific revision by name
          az containerapp ingress traffic set -n $APP -g $RG \
            --revision-weight "<known-good-revision-name>=100"
          ```
          
          ## Revision Lifecycle
          
          | Action | Command |
          |--------|---------|
          | List revisions | `az containerapp revision list -n $APP -g $RG` |
          | Show revision details | `az containerapp revision show -n $APP -g $RG --revision $REV` |
          | Activate a revision | `az containerapp revision activate -n $APP -g $RG --revision $REV` |
          | Deactivate a revision | `az containerapp revision deactivate -n $APP -g $RG --revision $REV` |
          | Restart a revision | `az containerapp revision restart -n $APP -g $RG --revision $REV` |
          
          > ⚠️ **Warning:** Deactivated revisions cannot receive traffic. Reactivate before routing traffic to them.
          
          ## Terraform Traffic Config
          
          ```hcl
          resource "azurerm_container_app" "app" {
            name                         = var.app_name
            container_app_environment_id = azurerm_container_app_environment.env.id
            resource_group_name          = azurerm_resource_group.rg.name
            revision_mode                = "Multiple"
          
            ingress {
              external_enabled = true
              target_port      = 8080
              traffic_weight {
                label           = "stable"
                revision_suffix = "v1"
                percentage      = 80
              }
              traffic_weight {
                label           = "canary"
                revision_suffix = "v2"
                percentage      = 20
              }
            }
          }
          ```
          
          ## Recommendations
          
          | Scenario | Revision Mode | Traffic Strategy |
          |----------|---------------|------------------|
          | Dev/Test | Single | N/A — auto-replace |
          | Prod API | Multiple | Blue/green with instant swap |
          | High-risk change | Multiple | Canary (10% → 50% → 100%) |
          | Feature flags | Multiple | Label-based routing |
          
        • scaling.md 1.4 KB
          # Container Apps Scaling Patterns
          
          ## HTTP-based Scaling
          
          Best for APIs and web applications:
          
          ```bicep
          scale: {
            minReplicas: 1
            maxReplicas: 10
            rules: [
              {
                name: 'http-scaling'
                http: {
                  metadata: {
                    concurrentRequests: '100'
                  }
                }
              }
            ]
          }
          ```
          
          ## Queue-based Scaling
          
          Best for background workers:
          
          ```bicep
          scale: {
            minReplicas: 0
            maxReplicas: 30
            rules: [
              {
                name: 'queue-scaling'
                azureQueue: {
                  queueName: 'orders'
                  queueLength: 10
                  auth: [
                    {
                      secretRef: 'storage-connection'
                      triggerParameter: 'connection'
                    }
                  ]
                }
              }
            ]
          }
          ```
          
          ## Service Bus Scaling
          
          ```bicep
          scale: {
            minReplicas: 0
            maxReplicas: 20
            rules: [
              {
                name: 'servicebus-scaling'
                custom: {
                  type: 'azure-servicebus'
                  metadata: {
                    queueName: 'myqueue'
                    messageCount: '5'
                  }
                  auth: [
                    {
                      secretRef: 'servicebus-connection'
                      triggerParameter: 'connection'
                    }
                  ]
                }
              }
            ]
          }
          ```
          
          ## Recommendations
          
          | Workload | Min Replicas | Max Replicas | Rule Type |
          |----------|--------------|--------------|-----------|
          | Production API | 1 | 10-20 | HTTP |
          | Dev/Test API | 0 | 5 | HTTP |
          | Background Worker | 0 | 30+ | Queue/Event |
          | Scheduled Job | 0 | 1 | KEDA cron |
          
        • terraform.md 6.1 KB
          # Container Apps Terraform Patterns
          
          > **⚠️ Container Registry Naming:** ACR names must be alphanumeric only (5-50 characters). Use Terraform's `replace()` function when constructing the value passed to `azurecaf_name`, or otherwise strip hyphens manually.
          
          > **⚠️ Two-Phase Deployment (Mandatory):** To avoid the chicken-and-egg problem where Terraform tries to create a Container App referencing an ACR image that doesn't exist yet:
          > - **Phase 1 (`terraform apply`):** Deploy ACR and Container App with a **public placeholder image** and **no `registry` block**.
          > - **Phase 2 (post-apply CLI):** Build/push the app image to ACR, configure the registry/identity link, then update the Container App image.
          >
          > This mirrors the [Bicep two-phase pattern](bicep.md). Without it, `terraform apply` fails with `ContainerAppOperationError` because the image doesn't exist in ACR yet.
          
          > **⚠️ ACR Authentication — Managed Identity Only:** Do **not** use `admin_enabled = true` on `azurerm_container_registry` or add a `registry` block with `username`/`password_secret_name` to the Container App. Admin credentials are a security risk and leak secrets into Terraform state. Always use **managed identity** with an `AcrPull` role assignment, and configure the registry link via `az containerapp registry set --identity system` in Phase 2.
          
          ## Phase 1: Container App Resource (No Registry Block)
          
          ```hcl
          # Placeholder image allows provisioning before the app image exists in ACR.
          # No registry block in Terraform during Phase 1 — the registry/identity link is
          # configured via CLI after provisioning (see Phase 2 below).
          # Do NOT add a registry block with username/password_secret_name — use managed identity.
          resource "azurerm_container_app" "api" {
            name                         = azurecaf_name.container_app.result
            container_app_environment_id = azurerm_container_app_environment.env.id
            resource_group_name          = azurerm_resource_group.rg.name
            revision_mode                = "Single"
          
            identity {
              type = "SystemAssigned"
            }
          
            tags = merge(var.tags, {
              "azd-service-name" = "api"
            })
          
            template {
              min_replicas = 1
              max_replicas = 3
          
              container {
                name   = "api"
                image  = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
                cpu    = 0.25
                memory = "0.5Gi"
              }
            }
          
            ingress {
              external_enabled = true
              target_port      = 8080
          
              traffic_weight {
                latest_revision = true
                percentage      = 100
              }
            }
          
            # Phase 2 updates the image and configures the registry/identity link via CLI.
            # Prevent Terraform from reverting those changes on subsequent applies.
            # Do NOT add a registry block here — use `az containerapp registry set --identity system`.
            lifecycle {
              ignore_changes = [
                template[0].container[0].image,
                registry,
              ]
            }
          }
          ```
          
          ## AcrPull Role Assignment
          
          Deploy the `AcrPull` role assignment as a **separate resource** that depends on the Container App (to read its system-assigned identity principal ID). Neither the ACR nor the Container App depends on this resource, so there is no circular dependency.
          
          ```hcl
          resource "azurerm_role_assignment" "api_acr_pull" {
            scope                = azurerm_container_registry.acr.id
            role_definition_name = "AcrPull"
            principal_id         = azurerm_container_app.api.identity[0].principal_id
            principal_type       = "ServicePrincipal"
          }
          ```
          
          > 💡 **Tip:** Always set `principal_type = "ServicePrincipal"` for managed identities. This skips the Graph API lookup and speeds up role assignment propagation.
          
          ## Phase 2: Post-Apply Deployment (CLI)
          
          After `terraform apply` succeeds, run these commands to build the real image and switch the Container App to it:
          
          ```bash
          ACR_NAME=$(terraform output -raw acr_name)
          ACR_SERVER=$(terraform output -raw acr_login_server)
          APP_NAME=$(terraform output -raw container_app_name)
          RG_NAME=$(terraform output -raw resource_group_name)
          
          # 1. Build and push the application image to ACR
          az acr build --registry $ACR_NAME --image myapp:latest ./src/api
          
          # 2. Configure the registry/identity link (managed identity, no passwords)
          az containerapp registry set \
            --name $APP_NAME \
            --resource-group $RG_NAME \
            --server $ACR_SERVER \
            --identity system
          
          # 3. Update the Container App to use the real image
          az containerapp update \
            --name $APP_NAME \
            --resource-group $RG_NAME \
            --image $ACR_SERVER/myapp:latest
          ```
          
          **PowerShell:**
          ```powershell
          $AcrName = terraform output -raw acr_name
          $AcrServer = terraform output -raw acr_login_server
          $AppName = terraform output -raw container_app_name
          $RgName = terraform output -raw resource_group_name
          
          # 1. Build and push the application image to ACR
          az acr build --registry $AcrName --image myapp:latest ./src/api
          
          # 2. Configure the registry/identity link (managed identity, no passwords)
          az containerapp registry set `
            --name $AppName `
            --resource-group $RgName `
            --server $AcrServer `
            --identity system
          
          # 3. Update the Container App to use the real image
          az containerapp update `
            --name $AppName `
            --resource-group $RgName `
            --image "$AcrServer/myapp:latest"
          ```
          
          > ⚠️ **Warning:** Step 2 requires the `AcrPull` role assignment to have propagated (1–5 minutes after `terraform apply`). If the image pull fails, wait and retry. See the **azure-deploy** skill's `references/pre-deploy-checklist.md` (Container Apps + ACR pre-deploy RBAC health check).
          
          ## Terraform Outputs
          
          Export the values needed by Phase 2:
          
          ```hcl
          output "acr_name" {
            value = azurerm_container_registry.acr.name
          }
          
          output "acr_login_server" {
            value = azurerm_container_registry.acr.login_server
          }
          
          output "container_app_name" {
            value = azurerm_container_app.api.name
          }
          
          output "resource_group_name" {
            value = azurerm_resource_group.rg.name
          }
          
          output "api_url" {
            value = "https://${azurerm_container_app.api.ingress[0].fqdn}"
          }
          ```
          
          ## Container Apps Environment
          
          ```hcl
          resource "azurerm_container_app_environment" "env" {
            name                       = azurecaf_name.container_app_env.result
            location                   = azurerm_resource_group.rg.location
            resource_group_name        = azurerm_resource_group.rg.name
            log_analytics_workspace_id = azurerm_log_analytics_workspace.logs.id
          }
          ```
          
      • cosmos-db
        • bicep.md 2 KB
          # Cosmos DB Bicep Patterns
          
          ## Account
          
          ```bicep
          resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
            name: '${resourcePrefix}-cosmos-${uniqueHash}'
            location: location
            kind: 'GlobalDocumentDB'
            properties: {
              databaseAccountOfferType: 'Standard'
              locations: [
                {
                  locationName: location
                  failoverPriority: 0
                  isZoneRedundant: false
                }
              ]
              consistencyPolicy: {
                defaultConsistencyLevel: 'Session'
              }
              capabilities: [
                {
                  name: 'EnableServerless'
                }
              ]
            }
          }
          ```
          
          ## Database
          
          ```bicep
          resource cosmosDatabase 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2023-04-15' = {
            parent: cosmosAccount
            name: 'appdb'
            properties: {
              resource: {
                id: 'appdb'
              }
            }
          }
          ```
          
          ## Container
          
          ```bicep
          resource cosmosContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-04-15' = {
            parent: cosmosDatabase
            name: 'items'
            properties: {
              resource: {
                id: 'items'
                partitionKey: {
                  paths: ['/partitionKey']
                  kind: 'Hash'
                }
                indexingPolicy: {
                  indexingMode: 'consistent'
                  includedPaths: [
                    { path: '/*' }
                  ]
                }
              }
            }
          }
          ```
          
          ## Autoscale Container
          
          ```bicep
          resource cosmosContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-04-15' = {
            parent: cosmosDatabase
            name: 'items'
            properties: {
              resource: {
                id: 'items'
                partitionKey: {
                  paths: ['/partitionKey']
                  kind: 'Hash'
                }
              }
              options: {
                autoscaleSettings: {
                  maxThroughput: 4000
                }
              }
            }
          }
          ```
          
          ## Global Distribution
          
          ```bicep
          properties: {
            locations: [
              {
                locationName: 'East US'
                failoverPriority: 0
              }
              {
                locationName: 'West US'
                failoverPriority: 1
              }
            ]
            enableMultipleWriteLocations: true
          }
          ```
          
        • partitioning.md 1.1 KB
          # Cosmos DB Partition Key Selection
          
          ## Good Partition Keys
          
          A good partition key should have:
          
          - **High cardinality** - Many distinct values
          - **Even data distribution** - No hot partitions
          - **Even request distribution** - Balanced workload
          - **Used in most queries** - Enables efficient routing
          
          ## Examples by Scenario
          
          | Scenario | Partition Key | Reason |
          |----------|---------------|--------|
          | User-centric data | `/userId` | Queries typically filter by user |
          | Multi-tenant apps | `/tenantId` | Isolates tenant data |
          | E-commerce orders | `/customerId` | Orders queried by customer |
          | IoT telemetry | `/deviceId` | High cardinality, even distribution |
          
          ## Hierarchical Partition Keys
          
          For complex scenarios, use hierarchical keys:
          
          ```bicep
          partitionKey: {
            paths: ['/tenantId', '/userId']
            kind: 'MultiHash'
          }
          ```
          
          ## Anti-Patterns
          
          Avoid these partition key choices:
          
          | Bad Choice | Problem |
          |------------|---------|
          | Timestamp | Creates hot partitions |
          | Boolean values | Only 2 partitions |
          | Low cardinality enums | Uneven distribution |
          | Random GUID | Can't query efficiently |
          
        • README.md 1.5 KB
          # Azure Cosmos DB
          
          Globally distributed, multi-model database for low-latency data at scale.
          
          ## When to Use
          
          - Global distribution requirements
          - Multi-model data (document, graph, key-value)
          - Variable and unpredictable throughput
          - Low-latency reads/writes at scale
          - Flexible schema requirements
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | None required | Cosmos DB is fully managed |
          | Key Vault | Store connection strings (recommended) |
          
          ## Capacity Modes
          
          | Mode | Use Case | Billing |
          |------|----------|---------|
          | **Serverless** | Variable/low traffic, dev/test | Per request |
          | **Provisioned** | Predictable workloads | Per RU/s |
          | **Autoscale** | Variable but predictable peaks | Per max RU/s |
          
          ## Consistency Levels
          
          | Level | Latency | Consistency |
          |-------|---------|-------------|
          | Strong | Highest | Linearizable |
          | Bounded Staleness | High | Bounded |
          | Session | Medium | Session-scoped |
          | Consistent Prefix | Low | Prefix ordering |
          | Eventual | Lowest | Eventually consistent |
          
          Recommendation: Use **Session** for most applications.
          
          ## Environment Variables
          
          | Variable | Value |
          |----------|-------|
          | `COSMOS_CONNECTION_STRING` | Primary connection string (Key Vault reference) |
          | `COSMOS_ENDPOINT` | Account endpoint URL |
          | `COSMOS_DATABASE` | Database name |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Partition Key Selection](partitioning.md)
          - [SDK Connection Patterns](sdk.md)
          
        • sdk.md 1.6 KB
          # Cosmos DB SDK Connection Patterns
          
          ## Node.js
          
          ```javascript
          const { CosmosClient } = require("@azure/cosmos");
          
          const client = new CosmosClient(process.env.COSMOS_CONNECTION_STRING);
          const database = client.database("appdb");
          const container = database.container("items");
          
          // Query example
          const { resources } = await container.items
            .query("SELECT * FROM c WHERE c.userId = @userId", {
              parameters: [{ name: "@userId", value: userId }]
            })
            .fetchAll();
          ```
          
          ## Python
          
          ```python
          from azure.cosmos import CosmosClient
          import os
          
          client = CosmosClient.from_connection_string(os.environ["COSMOS_CONNECTION_STRING"])
          database = client.get_database_client("appdb")
          container = database.get_container_client("items")
          
          # Query example
          items = container.query_items(
              query="SELECT * FROM c WHERE c.userId = @userId",
              parameters=[{"name": "@userId", "value": user_id}]
          )
          ```
          
          ## .NET
          
          ```csharp
          using Microsoft.Azure.Cosmos;
          
          var client = new CosmosClient(Environment.GetEnvironmentVariable("COSMOS_CONNECTION_STRING"));
          var database = client.GetDatabase("appdb");
          var container = database.GetContainer("items");
          
          // Query example
          var query = new QueryDefinition("SELECT * FROM c WHERE c.userId = @userId")
              .WithParameter("@userId", userId);
          var iterator = container.GetItemQueryIterator<dynamic>(query);
          ```
          
          ## Best Practices
          
          | Practice | Reason |
          |----------|--------|
          | Reuse client instances | Connection pooling |
          | Use parameterized queries | SQL injection prevention |
          | Set appropriate timeouts | Handle transient failures |
          | Enable diagnostics in dev | Debug RU consumption |
          
      • durable-task-scheduler
        • bicep.md 4.8 KB
          # Durable Task Scheduler — Bicep Patterns
          
          Bicep templates for provisioning the Durable Task Scheduler, task hubs, and RBAC role assignments.
          
          ## Scheduler + Task Hub
          
          ```bicep
          // Parameters — define these at file level or pass from a parent module
          param schedulerName string
          param location string = resourceGroup().location
          
          @allowed(['Consumption', 'Dedicated'])
          @description('Use Consumption for quickstarts/variable workloads, Dedicated for high-demand/predictable throughput')
          param skuName string = 'Consumption'
          
          resource scheduler 'Microsoft.DurableTask/schedulers@2025-11-01' = {
            name: schedulerName
            location: location
            properties: {
              sku: { name: skuName }
              ipAllowlist: ['0.0.0.0/0'] // Required: empty list denies all traffic
            }
          }
          
          resource taskHub 'Microsoft.DurableTask/schedulers/taskHubs@2025-11-01' = {
            parent: scheduler
            name: 'default'
          }
          ```
          
          ## SKU Selection
          
          | SKU | Best For |
          |-----|----------|
          | **Consumption** | quickstarts, variable or bursty workloads, pay-per-use |
          | **Dedicated** | High-demand workloads, predictable throughput requirements |
          
          > **💡 TIP**: Start with `Consumption` for development and variable workloads. Switch to `Dedicated` when you need consistent, high-throughput performance.
          
          > **⚠️ WARNING**: The scheduler's `ipAllowlist` **must** include at least one entry (e.g., `['0.0.0.0/0']` for allow-all). An empty array `[]` denies **all** traffic, causing 403 errors on gRPC calls even with correct RBAC.
          
          ## RBAC — Durable Task Data Contributor
          
          The Function App's managed identity **must** have the `Durable Task Data Contributor` role on the scheduler resource. Without it, the app receives **403 PermissionDenied** on gRPC calls.
          
          ```bicep
          // Assumes the UAMI principal ID is passed from the base template's identity module
          param functionAppPrincipalId string
          
          var durableTaskDataContributorRoleId = '0ad04412-c4d5-4796-b79c-f76d14c8d402'
          
          resource durableTaskRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(scheduler.id, functionAppPrincipalId, durableTaskDataContributorRoleId)
            scope: scheduler
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', durableTaskDataContributorRoleId)
              principalId: functionAppPrincipalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          ## RBAC — Dashboard Access for Developers
          
          To allow developers to view orchestration status and history in the [DTS dashboard](https://portal.azure.com), assign the same `Durable Task Data Contributor` role to the deploying user's identity. Without this, the dashboard returns **403 Forbidden**.
          
          ```bicep
          // Accept the deploying user's principal ID (azd auto-populates this from AZURE_PRINCIPAL_ID)
          param principalId string = ''
          
          resource dashboardRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(principalId)) {
            name: guid(scheduler.id, principalId, durableTaskDataContributorRoleId)
            scope: scheduler
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', durableTaskDataContributorRoleId)
              principalId: principalId
              principalType: 'User'
            }
          }
          ```
          
          > **💡 TIP**: This is the same role used for the Function App's managed identity, but assigned with `principalType: 'User'` to the developer. See the [sample repo](https://github.com/Azure-Samples/Durable-Task-Scheduler/blob/main/samples/infra/main.bicep) for a full example.
          
          ## Connection String App Setting
          
          Include these entries in the Function App resource's `siteConfig.appSettings` array:
          
          ```bicep
          // UAMI client ID from base template identity module - REQUIRED for UAMI auth
          param uamiClientId string
          
          {
            name: 'DURABLE_TASK_SCHEDULER_CONNECTION_STRING'
            value: 'Endpoint=${scheduler.properties.endpoint};TaskHub=${taskHub.name};Authentication=ManagedIdentity;ClientID=${uamiClientId}'
          }
          ```
          
          > **⚠️ IMPORTANT**: The base templates use User Assigned Managed Identity (UAMI). You **must** include `ClientID=<uami-client-id>` in the connection string. Without it, the Durable Task SDK cannot resolve the correct identity.
          
          > **⚠️ WARNING**: Always use `scheduler.properties.endpoint` to get the scheduler URL. Do **not** construct it manually — the endpoint includes a hash suffix and region (e.g., `https://myscheduler-abc123.westus2.durabletask.io`).
          
          ## Provision via CLI
          
          > **💡 TIP**: When hosting Durable Functions, use a **Flex Consumption** plan (`FC1` SKU) rather than the legacy Consumption plan (`Y1`). Flex Consumption supports identity-based storage connections natively and handles deployment artifacts correctly.
          
          ```bash
          # Install the durabletask CLI extension (if not already installed)
          az extension add --name durabletask
          
          # Create scheduler (consumption SKU for getting started)
          az durabletask scheduler create \
              --resource-group myResourceGroup \
              --name my-scheduler \
              --location eastus \
              --sku consumption
          ```
          
        • dotnet.md 6.1 KB
          # Durable Task Scheduler — .NET
          
          ## Learn More
          
          - [Durable Task Scheduler documentation](https://learn.microsoft.com/azure/durable-task-scheduler/)
          - [Durable Functions .NET isolated worker guide](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-dotnet-isolated-overview)
          
          ## Durable Functions Setup
          
          ### Required NuGet Packages
          
          ```xml
          <ItemGroup>
            <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.14.1" />
            <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged" Version="1.4.0" />
            <PackageReference Include="Azure.Identity" Version="1.17.1" />
          </ItemGroup>
          ```
          
          > **💡 Finding latest versions**: Search [nuget.org](https://www.nuget.org/) for each package name to find the current stable version. Look for the `Microsoft.Azure.Functions.Worker.Extensions.DurableTask` and `Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged` packages.
          
          ### host.json
          
          ```json
          {
            "version": "2.0",
            "extensions": {
              "durableTask": {
                "storageProvider": {
                  "type": "azureManaged",
                  "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
                },
                "hubName": "default"
              }
            }
          }
          ```
          
          > **💡 NOTE**: .NET isolated uses the `DurableTask.AzureManaged` NuGet package, which registers the `azureManaged` storage provider type. Other runtimes (Python, Java, JavaScript) use extension bundles and require `durabletask-scheduler` instead — see the respective language files. All runtimes use the same `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` environment variable.
          
          ### local.settings.json
          
          ```json
          {
            "IsEncrypted": false,
            "Values": {
              "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
              "AzureWebJobsStorage": "UseDevelopmentStorage=true",
              "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
            }
          }
          ```
          
          ## Minimal Example
          
          ```csharp
          using Microsoft.Azure.Functions.Worker;
          using Microsoft.DurableTask;
          using Microsoft.DurableTask.Client;
          
          public static class DurableFunctionsApp
          {
              [Function("HttpStart")]
              public static async Task<HttpResponseData> HttpStart(
                  [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
                  [DurableClient] DurableTaskClient client)
              {
                  string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(MyOrchestration));
                  return await client.CreateCheckStatusResponseAsync(req, instanceId);
              }
          
              [Function(nameof(MyOrchestration))]
              public static async Task<string> MyOrchestration([OrchestrationTrigger] TaskOrchestrationContext context)
              {
                  var result1 = await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo");
                  var result2 = await context.CallActivityAsync<string>(nameof(SayHello), "Seattle");
                  return $"{result1}, {result2}";
              }
          
              [Function(nameof(SayHello))]
              public static string SayHello([ActivityTrigger] string name) => $"Hello {name}!";
          }
          ```
          
          ## Workflow Patterns
          
          ### Fan-Out/Fan-In
          
          ```csharp
          [Function(nameof(FanOutFanIn))]
          public static async Task<string[]> FanOutFanIn([OrchestrationTrigger] TaskOrchestrationContext context)
          {
              string[] cities = { "Tokyo", "Seattle", "London", "Paris", "Berlin" };
          
              // Fan-out: schedule all in parallel
              var tasks = cities.Select(city => context.CallActivityAsync<string>(nameof(SayHello), city));
          
              // Fan-in: wait for all
              return await Task.WhenAll(tasks);
          }
          ```
          
          ### Human Interaction
          
          ```csharp
          [Function(nameof(ApprovalWorkflow))]
          public static async Task<string> ApprovalWorkflow([OrchestrationTrigger] TaskOrchestrationContext context)
          {
              await context.CallActivityAsync(nameof(SendApprovalRequest), context.GetInput<string>());
              
              // Wait for approval event with timeout
              using var cts = new CancellationTokenSource();
              var approvalTask = context.WaitForExternalEvent<bool>("ApprovalEvent");
              var timeoutTask = context.CreateTimer(context.CurrentUtcDateTime.AddDays(3), cts.Token);
              
              var winner = await Task.WhenAny(approvalTask, timeoutTask);
              
              if (winner == approvalTask)
              {
                  cts.Cancel();
                  return await approvalTask ? "Approved" : "Rejected";
              }
              return "Timed out";
          }
          ```
          
          ## Orchestration Determinism
          
          | ❌ NEVER | ✅ ALWAYS USE |
          |----------|--------------|
          | `DateTime.Now` | `context.CurrentUtcDateTime` |
          | `Guid.NewGuid()` | `context.NewGuid()` |
          | `Random` | Pass random values from activities |
          | `Task.Delay()`, `Thread.Sleep()` | `context.CreateTimer()` |
          | Direct I/O, HTTP, database | `context.CallActivityAsync()` |
          
          ### Replay-Safe Logging
          
          ```csharp
          [Function(nameof(MyOrchestration))]
          public static async Task<string> MyOrchestration([OrchestrationTrigger] TaskOrchestrationContext context)
          {
              ILogger logger = context.CreateReplaySafeLogger(nameof(MyOrchestration));
              logger.LogInformation("Started");  // Only logs once, not on replay
              return await context.CallActivityAsync<string>(nameof(MyActivity), "input");
          }
          ```
          
          ## Error Handling & Retry
          
          ```csharp
          var retryOptions = new TaskOptions
          {
              Retry = new RetryPolicy(
                  maxNumberOfAttempts: 3,
                  firstRetryInterval: TimeSpan.FromSeconds(5),
                  backoffCoefficient: 2.0,
                  maxRetryInterval: TimeSpan.FromMinutes(1))
          };
          
          var input = context.GetInput<string>();
          
          try
          {
              await context.CallActivityAsync<string>(nameof(UnreliableService), input, retryOptions);
          }
          catch (TaskFailedException ex)
          {
              context.SetCustomStatus(new { Error = ex.Message });
              await context.CallActivityAsync(nameof(CompensationActivity), input);
          }
          ```
          
          ## Durable Task SDK (Non-Functions)
          
          For applications running outside Azure Functions (containers, VMs, Azure Container Apps, Azure Kubernetes Service):
          
          ```csharp
          var connectionString = "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None";
          
          // Worker
          builder.Services.AddDurableTaskWorker()
              .AddTasks(registry => registry.AddAllGeneratedTasks())
              .UseDurableTaskScheduler(connectionString);
          
          // Client
          var client = DurableTaskClientBuilder.UseDurableTaskScheduler(connectionString).Build();
          string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("MyOrchestration", input);
          ```
          
          
        • java.md 7.7 KB
          # Durable Task Scheduler — Java
          
          ## Learn More
          
          - [Durable Task Scheduler documentation](https://learn.microsoft.com/azure/durable-task-scheduler/)
          - [Durable Functions Java guide](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview?tabs=java)
          
          ## Durable Functions Setup
          
          ### Required Maven Dependencies
          
          ```xml
          <dependencies>
            <dependency>
              <groupId>com.microsoft.azure.functions</groupId>
              <artifactId>azure-functions-java-library</artifactId>
              <version>3.2.3</version>
            </dependency>
            <dependency>
              <groupId>com.microsoft</groupId>
              <artifactId>durabletask-azure-functions</artifactId>
              <version>1.7.0</version>
            </dependency>
          </dependencies>
          ```
          
          > **💡 Finding latest versions**: Search [Maven Central](https://central.sonatype.com/) for `durabletask-azure-functions` (group: `com.microsoft`) to find the current stable version.
          
          ### host.json
          
          ```json
          {
            "version": "2.0",
            "extensions": {
              "durableTask": {
                "hubName": "default",
                "storageProvider": {
                  "type": "durabletask-scheduler",
                  "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
                }
              }
            },
            "extensionBundle": {
              "id": "Microsoft.Azure.Functions.ExtensionBundle",
              "version": "[4.*, 5.0.0)"
            }
          }
          ```
          
          ### local.settings.json
          
          ```json
          {
            "IsEncrypted": false,
            "Values": {
              "FUNCTIONS_WORKER_RUNTIME": "java",
              "AzureWebJobsStorage": "UseDevelopmentStorage=true",
              "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
            }
          }
          ```
          
          ## Minimal Example
          
          ```java
          import com.microsoft.azure.functions.*;
          import com.microsoft.azure.functions.annotation.*;
          import com.microsoft.durabletask.*;
          import com.microsoft.durabletask.azurefunctions.*;
          
          public class DurableFunctionsApp {
          
              @FunctionName("HttpStart")
              public HttpResponseMessage httpStart(
                      @HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION)
                      HttpRequestMessage<Void> request,
                      @DurableClientInput(name = "durableContext") DurableClientContext durableContext) {
                  DurableTaskClient client = durableContext.getClient();
                  String instanceId = client.scheduleNewOrchestrationInstance("MyOrchestration");
                  return durableContext.createCheckStatusResponse(request, instanceId);
              }
          
              @FunctionName("MyOrchestration")
              public String myOrchestration(
                      @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
                  String result1 = ctx.callActivity("SayHello", "Tokyo", String.class).await();
                  String result2 = ctx.callActivity("SayHello", "Seattle", String.class).await();
                  return result1 + ", " + result2;
              }
          
              @FunctionName("SayHello")
              public String sayHello(@DurableActivityTrigger(name = "name") String name) {
                  return "Hello " + name + "!";
              }
          }
          ```
          
          ## Workflow Patterns
          
          ### Fan-Out/Fan-In
          
          ```java
          @FunctionName("FanOutFanIn")
          public List<String> fanOutFanIn(
                  @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
              String[] cities = {"Tokyo", "Seattle", "London", "Paris", "Berlin"};
              List<Task<String>> parallelTasks = new ArrayList<>();
          
              // Fan-out: schedule all activities in parallel
              for (String city : cities) {
                  parallelTasks.add(ctx.callActivity("SayHello", city, String.class));
              }
          
              // Fan-in: wait for all to complete
              List<String> results = new ArrayList<>();
              for (Task<String> task : parallelTasks) {
                  results.add(task.await());
              }
          
              return results;
          }
          ```
          
          ### Human Interaction
          
          ```java
          @FunctionName("ApprovalWorkflow")
          public String approvalWorkflow(
                  @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
              ctx.callActivity("SendApprovalRequest", ctx.getInput(String.class)).await();
          
              // Wait for approval event with timeout
              Task<Boolean> approvalTask = ctx.waitForExternalEvent("ApprovalEvent", Boolean.class);
              Task<Void> timeoutTask = ctx.createTimer(Duration.ofDays(3));
          
              Task<?> winner = ctx.anyOf(approvalTask, timeoutTask).await();
          
              if (winner == approvalTask) {
                  return approvalTask.await() ? "Approved" : "Rejected";
              }
              return "Timed out";
          }
          ```
          
          ## Orchestration Determinism
          
          | ❌ NEVER | ✅ ALWAYS USE |
          |----------|--------------|
          | `System.currentTimeMillis()` | `ctx.getCurrentInstant()` |
          | `UUID.randomUUID()` | Pass random values from activities |
          | `Thread.sleep()` | `ctx.createTimer()` |
          | Direct I/O, HTTP, database | `ctx.callActivity()` |
          
          ### Replay-Safe Logging
          
          ```java
          private static final java.util.logging.Logger logger =
              java.util.logging.Logger.getLogger("MyOrchestration");
          
          @FunctionName("MyOrchestration")
          public String myOrchestration(
                  @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
              // Use isReplaying to avoid duplicate logs
              if (!ctx.getIsReplaying()) {
                  logger.info("Started");  // Only logs once, not on replay
              }
              return ctx.callActivity("MyActivity", "input", String.class).await();
          }
          ```
          
          ## Error Handling & Retry
          
          ```java
          @FunctionName("WorkflowWithRetry")
          public String workflowWithRetry(
                  @DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
              TaskOptions retryOptions = new TaskOptions(new RetryPolicy(
                  3,  // maxNumberOfAttempts
                  Duration.ofSeconds(5)  // firstRetryInterval
              ));
          
              try {
                  return ctx.callActivity("UnreliableService", ctx.getInput(String.class),
                          retryOptions, String.class).await();
              } catch (TaskFailedException ex) {
                  ctx.setCustomStatus(Map.of("Error", ex.getMessage()));
                  ctx.callActivity("CompensationActivity", ctx.getInput(String.class)).await();
                  return "Compensated";
              }
          }
          ```
          
          ## Durable Task SDK (Non-Functions)
          
          For applications running outside Azure Functions (containers, VMs, Azure Container Apps, Azure Kubernetes Service):
          
          ```java
          import com.microsoft.durabletask.*;
          import com.microsoft.durabletask.azuremanaged.DurableTaskSchedulerWorkerExtensions;
          import com.microsoft.durabletask.azuremanaged.DurableTaskSchedulerClientExtensions;
          
          import java.time.Duration;
          
          public class App {
              public static void main(String[] args) throws Exception {
                  String connectionString = "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None";
          
                  // Worker
                  DurableTaskGrpcWorker worker = DurableTaskSchedulerWorkerExtensions
                      .createWorkerBuilder(connectionString)
                      .addOrchestration(new TaskOrchestrationFactory() {
                          @Override public String getName() { return "MyOrchestration"; }
                          @Override public TaskOrchestration create() {
                              return ctx -> {
                                  String result = ctx.callActivity("SayHello",
                                          ctx.getInput(String.class), String.class).await();
                                  ctx.complete(result);
                              };
                          }
                      })
                      .addActivity(new TaskActivityFactory() {
                          @Override public String getName() { return "SayHello"; }
                          @Override public TaskActivity create() {
                              return ctx -> "Hello " + ctx.getInput(String.class) + "!";
                          }
                      })
                      .build();
          
                  worker.start();
          
                  // Client
                  DurableTaskClient client = DurableTaskSchedulerClientExtensions
                      .createClientBuilder(connectionString).build();
                  String instanceId = client.scheduleNewOrchestrationInstance("MyOrchestration", "World");
                  OrchestrationMetadata result = client.waitForInstanceCompletion(
                          instanceId, Duration.ofSeconds(30), true);
                  System.out.println("Output: " + result.readOutputAs(String.class));
          
                  worker.stop();
              }
          }
          ```
          
          
        • javascript.md 5.6 KB
          # Durable Task Scheduler — JavaScript
          
          ## Learn More
          
          - [Durable Task Scheduler documentation](https://learn.microsoft.com/azure/durable-task-scheduler/)
          - [Durable Functions JavaScript guide](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview?tabs=javascript)
          
          ## Durable Functions Setup
          
          ### Required npm Packages
          
          ```json
          {
            "dependencies": {
              "@azure/functions": "^4.0.0",
              "durable-functions": "^3.0.0"
            }
          }
          ```
          
          > **💡 Finding latest versions**: Run `npm view durable-functions version` or check [npmjs.com/package/durable-functions](https://www.npmjs.com/package/durable-functions) for the latest stable release.
          
          ### host.json
          
          ```json
          {
            "version": "2.0",
            "extensions": {
              "durableTask": {
                "hubName": "default",
                "storageProvider": {
                  "type": "durabletask-scheduler",
                  "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
                }
              }
            },
            "extensionBundle": {
              "id": "Microsoft.Azure.Functions.ExtensionBundle",
              "version": "[4.*, 5.0.0)"
            }
          }
          ```
          
          ### local.settings.json
          
          ```json
          {
            "IsEncrypted": false,
            "Values": {
              "FUNCTIONS_WORKER_RUNTIME": "node",
              "AzureWebJobsStorage": "UseDevelopmentStorage=true",
              "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
            }
          }
          ```
          
          ## Minimal Example
          
          ```javascript
          const { app } = require("@azure/functions");
          const df = require("durable-functions");
          
          // Activity
          df.app.activity("sayHello", {
            handler: (city) => `Hello ${city}!`,
          });
          
          // Orchestrator
          df.app.orchestration("myOrchestration", function* (context) {
            const result1 = yield context.df.callActivity("sayHello", "Tokyo");
            const result2 = yield context.df.callActivity("sayHello", "Seattle");
            return `${result1}, ${result2}`;
          });
          
          // HTTP Starter
          app.http("HttpStart", {
            route: "orchestrators/{orchestrationName}",
            methods: ["POST"],
            authLevel: "function",
            extraInputs: [df.input.durableClient()],
            handler: async (request, context) => {
              const client = df.getClient(context);
              const instanceId = await client.startNew(request.params.orchestrationName);
              return client.createCheckStatusResponse(request, instanceId);
            },
          });
          ```
          
          ## Workflow Patterns
          
          ### Fan-Out/Fan-In
          
          ```javascript
          df.app.orchestration("fanOutFanIn", function* (context) {
            const cities = ["Tokyo", "Seattle", "London", "Paris", "Berlin"];
          
            // Fan-out: schedule all activities in parallel
            const tasks = cities.map((city) => context.df.callActivity("sayHello", city));
          
            // Fan-in: wait for all to complete
            const results = yield context.df.Task.all(tasks);
            return results;
          });
          ```
          
          ### Human Interaction
          
          ```javascript
          df.app.orchestration("approvalWorkflow", function* (context) {
            yield context.df.callActivity("sendApprovalRequest", context.df.getInput());
          
            // Wait for approval event with timeout
            const expiration = new Date(context.df.currentUtcDateTime);
            expiration.setDate(expiration.getDate() + 3);
          
            const approvalTask = context.df.waitForExternalEvent("ApprovalEvent");
            const timeoutTask = context.df.createTimer(expiration);
          
            const winner = yield context.df.Task.any([approvalTask, timeoutTask]);
          
            if (winner === approvalTask) {
              return approvalTask.result ? "Approved" : "Rejected";
            }
            return "Timed out";
          });
          ```
          
          ## Orchestration Determinism
          
          | ❌ NEVER | ✅ ALWAYS USE |
          |----------|--------------|
          | `new Date()` | `context.df.currentUtcDateTime` |
          | `Math.random()` | Pass random values from activities |
          | `setTimeout()` | `context.df.createTimer()` |
          | Direct I/O, HTTP, database | `context.df.callActivity()` |
          
          ### Replay-Safe Logging
          
          ```javascript
          df.app.orchestration("myOrchestration", function* (context) {
            if (!context.df.isReplaying) {
              console.log("Started");  // Only logs once, not on replay
            }
            const result = yield context.df.callActivity("myActivity", "input");
            return result;
          });
          ```
          
          ## Error Handling & Retry
          
          ```javascript
          df.app.orchestration("workflowWithRetry", function* (context) {
            const retryOptions = new df.RetryOptions(5000, 3); // firstRetryInterval, maxAttempts
            retryOptions.backoffCoefficient = 2.0;
            retryOptions.maxRetryIntervalInMilliseconds = 60000;
          
            try {
              const result = yield context.df.callActivityWithRetry(
                "unreliableService",
                retryOptions,
                context.df.getInput()
              );
              return result;
            } catch (ex) {
              context.df.setCustomStatus({ error: ex.message });
              yield context.df.callActivity("compensationActivity", context.df.getInput());
              return "Compensated";
            }
          });
          ```
          
          ## Durable Task SDK (Non-Functions)
          
          For applications running outside Azure Functions (containers, VMs, Azure Container Apps, Azure Kubernetes Service):
          
          ```javascript
          const { createAzureManagedWorkerBuilder, createAzureManagedClient } = require("@microsoft/durabletask-js-azuremanaged");
          
          const connectionString = "Endpoint=http://localhost:8080;Authentication=None;TaskHub=default";
          
          // Activity
          const sayHello = async (_ctx, name) => `Hello ${name}!`;
          
          // Orchestrator
          const myOrchestration = async function* (ctx, name) {
            const result = yield ctx.callActivity(sayHello, name);
            return result;
          };
          
          async function main() {
            // Worker
            const worker = createAzureManagedWorkerBuilder(connectionString)
              .addOrchestrator(myOrchestration)
              .addActivity(sayHello)
              .build();
          
            await worker.start();
          
            // Client
            const client = createAzureManagedClient(connectionString);
            const instanceId = await client.scheduleNewOrchestration("myOrchestration", "World");
            const state = await client.waitForOrchestrationCompletion(instanceId, true, 30);
            console.log("Output:", state.serializedOutput);
          
            await client.stop();
            await worker.stop();
          }
          
          main().catch(console.error);
          ```
          
          
        • python.md 6.4 KB
          # Durable Task Scheduler — Python
          
          ## Learn More
          
          - [Durable Task Scheduler documentation](https://learn.microsoft.com/azure/durable-task-scheduler/)
          - [Durable Functions Python guide](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview?tabs=python)
          
          ## Durable Functions Setup
          
          ### Required Packages
          
          ```txt
          # requirements.txt
          azure-functions
          azure-functions-durable
          azure-identity
          ```
          
          > **💡 Finding latest versions**: Run `pip index versions azure-functions-durable` or check [pypi.org/project/azure-functions-durable](https://pypi.org/project/azure-functions-durable/) for the latest stable release.
          
          ### host.json
          
          ```json
          {
            "version": "2.0",
            "extensions": {
              "durableTask": {
                "hubName": "default",
                "storageProvider": {
                  "type": "durabletask-scheduler",
                  "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
                }
              }
            },
            "extensionBundle": {
              "id": "Microsoft.Azure.Functions.ExtensionBundle",
              "version": "[4.*, 5.0.0)"
            }
          }
          ```
          
          > **💡 NOTE**: Python uses extension bundles, so the storage provider type is `durabletask-scheduler`. .NET isolated uses the NuGet package directly and requires `azureManaged` instead — see [dotnet.md](dotnet.md).
          
          ### local.settings.json
          
          ```json
          {
            "IsEncrypted": false,
            "Values": {
              "FUNCTIONS_WORKER_RUNTIME": "python",
              "AzureWebJobsStorage": "UseDevelopmentStorage=true",
              "DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
            }
          }
          ```
          
          ## Minimal Example
          
          ```python
          import azure.functions as func
          import azure.durable_functions as df
          
          my_app = df.DFApp(http_auth_level=func.AuthLevel.FUNCTION)
          
          # HTTP Starter
          @my_app.route(route="orchestrators/{function_name}", methods=["POST"])
          @my_app.durable_client_input(client_name="client")
          async def http_start(req: func.HttpRequest, client):
              function_name = req.route_params.get('function_name')
              instance_id = await client.start_new(function_name)
              return client.create_check_status_response(req, instance_id)
          
          # Orchestrator
          @my_app.orchestration_trigger(context_name="context")
          def my_orchestration(context: df.DurableOrchestrationContext):
              result1 = yield context.call_activity("say_hello", "Tokyo")
              result2 = yield context.call_activity("say_hello", "Seattle")
              return f"{result1}, {result2}"
          
          # Activity
          @my_app.activity_trigger(input_name="name")
          def say_hello(name: str) -> str:
              return f"Hello {name}!"
          ```
          
          ## Workflow Patterns
          
          ### Fan-Out/Fan-In
          
          ```python
          @my_app.orchestration_trigger(context_name="context")
          def fan_out_fan_in(context: df.DurableOrchestrationContext):
              cities = ["Tokyo", "Seattle", "London", "Paris", "Berlin"]
              
              # Fan-out: schedule all in parallel
              parallel_tasks = []
              for city in cities:
                  task = context.call_activity("say_hello", city)
                  parallel_tasks.append(task)
              
              # Fan-in: wait for all
              results = yield context.task_all(parallel_tasks)
              return results
          ```
          
          ### Human Interaction
          
          ```python
          import datetime
          
          @my_app.orchestration_trigger(context_name="context")
          def approval_workflow(context: df.DurableOrchestrationContext):
              yield context.call_activity("send_approval_request", context.get_input())
              
              # Wait for approval event with timeout
              timeout = context.current_utc_datetime + datetime.timedelta(days=3)
              approval_task = context.wait_for_external_event("ApprovalEvent")
              timeout_task = context.create_timer(timeout)
              
              winner = yield context.task_any([approval_task, timeout_task])
              
              if winner == approval_task:
                  approved = approval_task.result
                  return "Approved" if approved else "Rejected"
              return "Timed out"
          ```
          
          ## Orchestration Determinism
          
          | ❌ NEVER | ✅ ALWAYS USE |
          |----------|--------------|
          | `datetime.now()` | `context.current_utc_datetime` |
          | `uuid.uuid4()` | `context.new_uuid()` |
          | `random.random()` | Pass random values from activities |
          | `time.sleep()` | `context.create_timer()` |
          | Direct I/O, HTTP, database | `context.call_activity()` |
          
          ### Replay-Safe Logging
          
          ```python
          import logging
          
          @my_app.orchestration_trigger(context_name="context")
          def my_orchestration(context: df.DurableOrchestrationContext):
              # Check if replaying to avoid duplicate logs
              if not context.is_replaying:
                  logging.info("Started")  # Only logs once, not on replay
              result = yield context.call_activity("my_activity", "input")
              return result
          ```
          
          ## Error Handling & Retry
          
          ```python
          retry_options = df.RetryOptions(
              first_retry_interval_in_milliseconds=5000,
              max_number_of_attempts=3,
              backoff_coefficient=2.0,
              max_retry_interval_in_milliseconds=60000
          )
          
          @my_app.orchestration_trigger(context_name="context")
          def workflow_with_retry(context: df.DurableOrchestrationContext):
              try:
                  result = yield context.call_activity_with_retry(
                      "unreliable_service", 
                      retry_options, 
                      context.get_input()
                  )
                  return result
              except Exception as ex:
                  context.set_custom_status({"error": str(ex)})
                  yield context.call_activity("compensation_activity", context.get_input())
                  return "Compensated"
          ```
          
          ## Durable Task SDK (Non-Functions)
          
          For applications running outside Azure Functions (containers, VMs, Azure Container Apps, Azure Kubernetes Service):
          
          ```python
          import asyncio
          from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
          
          # Activity function
          def say_hello(ctx, name: str) -> str:
              return f"Hello {name}!"
          
          # Orchestrator function
          def my_orchestration(ctx, name: str) -> str:
              result = yield ctx.call_activity('say_hello', input=name)
              return result
          
          async def main():
              with DurableTaskSchedulerWorker(
                  host_address="http://localhost:8080",
                  secure_channel=False,
                  taskhub="default"
              ) as worker:
                  worker.add_activity(say_hello)
                  worker.add_orchestrator(my_orchestration)
                  worker.start()
          
                  # Client
                  from durabletask.azuremanaged.client import DurableTaskSchedulerClient
                  client = DurableTaskSchedulerClient(
                      host_address="http://localhost:8080",
                      taskhub="default",
                      token_credential=None,
                      secure_channel=False
                  )
                  instance_id = client.schedule_new_orchestration("my_orchestration", input="World")
                  result = client.wait_for_orchestration_completion(instance_id, timeout=30)
                  print(f"Output: {result.serialized_output}")
          
          if __name__ == "__main__":
              asyncio.run(main())
          ```
          
          
        • README.md 4.9 KB
          # Durable Task Scheduler
          
          Build reliable, fault-tolerant workflows using durable execution with Azure Durable Task Scheduler.
          
          ## When to Use
          
          - Long-running workflows requiring state persistence
          - Distributed transactions with compensating actions (saga pattern)
          - Multi-step orchestrations with checkpointing
          - Fan-out/fan-in parallel processing
          - Workflows requiring human interaction or external events
          - Stateful entities (aggregators, counters, state machines)
          - Multi-agent AI orchestration
          - Data processing pipelines
          
          ## Framework Selection
          
          | Framework | Best For | Hosting |
          |-----------|----------|---------|
          | **Durable Functions** | Serverless event-driven apps | Azure Functions |
          | **Durable Task SDKs** | Any compute (containers, VMs) | Azure Container Apps, Azure Kubernetes Service, App Service, VMs |
          
          > **💡 TIP**: Use Durable Functions for serverless with built-in triggers. Use Durable Task SDKs for hosting flexibility.
          
          ## Quick Start - Local Emulator
          
          ```bash
          # Start the emulator (see https://mcr.microsoft.com/v2/dts/dts-emulator/tags/list for available versions)
          docker pull mcr.microsoft.com/dts/dts-emulator:latest
          docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latest
          
          # Dashboard available at http://localhost:8082
          ```
          
          ## Workflow Patterns
          
          | Pattern | Use When |
          |---------|----------|
          | **Function Chaining** | Sequential steps, each depends on previous |
          | **Fan-Out/Fan-In** | Parallel processing with aggregated results |
          | **Async HTTP APIs** | Long-running operations with HTTP polling |
          | **Monitor** | Periodic polling with configurable timeouts |
          | **Human Interaction** | Workflow pauses for external input/approval |
          | **Saga** | Distributed transactions with compensation |
          | **Durable Entities** | Stateful objects (counters, accounts) |
          
          ## Connection & Authentication
          
          | Environment | Connection String |
          |-------------|-------------------|
          | Local Development (Emulator) | `Endpoint=http://localhost:8080;Authentication=None;TaskHub=default` |
          | Azure (System-Assigned MI) | `Endpoint=https://<scheduler>.durabletask.io;Authentication=ManagedIdentity;TaskHub=default` |
          | Azure (User-Assigned MI) | `Endpoint=https://<scheduler>.durabletask.io;Authentication=ManagedIdentity;ClientID=<uami-client-id>;TaskHub=default` |
          
          > **⚠️ NOTE**: Durable Task Scheduler uses identity-based authentication only — no connection strings with keys. When using a User-Assigned Managed Identity (UAMI), you must include the `ClientID` in the connection string.
          
          ## Troubleshooting
          
          | Error | Cause | Fix |
          |-------|-------|-----|
          | **403 PermissionDenied** on gRPC call (e.g., `client.start_new()`) | Function App managed identity lacks RBAC on the Durable Task Scheduler resource, or IP allowlist blocks traffic | 1. Assign `Durable Task Data Contributor` role (`0ad04412-c4d5-4796-b79c-f76d14c8d402`) to the identity (SAMI or UAMI) scoped to the Durable Task Scheduler resource. For UAMI, also ensure the connection string includes `ClientID=<uami-client-id>`. 2. Ensure the scheduler's `ipAllowlist` includes `0.0.0.0/0` (an empty list denies all traffic). 3. RBAC propagation can take up to 10 minutes — restart the Function App after assigning roles. |
          | **Connection refused** to emulator | Emulator container not running or wrong port | Verify container is running: `docker ps` and confirm port 8080 is mapped |
          | **403 despite correct RBAC** | Scheduler IP allowlist is empty (denies all) | Set `ipAllowlist: ['0.0.0.0/0']` in Bicep or update via CLI: `az durabletask scheduler update --ip-allowlist '0.0.0.0/0'` |
          | **TaskHub not found** | Task hub not provisioned or name mismatch | Ensure the `TaskHub` parameter in the `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` matches the provisioned task hub name |
          | **403 Forbidden** on DTS dashboard | Deploying user lacks RBAC on the scheduler | Assign `Durable Task Data Contributor` role to your own user identity (not just the Function App MI) scoped to the scheduler resource — see [Bicep Patterns](bicep.md) for the dashboard role assignment snippet |
          
          ## References
          
          - [.NET](dotnet.md) — packages, setup, examples, determinism, retry, SDK
          - [Python](python.md) — packages, setup, examples, determinism, retry, SDK
          - [Java](java.md) — dependencies, setup, examples, determinism, retry, SDK
          - [JavaScript](javascript.md) — packages, setup, examples, determinism, retry, SDK
          - [Bicep Patterns](bicep.md) — scheduler, task hub, RBAC, CLI provisioning
          - [Official Documentation](https://learn.microsoft.com/azure/azure-functions/durable/durable-task-scheduler/durable-task-scheduler)
          - [Durable Functions Overview](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview)
          - [Sample Repository](https://github.com/Azure-Samples/Durable-Task-Scheduler)
          - [Choosing an Orchestration Framework](https://learn.microsoft.com/azure/azure-functions/durable/durable-task-scheduler/choose-orchestration-framework)
          
      • event-grid
        • bicep.md 1.7 KB
          # Event Grid - Bicep Patterns
          
          ## Custom Topic
          
          ```bicep
          resource eventGridTopic 'Microsoft.EventGrid/topics@2023-12-15-preview' = {
            name: '${resourcePrefix}-egt-${uniqueHash}'
            location: location
            properties: {
              inputSchema: 'EventGridSchema'
              publicNetworkAccess: 'Enabled'
            }
          }
          ```
          
          ## System Topic (Azure Resource Events)
          
          ```bicep
          resource storageSystemTopic 'Microsoft.EventGrid/systemTopics@2023-12-15-preview' = {
            name: '${resourcePrefix}-storage-topic'
            location: location
            properties: {
              source: storageAccount.id
              topicType: 'Microsoft.Storage.StorageAccounts'
            }
          }
          ```
          
          ## Event Domain
          
          ```bicep
          resource eventDomain 'Microsoft.EventGrid/domains@2023-12-15-preview' = {
            name: '${resourcePrefix}-domain'
            location: location
            properties: {
              inputSchema: 'EventGridSchema'
            }
          }
          ```
          
          ## Publishing Events
          
          ### Node.js
          
          ```javascript
          const { EventGridPublisherClient, AzureKeyCredential } = require("@azure/eventgrid");
          
          const client = new EventGridPublisherClient(
            process.env.EVENTGRID_TOPIC_ENDPOINT,
            "EventGrid",
            new AzureKeyCredential(process.env.EVENTGRID_TOPIC_KEY)
          );
          
          await client.send([{
            eventType: "Order.Created",
            subject: "/orders/12345",
            dataVersion: "1.0",
            data: { orderId: "12345" }
          }]);
          ```
          
          ### Python
          
          ```python
          from azure.eventgrid import EventGridPublisherClient, EventGridEvent
          from azure.core.credentials import AzureKeyCredential
          
          client = EventGridPublisherClient(
              os.environ["EVENTGRID_TOPIC_ENDPOINT"],
              AzureKeyCredential(os.environ["EVENTGRID_TOPIC_KEY"])
          )
          
          client.send([EventGridEvent(
              event_type="Order.Created",
              subject="/orders/12345",
              data={"orderId": "12345"},
              data_version="1.0"
          )])
          ```
          
        • README.md 1.1 KB
          # Azure Event Grid
          
          Serverless event routing for event-driven architectures.
          
          ## When to Use
          
          - Event-driven architectures
          - Reactive programming patterns
          - Decoupled event routing
          - Near real-time event delivery
          - Fan-out to multiple subscribers
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | None required | Event Grid is serverless |
          | Key Vault | Store topic keys |
          
          ## Event Sources
          
          | Type | Description |
          |------|-------------|
          | System Topics | Azure resource events (Storage, Key Vault, etc.) |
          | Custom Topics | Your application events |
          | Event Domains | Multi-tenant event management |
          
          ## Event Schemas
          
          | Schema | Use Case |
          |--------|----------|
          | Event Grid Schema | Azure native format |
          | CloudEvents 1.0 | CNCF standard, cross-platform |
          
          ## Environment Variables
          
          | Variable | Value |
          |----------|-------|
          | `EVENTGRID_TOPIC_ENDPOINT` | Topic endpoint URL |
          | `EVENTGRID_TOPIC_KEY` | Topic access key (Key Vault) |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Subscriptions](subscriptions.md)
          
        • subscriptions.md 1.7 KB
          # Event Grid - Subscriptions
          
          ## Event Subscription
          
          ```bicep
          resource eventGridSubscription 'Microsoft.EventGrid/topics/eventSubscriptions@2023-12-15-preview' = {
            parent: eventGridTopic
            name: 'order-processor-subscription'
            properties: {
              destination: {
                endpointType: 'WebHook'
                properties: {
                  endpointUrl: 'https://my-api.azurecontainerapps.io/webhooks/orders'
                }
              }
              filter: {
                includedEventTypes: [
                  'Order.Created'
                  'Order.Updated'
                ]
              }
              retryPolicy: {
                maxDeliveryAttempts: 30
                eventTimeToLiveInMinutes: 1440
              }
            }
          }
          ```
          
          ## Destination Types
          
          ### Webhook
          
          ```bicep
          destination: {
            endpointType: 'WebHook'
            properties: {
              endpointUrl: 'https://my-api.example.com/events'
            }
          }
          ```
          
          ### Azure Function
          
          ```bicep
          destination: {
            endpointType: 'AzureFunction'
            properties: {
              resourceId: functionApp.id
            }
          }
          ```
          
          ### Service Bus Queue
          
          ```bicep
          destination: {
            endpointType: 'ServiceBusQueue'
            properties: {
              resourceId: '${serviceBus.id}/queues/events'
            }
          }
          ```
          
          ### Event Hub
          
          ```bicep
          destination: {
            endpointType: 'EventHub'
            properties: {
              resourceId: eventHub.id
            }
          }
          ```
          
          ## Filtering
          
          ### Event Type Filter
          
          ```bicep
          filter: {
            includedEventTypes: [
              'Order.Created'
              'Order.Shipped'
            ]
          }
          ```
          
          ### Subject Filter
          
          ```bicep
          filter: {
            subjectBeginsWith: '/orders/priority'
            subjectEndsWith: '.json'
          }
          ```
          
          ### Advanced Filter
          
          ```bicep
          filter: {
            advancedFilters: [
              {
                operatorType: 'NumberGreaterThan'
                key: 'data.amount'
                value: 100
              }
            ]
          }
          ```
          
      • foundry
        • README.md 1.4 KB
          # Azure AI Foundry
          
          Azure AI Foundry (formerly Azure OpenAI) for building AI-powered applications with models like GPT-4o, GPT-4, and embeddings.
          
          > **💡 For detailed AI guidance**, invoke the **`microsoft-foundry`** skill. It provides model catalog access, RAG patterns, agent creation, and evaluation workflows.
          
          ## When to Use
          
          - Chat and conversational AI applications
          - Text generation and completion
          - Code generation assistants
          - Document analysis and summarization
          - Embeddings for search and RAG
          - Multi-modal applications (vision + text)
          
          ## Service Type in azure.yaml
          
          ```yaml
          services:
            my-ai-service:
              host: containerapp  # AI services typically deployed via Container Apps
              project: ./src/ai-service
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | Azure AI Foundry account | Model hosting |
          | Model deployment | Specific model (GPT-4o, GPT-4, etc.) |
          | Key Vault | Store API keys securely |
          | Application Insights | Monitor usage and costs |
          
          ## Model Selection
          
          | Model | Best For | Context Window |
          |-------|----------|----------------|
          | GPT-4o | General purpose, vision, latest | 128K |
          | GPT-4 | Complex reasoning | 32K |
          | GPT-3.5-Turbo | Cost-effective, simple tasks | 16K |
          | text-embedding-ada-002 | Embeddings for RAG/search | 8K |
          
          ## References
          
          - [Region Availability](region-availability.md)
          
        • region-availability.md 849 B
          # Foundry Region Availability
          
          ⚠️ **Very limited — varies by model**
          
          | Region | GPT-4o | GPT-4 | GPT-3.5 | Embeddings |
          |--------|:------:|:-----:|:-------:|:----------:|
          | `eastus` | ✅ | ✅ | ✅ | ✅ |
          | `eastus2` | ✅ | ✅ | ✅ | ✅ |
          | `westus` | ⚠️ | ⚠️ | ✅ | ✅ |
          | `westus3` | ✅ | ⚠️ | ✅ | ✅ |
          | `southcentralus` | ✅ | ✅ | ✅ | ✅ |
          | `swedencentral` | ✅ | ✅ | ✅ | ✅ |
          | `westeurope` | ⚠️ | ✅ | ✅ | ✅ |
          
          > Check https://learn.microsoft.com/azure/ai-services/openai/concepts/models for current model availability.
          
          ## Recommended Regions
          
          | Need | Recommended Region |
          |------|--------------------|
          | Full model availability | `eastus`, `eastus2`, `swedencentral` |
          | Europe compliance | `swedencentral`, `westeurope` |
          | With SWA | `eastus2` (only overlap) |
          
      • functions
        • templates
          • base
            • eval
              • python.md 762 B
                # Base HTTP Template - Python Eval
                
                ## Test Summary
                
                | Test | Status | Notes |
                |------|--------|-------|
                | Code Syntax | ✅ PASS | AST parse successful |
                | Function Routes | ✅ PASS | /api/hello, /api/health defined |
                | v2 Model | ✅ PASS | Uses `func.FunctionApp()` decorator model |
                | Health Endpoint | ✅ PASS | Anonymous auth, JSON response |
                
                ## Code Validation
                
                ```python
                # Validated syntax and structure
                import ast
                with open('function_app.py') as f:
                    ast.parse(f.read())
                # ✅ Code syntax valid
                ```
                
                ## Test Date
                
                2025-02-18
                
                ## Template Source
                
                Generated from `functions_template_get(language: "python", template: "http-trigger-python-azd")` MCP tool output
                
                ## Verdict
                
                **PASS** - Base HTTP template code validates correctly for Python v2 model.
                
              • summary.md 1.9 KB
                # Base HTTP Template - Eval Summary
                
                ## Coverage Status
                
                | Language | Manifest Templates | Eval | Status |
                |----------|-------------------|------|--------|
                | Python | 5 (Bicep + TF) | [✅](python.md) | ✅ Verified |
                | TypeScript | 2 (Bicep) | [✅](typescript.md) | ✅ Verified |
                | JavaScript | 2 (Bicep) | — | 📋 AZD template exists |
                | C# (.NET) | 4 (Bicep + TF) | — | 📋 AZD template exists |
                | Java | 2 (Bicep) | — | 📋 AZD template exists |
                | PowerShell | 1 (Bicep) | — | 📋 AZD template exists |
                
                > ⚠️ **Eval cost note:** Each language × trigger eval requires ~5 min of agent runtime. Full matrix (6 languages × 9 triggers) = ~4.5 hours of CI. Python is verified end-to-end; other languages are confirmed available in the [functions template manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json) (70 templates, 6 languages). Multi-language eval expansion tracked as follow-up.
                
                ## MCP Tool Validation
                
                | Test | Status | Details |
                |------|--------|---------|
                | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                | Template Discovery | ✅ PASS | HTTP templates found for all languages |
                | IaC Included | ✅ PASS | Bicep/Terraform infra/ included in projectFiles |
                | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls per language, templates retrieved and applied |
                
                ## Results
                
                | Test | Python | TypeScript |
                |------|--------|------------|
                | Syntax Valid | ✅ | ✅ |
                | Health Endpoint | ✅ | ✅ |
                | HTTP Trigger | ✅ | ✅ |
                | Code Indicator | ✅ `app.route` | ✅ `app.http` |
                | Template Scaffolded | `http-trigger-python-azd` | `http-trigger-typescript-azd` |
                
                ## Notes
                
                - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                - Base HTTP template provides the foundation for all recipes
                - All recipes compose on top of this base
                
                ## Test Date
                
                2026-04-22
                
              • typescript.md 1.1 KB
                # Base HTTP Template - TypeScript Eval
                
                ## MCP Template Validation
                
                | Criteria | Expected | Status |
                |----------|----------|--------|
                | Template discovery | `functions_template_get(language: "typescript")` returns list | ✅ PASS |
                | Template scaffolded | `http-trigger-typescript-azd` | ✅ PASS |
                | Has trigger code | `app.http` trigger in output | ✅ PASS |
                | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                
                ## Agent Behavior Validation
                
                ```text
                1. Agent calls: functions_template_get(language: "typescript")
                2. Agent scans templateList for HTTP trigger templates
                3. Agent selects: http-trigger-typescript-azd
                4. Agent calls: functions_template_get(language: "typescript", template: "http-trigger-typescript-azd")
                5. Agent writes: functionFiles[] + projectFiles[]
                ```
                
                ## Code Indicators Verified
                
                - `app.http` trigger pattern (V4 model)
                - TypeScript compilation successful (`npm install` + `tsc`)
                - HTTP trigger with request/response handling
                
                ## Test Date
                
                2026-04-22
                
                ## Verdict
                
                **PASS** - MCP template provides complete TypeScript HTTP trigger with IaC and V4 programming model.
                
          • recipes
            • blob-eventgrid
              • eval
                • python.md 1.2 KB
                  # blob-eventgrid Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "blob"` finds matches | ✅ PASS |
                  | Template scaffolded | `blob-eventgrid-trigger-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.blob_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Appropriate role assignment | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "blob" → blob-eventgrid-trigger-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "blob-eventgrid-trigger-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Notes
                  
                  - Template names may vary - use `resource` field or `description` to match
                  - Never hardcode template names - always discover via list call first
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete blob-eventgrid trigger with IaC, RBAC, and UAMI binding.
                  
                • summary.md 2.6 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 1 (Bicep) | ✅ | ✅ Verified |
                  | TypeScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | JavaScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 1 (Bicep) | — | 📋 AZD template exists |
                  | Java | 1 (Bicep) | — | 📋 AZD template exists |
                  | PowerShell | 1 (Bicep) | — | 📋 AZD template exists |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Templates found via resource filter |
                  | IaC Included | ✅ PASS | EventGrid + Storage Bicep in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls, template `blob-eventgrid-trigger-python-azd` retrieved and applied |
                  
                  ## IaC Validation
                  
                  | IaC Type | File | Syntax | Policy Compliant | Status |
                  |----------|------|--------|------------------|--------|
                  | Bicep | blob.bicep | ✅ | ✅ | PASS |
                  | Terraform | blob.tf | ✅ | ✅ | PASS |
                  
                  ## Deployment Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | AZD Template Init | ✅ PASS | `functions-quickstart-python-azd-eventgrid-blob` |
                  | AZD Provision | ✅ PASS | Resources created in `rg-blob-eval` |
                  | AZD Deploy | ✅ PASS | Function deployed to `func-mtgqcoepn4p3w` |
                  | HTTP Response | ✅ PASS | HTTP 200 from function endpoint |
                  | Event Grid Topic | ✅ PASS | `eventgridpdftopic` created |
                  | Storage Account | ✅ PASS | RBAC-only storage provisioned |
                  
                  ## Results
                  
                  | Test | Python |
                  |------|--------|
                  | Health | ✅ |
                  | Blob trigger | ✅ |
                  | EventGrid event | ✅ |
                  | Copy to processed | ✅ |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - Dedicated AZD templates available for all 6 languages
                  - Uses Event Grid for reliable blob event delivery
                  
                  ## IaC Features
                  
                  | Feature | Bicep | Terraform |
                  |---------|-------|-----------|
                  | Storage Account (RBAC-only) | ✅ | ✅ |
                  | Event Grid System Topic | ✅ | ✅ |
                  | Event Grid Subscription | ✅ | ✅ |
                  | RBAC Assignment | ✅ | ✅ |
                  | Private Endpoint (VNet) | ✅ | ✅ |
                  | Azure Policy Compliance | ✅ | ✅ |
                  
                  ## Test Date
                  
                  2026-04-22
                  
              • README.md 1.6 KB
                # Blob Storage with Event Grid Recipe
                
                Blob trigger via Event Grid for high-scale, low-latency blob processing.
                
                ## Template Selection
                
                Resource filter: `blob`  
                Discover templates via MCP or CDN manifest where `resource == "blob"` and `language` matches user request.
                
                ## Why Event Grid?
                
                | Aspect | Polling Trigger | Event Grid Source |
                |--------|-----------------|-------------------|
                | **Latency** | 10s-60s | Sub-second |
                | **Scale** | Limited | High-scale |
                
                ## Troubleshooting
                
                ### "Unauthorized" or "Forbidden" Errors
                
                **Cause:** Missing UAMI credential settings for Storage.  
                **Solution:** Ensure these settings are present in app configuration (prefix must match the connection name used in your function code, default: `AzureWebJobsStorage`):
                
                - `<ConnectionName>__blobServiceUri` (e.g., `https://<account>.blob.core.windows.net`)
                - `<ConnectionName>__credential` (value: `managedidentity`)
                - `<ConnectionName>__clientId`
                
                See [Blob Storage trigger connections](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob-trigger#connections) for identity-based config — refer to the **"Connections"** section on that page for managed identity app settings.
                
                ### Blob Events Not Triggering
                
                **Cause:** Event Grid subscription not created or filtering incorrectly.  
                **Solution:** Verify the Event Grid system topic and subscription exist. Check the blob container prefix filter matches the expected path.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                
            • common
              • dotnet-entry-point.md 2 KB
                # C# (.NET) Entry Point (DO NOT MODIFY)
                
                The base Azure Functions template includes a properly configured `Program.cs` that should NOT be modified or replaced by recipes.
                
                > ⛔ **CRITICAL**: Do NOT replace or modify `Program.cs` from the base template.
                
                ## Base Template Program.cs
                
                The official `functions-quickstart-dotnet-azd` template uses:
                
                ```csharp
                using Microsoft.Azure.Functions.Worker;
                using Microsoft.Extensions.DependencyInjection;
                using Microsoft.Extensions.Hosting;
                
                var host = new HostBuilder()
                    .ConfigureFunctionsWebApplication()  // ASP.NET Core integration
                    .ConfigureServices(services =>
                    {
                        services.AddApplicationInsightsTelemetryWorkerService();
                        services.ConfigureFunctionsApplicationInsights();
                    })
                    .Build();
                
                host.Run();
                ```
                
                ## Why This Matters
                
                | Feature | ConfigureFunctionsWebApplication | ConfigureFunctionsWorkerDefaults |
                |---------|----------------------------------|----------------------------------|
                | ASP.NET Core integration | ✅ Yes | ❌ No |
                | IActionResult return types | ✅ Yes | ❌ No |
                | [FromBody] model binding | ✅ Yes | ❌ No |
                | App Insights integration | ✅ Built-in | ❌ Manual setup |
                | Modern HTTP handling | ✅ Yes | ⚠️ Limited |
                
                ## What Recipes Should Provide
                
                Recipes only need to add:
                1. **Trigger function files** (`.cs` files with `[Function]` attributes)
                2. **Package references** (`.csproj` additions for extensions)
                3. **App settings** (connection strings, configuration)
                
                All triggers use attribute-based binding — no Program.cs modifications needed:
                - `[HttpTrigger]`, `[TimerTrigger]`, `[CosmosDBTrigger]`
                - `[ServiceBusTrigger]`, `[EventHubTrigger]`, `[BlobTrigger]`
                - `[DurableClient]`, `[SqlTrigger]`
                
                ## Common Mistake
                
                ❌ **WRONG** — Recipe overwrites Program.cs with outdated version:
                ```csharp
                var host = new HostBuilder()
                    .ConfigureFunctionsWorkerDefaults()  // OLD PATTERN
                    .Build();
                ```
                
                ✅ **CORRECT** — Recipe leaves Program.cs untouched, only adds function files.
                
              • error-handling.md 2.2 KB
                # Error Handling Patterns
                
                > **MANDATORY**: All function implementations MUST include proper error handling with logging.
                
                ## Python
                
                ```python
                import logging
                
                try:
                    # Your function logic here
                    result = process_data(data)
                    logging.info(f"Success: processed {item_id}")
                except Exception as error:
                    logging.error(f"Error processing {item_id}: {error}")
                    raise  # Re-raise to trigger retry/dead-letter
                ```
                
                ## TypeScript
                
                ```typescript
                try {
                    // Your function logic here
                    const result = await processData(data);
                    context.log(`Success: processed ${itemId}`);
                } catch (error) {
                    context.error(`Error processing ${itemId}:`, error);
                    throw error;  // Re-raise to trigger retry/dead-letter
                }
                ```
                
                ## JavaScript
                
                ```javascript
                try {
                    // Your function logic here
                    const result = await processData(data);
                    context.log(`Success: processed ${itemId}`);
                } catch (error) {
                    context.error(`Error processing ${itemId}:`, error);
                    throw error;  // Re-raise to trigger retry/dead-letter
                }
                ```
                
                ## C# (.NET)
                
                ```csharp
                try
                {
                    // Your function logic here
                    var result = await ProcessDataAsync(data);
                    _logger.LogInformation($"Success: processed {itemId}");
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"Error processing {itemId}");
                    throw;  // Re-raise to trigger retry/dead-letter
                }
                ```
                
                ## Java
                
                ```java
                try {
                    // Your function logic here
                    Result result = processData(data);
                    context.getLogger().info("Success: processed " + itemId);
                } catch (Exception e) {
                    context.getLogger().severe("Error processing " + itemId + ": " + e.getMessage());
                    throw e;  // Re-raise to trigger retry/dead-letter
                }
                ```
                
                ## PowerShell
                
                ```powershell
                try {
                    # Your function logic here
                    $result = Process-Data -Data $data
                    Write-Host "Success: processed $itemId"
                }
                catch {
                    Write-Error "Error processing $itemId : $_"
                    throw  # Re-raise to trigger retry/dead-letter
                }
                ```
                
                ## Key Principles
                
                1. **Always log before throwing** - Enables debugging from logs
                2. **Re-throw exceptions** - Allows Functions runtime to handle retry/dead-letter
                3. **Include context in logs** - Item ID, operation name, relevant metadata
                4. **Use appropriate log levels** - Info for success, Error for failures
                
              • health-check.md 2.7 KB
                # Health Check Endpoint
                
                > **RECOMMENDED**: Add a health endpoint for monitoring and load balancer probes.
                
                ## Python
                
                ```python
                @app.route(route="health", methods=["GET"], auth_level=func.AuthLevel.ANONYMOUS)
                def health(req: func.HttpRequest) -> func.HttpResponse:
                    return func.HttpResponse(
                        '{"status":"healthy"}',
                        mimetype="application/json",
                        status_code=200
                    )
                ```
                
                ## TypeScript
                
                ```typescript
                import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
                
                app.http('health', {
                    methods: ['GET'],
                    authLevel: 'anonymous',
                    handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
                        return {
                            status: 200,
                            jsonBody: { status: 'healthy' }
                        };
                    }
                });
                ```
                
                ## JavaScript
                
                ```javascript
                const { app } = require('@azure/functions');
                
                app.http('health', {
                    methods: ['GET'],
                    authLevel: 'anonymous',
                    handler: async () => ({
                        status: 200,
                        jsonBody: { status: 'healthy' }
                    })
                });
                ```
                
                ## C# (.NET)
                
                ```csharp
                public class Health
                {
                    [Function("health")]
                    public HttpResponseData Run(
                        [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req)
                    {
                        var response = req.CreateResponse();
                        response.Headers.Add("Content-Type", "application/json");
                        response.WriteString("{\"status\":\"healthy\"}");
                        return response;
                    }
                }
                ```
                
                ## Java
                
                ```java
                @FunctionName("health")
                public HttpResponseMessage health(
                        @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS)
                        HttpRequestMessage<Optional<String>> request,
                        final ExecutionContext context) {
                    
                    return request.createResponseBuilder(HttpStatus.OK)
                            .header("Content-Type", "application/json")
                            .body("{\"status\":\"healthy\"}")
                            .build();
                }
                ```
                
                ## PowerShell
                
                **health/function.json:**
                ```json
                {
                  "bindings": [
                    {
                      "authLevel": "anonymous",
                      "type": "httpTrigger",
                      "direction": "in",
                      "name": "Request",
                      "methods": ["get"]
                    },
                    {
                      "type": "http",
                      "direction": "out",
                      "name": "Response"
                    }
                  ]
                }
                ```
                
                **health/run.ps1:**
                ```powershell
                param($Request, $TriggerMetadata)
                
                Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
                    StatusCode = [HttpStatusCode]::OK
                    Body = '{"status":"healthy"}'
                    ContentType = 'application/json'
                })
                ```
                
                ## Usage Notes
                
                - **Auth level**: Use `anonymous` for load balancer probes
                - **Response**: Return JSON with at least `{"status":"healthy"}`
                - **Extended checks**: Can add database connectivity, dependency checks
                - **Route**: Standard route is `/api/health`
                
              • nodejs-entry-point.md 4.1 KB
                # Node.js Entry Point (REQUIRED)
                
                Azure Functions Node.js v4 programming model requires an entry point file that initializes the runtime.
                
                > ⛔ **CRITICAL**: Without this file, functions will deploy but return 404 on all endpoints.
                
                ## Project Structure (CRITICAL)
                
                The project structure MUST be:
                
                ```
                project-root/                # ← azure.yaml project: "."
                ├── azure.yaml
                ├── package.json             # ← MUST be at ROOT, not in src/
                ├── host.json
                ├── src/
                │   ├── index.js             # ← Entry point (app.setup)
                │   └── functions/
                │       ├── myFunction.js    # ← Functions auto-discovered
                │       └── ...
                └── infra/
                ```
                
                > ⛔ **CRITICAL**: `package.json` MUST be at project root, NOT inside `src/`.
                > The `azure.yaml` must have `project: .` (not `project: ./src/`).
                
                ## JavaScript: src/index.js
                
                **This file MUST exist and MUST NOT be removed when replacing trigger files.**
                
                ```javascript
                const { app } = require('@azure/functions');
                
                app.setup({
                    enableHttpStream: true,
                });
                ```
                
                ## TypeScript: src/index.ts
                
                **This file MUST exist and MUST NOT be removed when replacing trigger files.**
                
                ```typescript
                import { app } from '@azure/functions';
                
                app.setup({
                    enableHttpStream: true,
                });
                ```
                
                ## package.json Configuration
                
                The `package.json` MUST be at the project root (same level as `azure.yaml`).
                
                > ⛔ **CRITICAL: The glob pattern is REQUIRED for function discovery!**
                > Using `"main": "src/index.js"` alone will result in 404 on all endpoints.
                > You MUST use the glob pattern that includes function files.
                
                ### JavaScript (REQUIRED pattern)
                ```json
                {
                  "main": "src/{index.js,functions/*.js}",
                  "scripts": {
                    "start": "func start"
                  }
                }
                ```
                
                ### TypeScript (REQUIRED pattern)
                ```json
                {
                  "main": "dist/src/{index.js,functions/*.js}",
                  "scripts": {
                    "build": "tsc",
                    "prestart": "npm run build",
                    "start": "func start"
                  }
                }
                ```
                
                > **Why the Glob Pattern is Required**: The Azure Functions Node.js v4 runtime uses the `main` field to determine which files to load. Unlike CommonJS where `require()` chains work, the runtime needs ALL function files explicitly listed in the glob pattern. Without the glob, only `index.js` loads and functions in `src/functions/` are never registered.
                >
                > ❌ **WRONG**: `"main": "src/index.js"` — Functions not discovered, 404 on all routes
                > ✅ **CORRECT**: `"main": "src/{index.js,functions/*.js}"` — All functions discovered
                
                ## azure.yaml Configuration
                
                ```yaml
                services:
                  api:
                    project: .           # ← ROOT directory, not ./src/
                    language: js         # or ts for TypeScript
                    host: function
                ```
                
                > ⛔ **CRITICAL**: Use `project: .` — NOT `project: ./src/`. The runtime expects `package.json` at the project root.
                
                ## Build Requirements (TypeScript only)
                
                Before deployment, TypeScript must be compiled:
                
                ```bash
                npm run build
                ```
                
                This outputs JavaScript to `dist/` which is what Azure Functions actually runs.
                
                ## Common Mistakes
                
                | Mistake | Symptom | Fix |
                |---------|---------|-----|
                | **Using `"main": "src/index.js"` without glob** | **404 on all endpoints** | **Use `"main": "src/{index.js,functions/*.js}"`** |
                | Missing `src/index.js` | 404 on all endpoints | Add the entry point file with `app.setup()` |
                | Deleting `src/index.js` when replacing triggers | 404 after recipe applied | Keep index.js, only replace function files |
                | `package.json` in `src/` instead of root | 404, functions not found | Move `package.json` to project root |
                | `project: ./src/` in azure.yaml | Deployment fails or 404 | Use `project: .` |
                | Missing `npm run build` for TypeScript | 404 or old code runs | Run build before deploy |
                
                > ⛔ **#1 CAUSE OF 404 ERRORS**: Using `"main": "src/index.js"` instead of the glob pattern.
                > The glob `src/{index.js,functions/*.js}` is **REQUIRED** — it's not optional!
                
                ## Terraform vs Bicep: Source Code is IDENTICAL
                
                The Node.js source code and `package.json` are **exactly the same** for both IaC types.
                
                Only the `infra/` folder differs:
                - Bicep: `infra/*.bicep`
                - Terraform: `infra/*.tf`
                
                > ⚠️ If you find yourself changing imports or source code because of IaC choice, something is wrong. The application code should be IaC-agnostic.
                
            • cosmosdb
              • eval
                • python.md 1.2 KB
                  # Cosmos DB Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "cosmos"` finds matches | ✅ PASS |
                  | Template scaffolded | `cosmos-trigger-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.cosmos_db_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Cosmos DB Data Contributor role | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "cosmos" → cosmos-trigger-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "cosmos-trigger-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Notes
                  
                  - Template names may vary - use `resource` field or `description` to match
                  - Never hardcode template names - always discover via list call first
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete Cosmos DB trigger with IaC, RBAC, and UAMI binding.
                  
                • summary.md 1.8 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 1 (Bicep) | [✅](python.md) | ✅ Verified |
                  | TypeScript | 1 (Bicep) | [✅](typescript.md) | ✅ Verified |
                  | C# (.NET) | 1 (Bicep) | — | 📋 AZD template exists |
                  | Java | 3 (Bicep) | — | 📋 AZD template exists |
                  | JavaScript | — | — | ⚠️ No AZD template |
                  | PowerShell | — | — | ⚠️ No AZD template |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). JavaScript and PowerShell have no Cosmos DB AZD template. Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Cosmos templates found via resource filter |
                  | IaC Included | ✅ PASS | Cosmos Bicep module + RBAC in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls per language, templates retrieved and applied |
                  
                  ## Results
                  
                  | Test | Python | TypeScript |
                  |------|--------|------------|
                  | Health | ✅ | ✅ |
                  | Trigger fires | ✅ | ✅ |
                  | Change detected | ✅ | ✅ |
                  | Code Indicator | ✅ `cosmos_db_trigger` | ✅ `app.cosmosDB` |
                  | Extra Indicator (IaC) | ✅ `Microsoft.DocumentDB` | ✅ `Microsoft.DocumentDB` |
                  | Template Scaffolded | `cosmos-trigger-python-azd` | `cosmos-trigger-typescript-azd` |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - Cosmos DB requires dual RBAC: Azure control plane + SQL data plane
                  - See README for RBAC troubleshooting
                  
                  ## Test Date
                  
                  2026-04-22
                  
                • typescript.md 1.3 KB
                  # Cosmos DB Recipe - TypeScript Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "typescript")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "cosmos"` finds matches | ✅ PASS |
                  | Template scaffolded | `cosmos-trigger-typescript-azd` | ✅ PASS |
                  | Has trigger code | `app.cosmosDB` trigger binding in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Cosmos DB Data Contributor role | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "typescript")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "cosmos" → cosmos-trigger-typescript-azd
                  4. Agent calls: functions_template_get(language: "typescript", template: "cosmos-trigger-typescript-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Code Indicators Verified
                  
                  - `app.cosmosDB` trigger binding (V4 model)
                  - TypeScript compilation successful (`npm install` + `tsc`)
                  - Cosmos DB with managed identity and proper RBAC assignments
                  - Infrastructure uses `Microsoft.DocumentDB`
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete TypeScript Cosmos DB trigger with IaC, RBAC, and UAMI binding.
                  
              • README.md 1.5 KB
                # Cosmos DB Recipe
                
                Cosmos DB change feed trigger with managed identity authentication.
                
                ## Template Selection
                
                Resource filter: `cosmos`  
                Discover templates via MCP or CDN manifest where `resource == "cosmos"` and `language` matches user request.
                
                ## Troubleshooting
                
                ### "Forbidden" on Data Operations
                
                **Cause:** Cosmos DB uses **two separate RBAC systems** — Azure RBAC (control plane) and Cosmos SQL RBAC via `sqlRoleAssignments` (data plane). The MCP template configures both, but if the SQL role assignment is missing, data reads/writes will fail even if Azure RBAC is correctly assigned.
                
                **Solution:** Verify the `sqlRoleAssignments` resource exists in the Bicep/Terraform output. Check the function app has the `Cosmos DB Built-in Data Contributor` SQL role.
                
                ### UAMI Connection Issues
                
                **Cause:** Missing managed identity credential settings.  
                **Solution:** Ensure all three settings are present in app configuration:
                
                - `COSMOS_CONNECTION__accountEndpoint`
                - `COSMOS_CONNECTION__credential` (value: `managedidentity`)
                - `COSMOS_CONNECTION__clientId`
                
                See [Cosmos DB trigger connections](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-trigger#connections) for identity-based config — refer to the **"Connections"** section on that page for managed identity app settings.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                | [eval/typescript.md](eval/typescript.md) | TypeScript evaluation results |
                
            • durable
              • eval
                • python.md 1.2 KB
                  # durable Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "durable"` finds matches | ✅ PASS |
                  | Template scaffolded | `durable-functions-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.orchestration_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Appropriate role assignment | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "durable" → durable-functions-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "durable-functions-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Notes
                  
                  - Template names may vary - use `resource` field or `description` to match
                  - Never hardcode template names - always discover via list call first
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete durable trigger with IaC, RBAC, and UAMI binding.
                  
                • summary.md 1.6 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 3 (Bicep) | ✅ | ✅ Verified |
                  | TypeScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | JavaScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 8 (Bicep) | — | 📋 AZD template exists |
                  | Java | 1 (Bicep) | — | 📋 AZD template exists |
                  | PowerShell | — | — | ⚠️ No AZD template |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). PowerShell has no Durable Functions AZD template. Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Templates found via resource filter |
                  | IaC Included | ✅ PASS | Durable Task Scheduler Bicep in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls, template retrieved and applied |
                  
                  ## Results
                  
                  | Test | Python |
                  |------|--------|
                  | Health | ✅ |
                  | Orchestration starts | ✅ |
                  | Activities complete | ✅ |
                  | Status query works | ✅ |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - Uses Durable Task Scheduler (NOT Storage queues/tables)
                  - See [Durable Task Scheduler docs](../../../../../durable-task-scheduler/README.md)
                  
                  ## Test Date
                  
                  2026-04-22
                  
              • README.md 1.8 KB
                # Durable Functions Recipe
                
                Orchestration workflows with Durable Task Scheduler as the backend.
                
                ## Template Selection
                
                Resource filter: `durable`  
                Discover templates via MCP or CDN manifest where `resource == "durable"` and `language` matches user request.
                
                ## Key Concept
                
                Uses **Durable Task Scheduler** (DTS) — a fully managed backend for state persistence. Do NOT use Azure Storage queues/tables.
                
                See [Durable Task Scheduler reference](../../../../durable-task-scheduler/README.md) for backend configuration details.
                
                ## Troubleshooting
                
                ### Orchestration Fails to Start
                
                **Cause:** Durable Task Scheduler (DTS) backend not provisioned or connection misconfigured.  
                **Solution:** Verify the DTS resource exists and the function app has the `Durable Task Scheduler Worker` role. Do NOT use Azure Storage queues/tables as backend.
                
                ### UAMI Connection Issues
                
                **Cause:** Missing or incorrect Durable Task Scheduler connection string.  
                **Solution:** DTS uses a connection string format (not the `__` suffix pattern). Set these app settings:
                
                - `DURABLE_TASK_SCHEDULER_CONNECTION_STRING`: `Endpoint=<scheduler-endpoint>;Authentication=ManagedIdentity;ClientID=<client-id>`
                - `TASKHUB_NAME`: name of the task hub
                
                For system-assigned identity, omit `ClientID`:
                `Endpoint=<scheduler-endpoint>;Authentication=ManagedIdentity`
                
                The identity must have the **Durable Task Data Contributor** role on the scheduler or task hub resource. See [DTS identity auth](https://learn.microsoft.com/en-us/azure/durable-task/scheduler/durable-task-scheduler-identity) for identity-based config — refer to the **"Configure managed identity"** section for connection string and role assignment details.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                
            • eventhubs
              • eval
                • python.md 1.2 KB
                  # eventhubs Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "eventhub"` finds matches | ✅ PASS |
                  | Template scaffolded | `eventhub-trigger-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.event_hub_message_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Appropriate role assignment | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "eventhub" → eventhub-trigger-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "eventhub-trigger-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Notes
                  
                  - Template names may vary - use `resource` field or `description` to match
                  - Never hardcode template names - always discover via list call first
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete eventhubs trigger with IaC, RBAC, and UAMI binding.
                  
                • summary.md 1.5 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 1 (Bicep) | ✅ | ✅ Verified |
                  | TypeScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 1 (Bicep) | — | 📋 AZD template exists |
                  | Java | 1 (Bicep) | — | 📋 AZD template exists |
                  | JavaScript | — | — | ⚠️ No AZD template |
                  | PowerShell | — | — | ⚠️ No AZD template |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). JavaScript and PowerShell have no Event Hubs AZD template. Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Templates found via resource filter |
                  | IaC Included | ✅ PASS | Event Hubs Bicep + RBAC in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls, template `eventhub-trigger-python-azd` retrieved and applied |
                  
                  ## Results
                  
                  | Test | Python |
                  |------|--------|
                  | Health | ✅ |
                  | Event received | ✅ |
                  | Batch processing | ✅ |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - UAMI configuration included in template IaC
                  
                  ## Test Date
                  
                  2026-04-22
                  
              • README.md 1.2 KB
                # Event Hubs Recipe
                
                Event Hubs streaming trigger with managed identity authentication.
                
                ## Template Selection
                
                Resource filter: `eventhub`  
                Discover templates via MCP or CDN manifest where `resource == "eventhub"` and `language` matches user request.
                
                ## Troubleshooting
                
                ### "Unauthorized" or "Forbidden" Errors
                
                **Cause:** Missing UAMI credential settings for Event Hubs.  
                **Solution:** Ensure all three settings are present in app configuration:
                
                - `EventHubConnection__fullyQualifiedNamespace`
                - `EventHubConnection__credential` (value: `managedidentity`)
                - `EventHubConnection__clientId`
                
                See [Event Hubs trigger connections](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-event-hubs-trigger#connections) for identity-based config — refer to the **"Connections"** section on that page for managed identity app settings.
                
                ### Events Not Arriving
                
                **Cause:** Consumer group or checkpoint storage misconfigured.  
                **Solution:** Verify the Event Hubs consumer group exists and the function has `Azure Event Hubs Data Receiver` role on the namespace.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                
            • mcp
              • eval
                • python.md 1.2 KB
                  # MCP Server Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "mcp"` finds matches | ✅ PASS |
                  | Template scaffolded | `mcp-server-remote-python` | ✅ PASS |
                  | Has trigger code | HTTP trigger with JSON-RPC handler in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "mcp" → mcp-server-remote-python
                  4. Agent calls: functions_template_get(language: "python", template: "mcp-server-remote-python")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Code Indicators Verified
                  
                  - HTTP trigger endpoint for JSON-RPC 2.0 protocol
                  - `tools/list` returns tool definitions with schemas
                  - `tools/call` executes tools and returns results
                  - Ready for AI agent integration (Copilot, Claude, etc.)
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete remote MCP server with JSON-RPC endpoint and IaC.
                  
                • summary.md 1.5 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 3 (Bicep) | ✅ | ✅ Verified |
                  | TypeScript | 2 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 2 (Bicep) | — | 📋 AZD template exists |
                  | Java | 2 (Bicep) | — | 📋 AZD template exists |
                  | JavaScript | — | — | ⚠️ No AZD template |
                  | PowerShell | — | — | ⚠️ No AZD template |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). JavaScript and PowerShell have no MCP AZD template. Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Templates found via resource filter |
                  | IaC Included | ✅ PASS | Storage queue config in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls, template `mcp-server-remote-python` retrieved and applied |
                  
                  ## Results
                  
                  | Test | Python |
                  |------|--------|
                  | Health | ✅ |
                  | tools/list | ✅ |
                  | tools/call | ✅ |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - Requires `enableQueue: true` for MCP state management
                  - Uses JSON-RPC 2.0 protocol over HTTP
                  
                  ## Test Date
                  
                  2026-04-22
                  
              • README.md 1.9 KB
                # MCP (Model Context Protocol) Recipe
                
                MCP tool endpoints for AI agent integration via the JSON-RPC 2.0 protocol.
                
                ## Template Selection
                
                Resource filter: `mcp`  
                Discover templates via MCP or CDN manifest where `resource == "mcp"` and `language` matches user request.
                
                ## Protocol
                
                MCP uses the **JSON-RPC 2.0** protocol. Two template types with different transport support:
                
                - **Extension-based** (`mcp-server-remote-*`) supports both Streamable HTTP and SSE transports
                - **Self-hosted** (`mcp-sdk-hosting-*`) — supports Streamable HTTP only
                
                See [MCP Specification](https://modelcontextprotocol.io/) for protocol details.
                
                ## Troubleshooting
                
                ### Transport Mismatch
                
                **Cause:** Client and server using different transports — SSE client gets `404`/`405`, HTTP client gets unexpected `text/event-stream`.  
                **Solution:** Extension-based templates support both Streamable HTTP and SSE. Self-hosted templates support Streamable HTTP only. In VS Code `mcp.json`, set `"type": "sse"` or `"type": "http"` to match the server.
                
                ### Missing App Settings After Deploy
                
                **Cause:** Required app settings not configured on the function app.  
                **Solution:** Ensure protected resource metadata settings are present. For C# self-hosted servers, verify `host.json` `arguments` points to the compiled DLL path.
                
                See [MCP extension trigger and bindings](https://learn.microsoft.com/azure/azure-functions/functions-bindings-mcp) for extension-based servers, [Self-hosted MCP servers](https://learn.microsoft.com/en-us/azure/azure-functions/self-hosted-mcp-servers) for self-hosted architecture, and [MCP tutorial troubleshooting](https://learn.microsoft.com/en-us/azure/azure-functions/functions-mcp-tutorial?tabs=self-hosted#troubleshooting) for self-hosted deployment issues.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                
            • servicebus
              • eval
                • python.md 1.3 KB
                  # Service Bus Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "servicebus"` finds matches | ✅ PASS |
                  | Template scaffolded | `servicebus-trigger-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.service_bus_queue_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Service Bus Data Receiver/Sender role | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "servicebus" → servicebus-trigger-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "servicebus-trigger-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Code Indicators Verified
                  
                  - `@app.service_bus_queue_trigger` with queue_name
                  - `connection="ServiceBusConnection"` (UAMI pattern)
                  - `ServiceBusConnection__fullyQualifiedNamespace` binding
                  - Extension bundle v4
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete Service Bus trigger with IaC, RBAC, and UAMI binding.
                  
                • summary.md 1.8 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 1 (Bicep) | [✅](python.md) | ✅ Verified |
                  | TypeScript | 1 (Bicep) | [✅](typescript.md) | ✅ Verified |
                  | JavaScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 1 (Bicep) | — | 📋 AZD template exists |
                  | Java | 1 (Bicep) | — | 📋 AZD template exists |
                  | PowerShell | 1 (Bicep) | — | 📋 AZD template exists |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Templates found via resource filter |
                  | IaC Included | ✅ PASS | Service Bus Bicep + RBAC in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls per language, templates retrieved and applied |
                  
                  ## Results
                  
                  | Test | Python | TypeScript |
                  |------|--------|------------|
                  | Health | ✅ | ✅ |
                  | Queue message | ✅ | ✅ |
                  | Output binding | ✅ | ✅ |
                  | Code Indicator | ✅ `service_bus_queue_trigger` | ✅ `app.serviceBusQueue` |
                  | Extra Indicator (IaC) | ✅ `Microsoft.ServiceBus` | ✅ `Microsoft.ServiceBus` |
                  | Template Scaffolded | `servicebus-trigger-python-azd` | `servicebus-trigger-typescript-azd` |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - See README for UAMI troubleshooting (500 error, Unauthorized)
                  
                  ## Test Date
                  
                  2026-04-22
                  
                • typescript.md 1.4 KB
                  # Service Bus Recipe - TypeScript Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "typescript")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "servicebus"` finds matches | ✅ PASS |
                  | Template scaffolded | `servicebus-trigger-typescript-azd` | ✅ PASS |
                  | Has trigger code | `app.serviceBusQueue` trigger binding in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Service Bus Data Receiver/Sender role | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "typescript")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "servicebus" → servicebus-trigger-typescript-azd
                  4. Agent calls: functions_template_get(language: "typescript", template: "servicebus-trigger-typescript-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Code Indicators Verified
                  
                  - `app.serviceBusQueue` trigger binding (V4 model)
                  - TypeScript compilation successful (`npm install` + `tsc`)
                  - Service Bus namespace with managed identity RBAC
                  - VNet integration and Flex Consumption plan
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete TypeScript Service Bus trigger with IaC, RBAC, and UAMI binding.
                  
              • README.md 1.3 KB
                # Service Bus Recipe
                
                Service Bus queue/topic trigger with managed identity authentication.
                
                ## Template Selection
                
                Resource filter: `servicebus`  
                Discover templates via MCP tool or CDN manifest where `resource == "servicebus"` and `language` matches user request.
                
                ## Troubleshooting
                
                ### 500 Error on First Request
                
                **Cause:** RBAC role assignment hasn't propagated to Service Bus data plane.  
                **Solution:** Wait 30-60 seconds after provisioning, or restart the function app.
                
                ### "Unauthorized" or "Forbidden" Errors
                
                **Cause:** Missing UAMI credential settings.  
                **Solution:** Ensure all three settings are present in app configuration:
                
                - `ServiceBusConnection__fullyQualifiedNamespace`
                - `ServiceBusConnection__credential` (value: `managedidentity`)
                - `ServiceBusConnection__clientId`
                
                See [Service Bus trigger connections](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus-trigger#connections) for identity-based config — refer to the **"Connections"** section on that page for managed identity app settings.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                | [eval/typescript.md](eval/typescript.md) | TypeScript evaluation results |
                
            • sql
              • eval
                • python.md 1.2 KB
                  # sql Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "sql"` finds matches | ✅ PASS |
                  | Template scaffolded | `sql-trigger-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.sql_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  | Has RBAC | Appropriate role assignment | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "sql" → sql-trigger-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "sql-trigger-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Notes
                  
                  - Template names may vary - use `resource` field or `description` to match
                  - Never hardcode template names - always discover via list call first
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete sql trigger with IaC, RBAC, and UAMI binding.
                  
                • summary.md 2.8 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 1 (Bicep) | ✅ | ✅ Verified |
                  | TypeScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 1 (Bicep) | — | 📋 AZD template exists |
                  | Java | — | — | ⚠️ No AZD template |
                  | JavaScript | — | — | ⚠️ No AZD template |
                  | PowerShell | — | — | ⚠️ No AZD template |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). Java, JavaScript, and PowerShell have no SQL AZD template. Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Templates found via resource filter |
                  | IaC Included | ✅ PASS | SQL Server Bicep + RBAC in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls, template `sql-trigger-python-azd` retrieved and applied |
                  
                  ## IaC Validation
                  
                  | IaC Type | File | Syntax | Policy Compliant | Status |
                  |----------|------|--------|------------------|--------|
                  | Bicep | sql.bicep | ✅ | ✅ | PASS |
                  | Terraform | sql.tf | ✅ | ✅ | PASS |
                  
                  ## Deployment Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | AZD Template Init | ✅ PASS | `functions-quickstart-python-azd-sql` |
                  | AZD Provision | ✅ PASS | Resources created in `rg-sql-eval` |
                  | AZD Deploy | ✅ PASS | Function deployed to `func-api-arkwcvhvbkqwc` |
                  | HTTP Response | ✅ PASS | HTTP 200 from function endpoint |
                  | SQL Server | ✅ PASS | `sql-arkwcvhvbkqwc` with Entra-only auth |
                  | SQL Database | ✅ PASS | `ToDo` database created |
                  
                  ## Results
                  
                  | Test | Python |
                  |------|--------|
                  | Health | ✅ |
                  | SQL trigger | ✅ |
                  | SQL output | ✅ |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - Dedicated AZD templates available for Python, TypeScript, .NET
                  - Requires T-SQL post-deploy for managed identity access
                  
                  ## IaC Features
                  
                  | Feature | Bicep | Terraform |
                  |---------|-------|-----------|
                  | SQL Server (Entra-only) | ✅ | ✅ |
                  | SQL Database | ✅ | ✅ |
                  | Firewall Rules | ✅ | ✅ |
                  | Private Endpoint (VNet) | ✅ | ✅ |
                  | Azure Policy Compliance | ✅ | ✅ |
                  
                  ## Post-Deploy Note
                  
                  SQL managed identity access requires T-SQL after deployment:
                  ```sql
                  CREATE USER [<function-app-name>] FROM EXTERNAL PROVIDER;
                  ALTER ROLE db_datareader ADD MEMBER [<function-app-name>];
                  ALTER ROLE db_datawriter ADD MEMBER [<function-app-name>];
                  ```
                  
                  ## Test Date
                  
                  2026-04-22
                  
              • README.md 1.8 KB
                # Azure SQL Recipe
                
                SQL change tracking trigger with Entra ID managed identity authentication.
                
                ## Template Selection
                
                Resource filter: `sql`  
                Discover templates via MCP or CDN manifest where `resource == "sql"` and `language` matches user request.
                
                ## Troubleshooting
                
                ### SQL Trigger Not Firing
                
                **Cause:** Change tracking not enabled on the target table.  
                **Solution:** Run these T-SQL commands on your database:
                
                ```sql
                ALTER DATABASE [YourDatabase] SET CHANGE_TRACKING = ON;
                ALTER TABLE [dbo].[ToDo] ENABLE CHANGE_TRACKING;
                ```
                
                ### "Login failed" or "Unauthorized" Errors
                
                **Cause:** Missing managed identity authentication or SQL access not granted.  
                **Solution:** Set the SQL connection string with managed identity authentication:
                
                ```
                Server=tcp:<server>.database.windows.net,1433;Database=<database>;Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;
                ```
                
                For user-assigned managed identity, add `User Id=<ClientId>`:
                
                ```
                Server=tcp:<server>.database.windows.net,1433;Database=<database>;Authentication=Active Directory Default;User Id=<ClientId>;Encrypt=True;TrustServerCertificate=False;
                ```
                
                Also run post-deploy T-SQL to grant the function app data access:
                
                ```sql
                CREATE USER [<function-app-name>] FROM EXTERNAL PROVIDER;
                ALTER ROLE db_datareader ADD MEMBER [<function-app-name>];
                ALTER ROLE db_datawriter ADD MEMBER [<function-app-name>];
                ```
                
                See [SQL managed identity](https://learn.microsoft.com/en-us/azure/azure-functions/functions-identity-access-azure-sql-with-managed-identity) for identity-based config — refer to the **"Configure the function app"** section for connection string and identity settings.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                
            • timer
              • eval
                • python.md 1.2 KB
                  # Timer Recipe - Python Eval
                  
                  ## MCP Template Validation
                  
                  | Criteria | Expected | Status |
                  |----------|----------|--------|
                  | Template discovery | `functions_template_get(language: "python")` returns list | ✅ PASS |
                  | Filter by resource | `resource == "timer"` finds matches | ✅ PASS |
                  | Template scaffolded | `timer-trigger-python-azd` | ✅ PASS |
                  | Has trigger code | `@app.timer_trigger` decorator in output | ✅ PASS |
                  | Has IaC | `projectFiles[]` includes Bicep | ✅ PASS |
                  
                  ## Agent Behavior Validation
                  
                  ```text
                  1. Agent calls: functions_template_get(language: "python")
                  2. Agent scans templateList.triggers[] descriptions and resource field
                  3. Agent selects: template where resource == "timer" → timer-trigger-python-azd
                  4. Agent calls: functions_template_get(language: "python", template: "timer-trigger-python-azd")
                  5. Agent writes: functionFiles[] + projectFiles[]
                  ```
                  
                  ## Code Indicators Verified
                  
                  - `@app.timer_trigger` with schedule parameter
                  - `TIMER_SCHEDULE` app setting reference (`%TIMER_SCHEDULE%`)
                  - 6-part cron expression (with seconds)
                  
                  ## Test Date
                  
                  2026-04-22
                  
                  ## Verdict
                  
                  **PASS** - MCP template provides complete timer trigger with configurable schedule and IaC.
                  
                • summary.md 1.5 KB
                  # Eval Summary
                  
                  ## Coverage Status
                  
                  | Language | Manifest Templates | Eval | Status |
                  |----------|-------------------|------|--------|
                  | Python | 1 (Bicep) | ✅ | ✅ Verified |
                  | TypeScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | JavaScript | 1 (Bicep) | — | 📋 AZD template exists |
                  | C# (.NET) | 1 (Bicep) | — | 📋 AZD template exists |
                  | Java | 1 (Bicep) | — | 📋 AZD template exists |
                  | PowerShell | 1 (Bicep) | — | 📋 AZD template exists |
                  
                  > ⚠️ **Eval cost note:** Each language eval requires ~5 min of agent runtime. Python is verified end-to-end; other languages confirmed in [manifest](https://cdn.functions.azure.com/public/templates-manifest/manifest.json). Multi-language eval expansion tracked as follow-up.
                  
                  ## MCP Tool Validation
                  
                  | Test | Status | Details |
                  |------|--------|---------|
                  | `functions_template_get` | ✅ PASS | 2 calls via `azure-functions` MCP tool |
                  | Template Discovery | ✅ PASS | Timer templates found via resource filter |
                  | IaC Included | ✅ PASS | Bicep infra/ included in projectFiles |
                  | E2E Agent Test | ✅ PASS | 2 `azure-functions` calls, template `timer-trigger-python-azd` retrieved and applied |
                  
                  ## Results
                  
                  | Test | Python |
                  |------|--------|
                  | Health | ✅ |
                  | Timer fires | ✅ |
                  | Schedule correct | ✅ |
                  
                  ## Notes
                  
                  - Templates retrieved via `functions_template_get(language: "<language>", template: "<template-name>")` MCP tool
                  - Timer templates include cron schedule in function code
                  
                  ## Test Date
                  
                  2026-04-22
                  
              • README.md 1.5 KB
                # Timer Recipe
                
                Scheduled/cron trigger for periodic task execution.
                
                ## Template Selection
                
                Resource filter: `timer`  
                Discover templates via MCP or CDN manifest where `resource == "timer"` and `language` matches user request.
                
                ## Cron Expressions
                
                Some templates define the schedule via an app setting reference `%TIMER_SCHEDULE%` so the cron expression is configurable without code changes. Set the `TIMER_SCHEDULE` app setting to the desired expression.
                
                | Schedule | Expression |
                |----------|------------|
                | Every 5 minutes | `0 */5 * * * *` |
                | Every hour | `0 0 * * * *` |
                | Every day at midnight | `0 0 0 * * *` |
                | Every Monday at 9am | `0 0 9 * * 1` |
                | Every 30 seconds | `*/30 * * * * *` |
                
                > Azure uses 6-part cron expressions (with seconds). See [Azure Functions timer trigger](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer#ncrontab-expressions) for full syntax reference.
                
                ## Troubleshooting
                
                ### Timer Not Firing
                
                **Cause:** Invalid cron expression or function app not running.  
                **Solution:** Verify cron syntax; check function app is started and healthy.
                
                ### Duplicate Executions
                
                **Cause:** Multiple instances running the same timer.  
                **Solution:** Timer triggers use Storage lease to ensure single execution. Verify `AzureWebJobsStorage` is configured.
                
                ## Eval
                
                | Path | Description |
                |------|-------------|
                | [eval/summary.md](eval/summary.md) | Evaluation summary |
                | [eval/python.md](eval/python.md) | Python evaluation results |
                
            • composition.md 11.1 KB
              # Composition Algorithm
              
              Combine multiple templates into a single deployable project.
              
              ## Azure MCP Path
              
              ```
              INPUT: language, user_requirements
              OUTPUT: Complete project ready for `azd up`
              
              1. DISCOVER
                 functions_template_get(language) → template list with descriptions
              
              2. CHECK SINGLE-TEMPLATE MATCH
                 If one template's description covers ALL requirements → use it alone
              
              3. SELECT TEMPLATES
                 - Trigger template (REQUIRED) — base project with IaC
                 - Binding templates (OPTIONAL) — extract patterns only
              
              4. FETCH TEMPLATES
                 - Single match: 1 call
                 - Multiple: parallel calls
              
              5. COMPOSE
                 - Use trigger template as BASE (functionFiles + projectFiles)
                 - EXTRACT binding patterns from binding templates
                 - MERGE IaC resources and RBAC roles
                 - ADD user's custom business logic
              
              6. TRIM unused demo code (keep AzureWebJobsStorage)
              
              7. WRITE files — for each entry { path, content } in functionFiles[] AND projectFiles[]:
                    a. Create parent directories from path
                    b. Write content to path
                    NEVER hand-write Bicep/Terraform and use azd init -t/func init/func new as fallback when composing multiple recipes and required templates are not found
              
              8. DEPLOY: azd up --no-prompt
              ```
              
              ## Fallback Path (Azure MCP Unavailable)
              
              ```
              INPUT: language, user_requirements
              OUTPUT: Complete project ready for `azd up`
              
              1. FETCH MANIFEST
                 GET https://cdn.functions.azure.com/public/templates-manifest/manifest.json
                 If fetch fails → fall back to: https://github.com/Azure/azure-functions-templates/blob/dev/Functions.Templates/Template-Manifest/manifest.json
                 If both fail → fall back to known-good Azure-Samples/functions-quickstart-* repos by language+resource
                 If all fail → report error and ask user to retry later
              
              2. FILTER TEMPLATES
                 Filter by: language, resource (from selection.md), iac
              
              3. CHECK SINGLE-TEMPLATE MATCH
                 If one template covers ALL requirements → use it alone
              
              4. SELECT TEMPLATES
                 - Trigger template (REQUIRED) — base project
                 - Binding templates (OPTIONAL) — extract patterns only
              
              5. DOWNLOAD TEMPLATES
                 For each template:
                 - If folderPath == "." → ZIP download + unzip
                 - If folderPath != "." → fetch tree + raw github url file downloads
                 - Fallback: git clone --depth 1
              
              6. COMPOSE
                 - Use trigger template as BASE
                 - EXTRACT binding patterns from binding templates
                 - MERGE IaC resources, RBAC roles and settings, README and other files.
                 - ADD user's custom business logic
              
              7. TRIM unused demo code (keep AzureWebJobsStorage)
              
              8. WRITE all files
              
              9. DEPLOY: azd up --no-prompt
              ```
              
              ## Example (MCP)
              
              **User:** "HTTP function that writes to Cosmos DB"
              
              ```
              1. Discover: functions_template_get(language: "python") → returns template list
              2. Check: No single template description mentions BOTH HTTP trigger AND Cosmos output
              3. Select from discovered list:
                 - Template with resource: "http" (trigger, base)
                 - Template with resource: "cosmos" and description mentioning "output" (binding)
              4. Fetch both templates by templateName from discovery results
              5. Compose:
                 - Base: HTTP template (has IaC, azure.yaml)
                 - Extract: Cosmos output binding + RBAC from cosmos template
                 - Merge: Add Cosmos module to infra/main.bicep
              6. Trim: Remove HTTP demo response code
              7. Write files
              8. Deploy
              ```
              
              ## Example (Fallback)
              
              **User:** "HTTP function that writes to Cosmos DB"
              
              ```
              1. Fetch: GET manifest.json from CDN
              2. Filter: language=Python, resource=http OR resource=cosmos, iac=bicep
              3. Check: No single template covers both
              4. Select:
                 - http-trigger-python-azd (trigger, base) → repositoryUrl + folderPath
                 - cosmos-trigger-python-azd (binding) → repositoryUrl + folderPath
              5. Download: ZIP download both repos (folderPath = ".")
              6. Compose:
                 - Base: HTTP template (has infra/, azure.yaml)
                 - Extract: Cosmos IaC module + RBAC from cosmos template
                 - Merge: Add cosmos.bicep to infra/app/, wire into main.bicep
              7. Trim: Remove demo code
              8. Write files
              9. Deploy
              ```
              
              ## Critical Rules
              
              1. **NEVER hardcode template names** — always discover/fetch manifest first
              2. **PRESERVE generated IaC patterns** — keep RBAC roles, managed identity config, and security settings intact when merging
              3. **ALWAYS keep AzureWebJobsStorage** — runtime requires it
              4. **ALWAYS use `--no-prompt`** — the agent must never elicit user input during azd commands
              5. **ALWAYS include ALL THREE UAMI settings for every binding** — see UAMI Configuration below
              6. **ALWAYS wait for RBAC propagation** — use two-phase deploy if 403 errors occur
              7. **NEVER enable `allowSharedKeyAccess: true`** — correct solution is waiting for RBAC, not disabling security
              
              ## IaC Merge Guidelines
              
              When composing multiple templates:
              
              | Action | Allowed | Not Allowed |
              |--------|---------|-------------|
              | Add resource modules from binding templates | ✅ | |
              | Add RBAC role assignments from binding templates | ✅ | |
              | Merge environment variables | ✅ | |
              | Remove RBAC roles | | ❌ |
              | Change managed identity to connection strings | | ❌ |
              | Remove security configurations | | ❌ |
              | Modify resource SKUs without user request | | ❌ |
              
              **Merge = additive combination, not modification of security patterns.**
              
              ## UAMI Configuration (CRITICAL)
              
              Templates use User Assigned Managed Identity (UAMI). ALL service bindings require explicit `credential` and `clientId` app settings. Missing these causes 500/401/403 errors at runtime.
              
              **Required pattern for EVERY service binding:**
              
              ```bicep
              appSettings: {
                // Endpoint (varies by service)
                EventHubConnection__fullyQualifiedNamespace: eventhubs.outputs.fullyQualifiedNamespace
                // UAMI credentials - REQUIRED, prefix vary by example
                EventHubConnection__credential: 'managedidentity'
                EventHubConnection__clientId: apiUserAssignedIdentity.outputs.clientId
              }
              ```
              
              **Validation Checklist (MANDATORY before deploy):**
              
              | Setting Pattern | Required | Example |
              |-----------------|:--------:|---------|
              | `{Connection}__fullyQualifiedNamespace` or `{Connection}__accountEndpoint` | ✅ | `EventHubConnection__fullyQualifiedNamespace` |
              | `{Connection}__credential` | ✅ | `'managedidentity'` (exact case) |
              | `{Connection}__clientId` | ✅ | `apiUserAssignedIdentity.outputs.clientId` |
              
              > ⛔ **STOP if any check fails.** The function WILL fail at runtime with 500/Unauthorized errors.
              
              ## Language-Specific Entry Points
              
              ### Node.js (JavaScript/TypeScript)
              
              > ⛔ **Do NOT delete `src/index.js` (JS) or `src/index.ts` (TS).**
              > This file contains `app.setup()` which initializes the Functions runtime.
              > Without it, functions deploy but return 404 on all endpoints.
              
              > ⛔ **Glob pattern REQUIRED in package.json:**
              >
              > ```json
              > { "main": "src/{index.js,functions/*.js}" }
              > ```
              >
              > Using `"main": "src/index.js"` alone results in 404 on ALL endpoints.
              
              > ⛔ **package.json must be at project ROOT** (same level as `azure.yaml`), NOT inside `src/`.
              
              > 📦 **TypeScript:** Run `npm run build` before deployment.
              > Use: `"main": "dist/src/{index.js,functions/*.js}"`
              
              ### C# (.NET)
              
              > ⛔ **Do NOT replace `Program.cs` from the base template.**
              > The base template uses `ConfigureFunctionsWebApplication()` with App Insights integration.
              > Recipes only ADD trigger function files (`.cs`) and package references (`.csproj`).
              
              ## Deployment Strategy
              
              **Option A: Single command** (fast, may fail on first deploy due to RBAC propagation)
              
              ```bash
              azd up --no-prompt
              ```
              
              **Option B: Two-phase** (recommended for reliability)
              
              ```bash
              azd provision --no-prompt     # Create resources + RBAC assignments
              sleep 60                       # Wait for RBAC propagation (Azure AD needs 30-60s)
              azd deploy --no-prompt        # Deploy code (RBAC now active)
              ```
              
              ## Terraform-Specific Requirements
              
              > ⚠️ **CRITICAL**: All Terraform must use `sku_name = "FC1"` (Flex Consumption). **NEVER use Y1/Dynamic.**
              
              ### Runtime Versions
              
              > ⚠️ **ALWAYS QUERY OFFICIAL DOCUMENTATION** — Do NOT use hardcoded versions.
              >
              > **Primary Source:** [Azure Functions Supported Languages](https://learn.microsoft.com/en-us/azure/azure-functions/supported-languages)
              >
              > Query for latest GA/LTS versions before generating IaC.
              
              | Language | `function_runtime` | Version Source |
              |----------|-------------------|----------------|
              | C# (.NET) | `dotnet-isolated` | Latest LTS from docs |
              | TypeScript/JS | `node` | Latest LTS from docs |
              | Python | `python` | Latest GA from docs |
              | Java | `java` | Latest LTS from docs |
              | PowerShell | `powershell` | Latest GA from docs |
              
              ### Flex Consumption (FC1) Requires azapi
              
              > ⚠️ Use `azapi_resource` instead of `azurerm_linux_function_app` for FC1.
              > The AzureRM provider doesn't support FC1's `functionAppConfig` block.
              
              ```hcl
              resource "azapi_resource" "function_app" {
                type      = "Microsoft.Web/sites@2023-12-01"
                name      = "func-${local.name}"
                location  = azurerm_resource_group.rg.location
                parent_id = azurerm_resource_group.rg.id
              
                body = {
                  kind = "functionapp,linux"
                  properties = {
                    serverFarmId = azapi_resource.plan.id
                    functionAppConfig = {
                      runtime = { name = "node", version = "22" }
                      scaleAndConcurrency = { maximumInstanceCount = 100, instanceMemoryMB = 2048 }
                      deployment = {
                        storage = {
                          type  = "blobContainer"
                          value = "${azurerm_storage_account.storage.primary_blob_endpoint}deploymentpackage"
                          authentication = {
                            type                           = "UserAssignedIdentity"
                            userAssignedIdentityResourceId = azurerm_user_assigned_identity.api.id
                          }
                        }
                      }
                    }
                  }
                }
                depends_on = [time_sleep.rbac_propagation]
              }
              ```
              
              ### RBAC Propagation Delay
              
              Azure RBAC takes 30-60s to propagate. Terraform's `depends_on` only waits for resource creation, not RBAC propagation.
              
              **Solution 1: Add `time_sleep` resource**
              
              ```hcl
              resource "time_sleep" "rbac_propagation" {
                depends_on      = [azurerm_role_assignment.storage_blob_owner]
                create_duration = "60s"
              }
              
              resource "azapi_resource" "function_app" {
                depends_on = [time_sleep.rbac_propagation]
                # ...
              }
              ```
              
              **Solution 2: Create deployment container explicitly**
              
              ```hcl
              resource "azurerm_storage_container" "deployment" {
                name                  = "deploymentpackage"
                storage_account_id    = azurerm_storage_account.storage.id
                container_access_type = "private"
                depends_on            = [azurerm_role_assignment.storage_blob_owner]
              }
              ```
              
              > ⚠️ **Common Failures Without These Fixes:**
              >
              > - `403 Forbidden` — RBAC not yet propagated
              > - `404 Container Not Found` — deployment container not created
              > - `Tag Not Found: azd-service-name` — Azure resource tags take time to be queryable
              
              ### Required: azd-service-name Tag
              
              ```hcl
              tags = {
                "azd-service-name" = "api"   # MUST match service name in azure.yaml
              }
              ```
              
              > ⚠️ **Without this tag, `azd deploy` fails with:**
              > `resource not found: unable to find a resource tagged with 'azd-service-name: api'`
              
              ### Disabled Local Auth (Policy Required)
              
              Azure Policy often enforces RBAC-only authentication. Always disable local auth (connection strings, SAS keys) and use managed identity instead.
              
              ```hcl
              # Storage
              shared_access_key_enabled = false
              
              # Service Bus
              local_auth_enabled = false
              
              # Event Hubs
              local_authentication_enabled = false
              
              # Cosmos DB
              local_authentication_disabled = true
              ```
              
            • README.md 1.1 KB
              # Function Template Recipes
              
              Composable templates for Azure Functions integrations.
              
              ## Recipe Index
              
              For intent-to-resource mapping and selection algorithm, see [selection.md](../selection.md).
              
              | Recipe | Resource |
              |--------|----------|
              | [cosmosdb](cosmosdb/README.md) | `cosmos` |
              | [eventhubs](eventhubs/README.md) | `eventhub` |
              | [servicebus](servicebus/README.md) | `servicebus` |
              | [timer](timer/README.md) | `timer` |
              | [durable](durable/README.md) | `durable` |
              | [mcp](mcp/README.md) | `mcp` |
              | [sql](sql/README.md) | `sql` |
              | [blob-eventgrid](blob-eventgrid/README.md) | `blob` |
              
              ## Composition
              
              See [composition.md](composition.md) for merging multiple templates.
              
              ## Common Patterns
              
              | Pattern | Description |
              |---------|-------------|
              | [Error Handling](common/error-handling.md) | Try/catch + logging patterns |
              | [Health Check](common/health-check.md) | Health endpoint for monitoring |
              | [Node.js Entry Point](common/nodejs-entry-point.md) | `src/index.js` requirements |
              | [.NET Entry Point](common/dotnet-entry-point.md) | `Program.cs` requirements |
              
          • README.md 6.5 KB
            # Azure Functions Templates
            
            Dynamic template selection for Azure Functions projects.
            
            ## Prerequisites: Check MCP Availability
            
            Before proceeding, verify Azure MCP Server and Functions tools are available:
            
            ```
            functions_template_get(language: "python")
            ```
            
            | Result | Action |
            |--------|--------|
            | ✅ Returns template list | Use **Primary Path: MCP Tools** below |
            | ❌ Tool not found / Error | Jump to **[Fallback (MCP Unavailable)](#fallback-mcp-unavailable)** |
            
            ---
            
            ## Primary Path: MCP Tools
            
            **Use Azure MCP tools** — they provide complete, up-to-date AZD templates with IaC, RBAC, and managed identity.
            
            ### Step 1: Discover Templates
            
            ```
            functions_template_get(language: "<python|csharp|typescript|javascript|java|powershell>")
            ```
            
            Returns template list with metadata:
            
            - `templateName` — pass to generate call
            - `description` — use for selection (describes trigger, bindings, security features)
            - `resource` — filter by trigger type (http, cosmos, timer, eventhub, blob, sql, mcp, ai)
            - `infrastructure` — prefer `bicep` (default), use `terraform` if user requests
            
            ### Step 2: Select Template
            
            Use the intent→resource mapping in [selection.md](selection.md) to map user intent to a `resource` filter value.
            
            > **Default behavior:** When user intent cannot be determined or no trigger type is known, use `http` as the default.
            
            **Single-template optimization:** If description mentions BOTH trigger AND binding user needs, fetch that one template only.
            
            **IaC selection:**
            
            - Default: `infrastructure: "bicep"`
            - If user says "terraform": `infrastructure: "terraform"`
            
            ### Step 3: Generate Project
            
            ```
            functions_template_get(
              language: "<language>",
              template: "<selected-template-name>"
            )
            ```
            
            ### Step 4: Write All Files
            
            > Write files directly from the MCP tool output — CLI scaffolding tools produce incomplete projects missing IaC, RBAC, and managed identity configuration. NEVER hand-write Bicep/Terraform and use `azd init -t <template>`/`func init`/`func new` as fallback when composing multiple recipes and required templates are not found in MCP.
            
            Each array entry contains `{ path, content }`. For every entry in BOTH arrays:
            
            1. **Create directories** — extract the parent directory from each `path` (e.g., `infra/` from `infra/main.bicep`, `.azure/` from `.azure/config.json`). Create all unique parent directories first.
            2. **Write the file** — use the `path` as the file path and `content` as the file body. Write every file exactly as specified.
            
            | Array | Contains | Action |
            |-------|----------|--------|
            | `functionFiles[]` | Function source code, infra, and config files | Write all — these are the trigger/binding code and IaC |
            | `projectFiles[]` | host.json, dependencies, settings files | Write all — these are runtime configuration |
            
            **PRESERVE generated IaC security patterns** — keep RBAC, managed identity, and security config intact. When composing multiple templates, merge additively (see [composition.md](recipes/composition.md)).
            
            > **Skip content verification** — MCP template files are pre-validated. After writing, do not `view`/`cat` files unless you customized them. Check success via exit code and file count only.
            
            ### Step 5: Deploy
            
            ```bash
            azd env set AZURE_LOCATION <region>
            azd up --no-prompt
            ```
            
            ---
            
            ## Recipe Composition (Multiple Templates)
            
            When user needs trigger + bindings not in a single template:
            
            1. **Discover** all templates for language
            2. **Select trigger template** as base (has complete project structure)
            3. **Select binding templates** for additional integrations
            4. **Fetch all** templates (parallel calls)
            5. **Compose**:
               - Use trigger template as base
               - Extract binding patterns from binding templates
               - Merge IaC resources and RBAC roles
               - Add user's custom logic
            6. **Trim** unused demo code from samples
            
            > **AzureWebJobsStorage exception**: Always keep storage account + RBAC — runtime requires it.
            
            ---
            
            ## Fallback (MCP Unavailable)
            
            If MCP tools are unavailable, download the CDN manifest which points to the same GitHub repos:
            
            ### Step 1: Fetch Manifest
            
            ```
            GET https://cdn.functions.azure.com/public/templates-manifest/manifest.json
            ```
            
            > ⚠️ **If manifest fetch fails** (any error):
            > 1. Fall back to the source manifest at `https://github.com/Azure/azure-functions-templates/blob/dev/Functions.Templates/Template-Manifest/manifest.json`
            > 2. If both sources are unreachable, fall back to known-good repos: `Azure-Samples/functions-quickstart-*` keyed by language + resource (e.g., `functions-quickstart-python-http-azd`)
            > 3. If all fallbacks fail, report the error to the user and ask them to retry later
            
            ### Step 2: Filter Templates
            
            Each template entry contains:
            
            | Field | Description | Example |
            |-------|-------------|---------|
            | `language` | Programming language | `Python`, `TypeScript`, `JavaScript`, `Java`, `CSharp`, `PowerShell` |
            | `resource` | Trigger type (see [selection.md](selection.md)) | `http`, `cosmos`, `timer`, `eventhub`, `servicebus`, `blob`, `sql`, `mcp`, `durable` |
            | `iac` | Infrastructure type | `bicep`, `terraform` |
            | `repositoryUrl` | GitHub repo with complete project | `https://github.com/Azure-Samples/functions-quickstart-python-http-azd` |
            | `folderPath` | Path within repo | `.` or `src/api` |
            
            Filter: `language == <user-lang> AND resource == <mapped-resource> AND iac == <user-iac>`
            
            ### Step 3: Download Template
            
            **If `folderPath` is `.` (root):** ZIP download + unzip
            
            ```
            GET https://github.com/{owner}/{repo}/archive/refs/heads/main.zip
            unzip main.zip -d <project-dir>
            ```
            
            **If `folderPath` is a subfolder:** Fetch tree + raw file downloads
            
            ```
            1. GET https://api.github.com/repos/{owner}/{repo}/git/trees/main?recursive=1
            2. Filter tree entries where path starts with {folderPath}/
            3. For each file:
               GET https://raw.githubusercontent.com/{owner}/{repo}/main/{path}
            ```
            
            **If downloads fail:** Fall back to git clone
            
            ```bash
            git clone <repositoryUrl> --depth 1
            # If folderPath != ".", copy only that folder
            ```
            
            The downloaded content is the **same** as MCP `functionFiles[]` + `projectFiles[]`:
            
            - Source code (function triggers, bindings)
            - IaC (Bicep/Terraform with RBAC, managed identity)
            - azure.yaml (azd configuration)
            - host.json, dependencies
            
            ### Step 4: Deploy
            
            ```bash
            azd up --no-prompt
            ```
            
            ---
            
            ## References
            
            - [Composition Details](recipes/composition.md) — recipe algorithm
            - [Selection Guide](selection.md) — intent→resource mapping
            - [Recipes Index](recipes/README.md) — all available recipes
            - [Base Template Eval](base/eval/summary.md) — HTTP base evaluation results
            
            **Browse all:** [Awesome AZD Functions](https://azure.github.io/awesome-azd/?tags=functions)
            
          • selection.md 4.3 KB
            # Template Selection Guide
            
            Map user intent to MCP template `resource` filter.
            
            > **NEVER hardcode template names** — names can change. Always use `functions_template_get(language)` to discover available templates, then filter by `resource` field.
            
            ## Intent → Resource Mapping
            
            | User Says | Code Indicators (existing projects) | Resource Filter | Recipe Reference |
            |-----------|-------------------------------------|-----------------|------------------|
            | "HTTP", "REST", "API", "webhook" | `HttpTrigger`, `@app.route`, `req: HttpRequest` | `http` | — |
            | "timer", "schedule", "cron", "periodic" | `TimerTrigger`, `@app.schedule`, `TimerInfo` | `timer` | [recipes/timer/README.md](recipes/timer/README.md) |
            | "Cosmos", "CosmosDB", "document DB" | `CosmosDBTrigger`, `@app.cosmos_db`, `cosmos_db_input` | `cosmos` | [recipes/cosmosdb/README.md](recipes/cosmosdb/README.md) |
            | "Event Hub", "streaming", "events" | `EventHubTrigger`, `@app.event_hub`, `event_hub_output` | `eventhub` | [recipes/eventhubs/README.md](recipes/eventhubs/README.md) |
            | "Service Bus", "queue", "message" | `ServiceBusTrigger`, `@app.service_bus_queue` | `servicebus` | [recipes/servicebus/README.md](recipes/servicebus/README.md) |
            | "blob", "file", "storage trigger" | `BlobTrigger`, `@app.blob`, `blob_input`, `blob_output` | `blob` | [recipes/blob-eventgrid/README.md](recipes/blob-eventgrid/README.md) |
            | "SQL", "database trigger" | `SqlTrigger`, `@app.sql`, `sql_input`, `SqlOutput` | `sql` | [recipes/sql/README.md](recipes/sql/README.md) |
            | "MCP", "MCP server", "tools" | `McpToolTrigger`, `@app.mcp_tool`, `mcp` in project | `mcp` | [recipes/mcp/README.md](recipes/mcp/README.md) |
            | "durable", "workflow", "orchestration" | `DurableOrchestrationTrigger`, `orchestrator`, `durable_functions` | `durable` | [recipes/durable/README.md](recipes/durable/README.md) |
            | "AI", "agent", "chatbot", "OpenAI" | `openai`, `AzureOpenAI`, `langchain`, `semantic_kernel` | `http` | Scan description for "AI", "agent", "Copilot SDK" |
            | **No specific trigger / intent unclear** | — | `http` (default) | — |
            
            ## Selection Algorithm
            
            ```
            1. DISCOVER: functions_template_get(language) → template list
            2. DETECT (existing code): Scan for code indicators above → map to resource
            3. MATCH (new projects): Scan template descriptions for user intent
            4. FILTER: resource == mapped_resource AND infrastructure == user_iac_choice
            5. PREFER: AZD-enabled (infrastructure: "bicep" or "terraform")
            6. SELECT: Template whose description best matches user intent
            7. DEFAULT: If intent unclear or no trigger specified → use `http`
            ```
            
            ## Output: Working Function App
            
            MCP templates return **complete, deployable projects** — each array entry has `{ path, content }`:
            
            | Array | Contents | Action |
            |-------|----------|--------|
            | `functionFiles[]` | Function source code (triggers, bindings, business logic), infra and other files | Create directories from `path`, write `content` to each file |
            | `projectFiles[]` | settings.json, host.json, dependencies files | Create directories from `path`, write `content` to each file |
            
            > Write files from the array output above. NEVER hand-write Bicep/Terraform and use `azd init -t <template>`/`func init`/`func new` as fallback when composing multiple recipes and required templates are not found.
            
            For deployment steps, see [README.md](README.md#step-5-deploy).
            
            ## Default Behavior
            
            **When user intent is ambiguous or no specific trigger is mentioned**, default to HTTP:
            
            - User says "create a function" with no trigger → HTTP
            - User describes business logic without specifying trigger → HTTP
            - Intent cannot be determined from context → HTTP
            
            HTTP is the safest default because it's the most common trigger type and provides a simple request/response pattern that works for most use cases.
            
            ## IaC Selection
            
            | User Says | Filter By |
            |-----------|-----------|
            | Nothing specified | `infrastructure: "bicep"` (default) |
            | "terraform", "use terraform" | `infrastructure: "terraform"` |
            | "bicep", "use bicep" | `infrastructure: "bicep"` |
            
            ## Non-AZD Fallback
            
            If specific trigger/binding has no AZD template:
            
            1. Use non-AZD template for **function code** only
            2. Find related AZD template as **IaC reference**:
            
            | Non-AZD Resource | Related AZD Reference |
            |-----------------|----------------------|
            | RabbitMQ, Kafka | EventHub AZD |
            | SendGrid, Twilio, Webhook | HTTP AZD |
            | IoT Hub | EventHub AZD |
            
        • aspire-containerapps.md 3.3 KB
          # Azure Functions on Azure Container Apps (Aspire)
          
          When .NET Aspire deploys Azure Functions via `azd`, Functions run as containerized workloads on Azure Container Apps. **File-based secret storage is required** when using identity-based storage access.
          
          > ⚠️ **Critical:** When Azure Functions use identity-based storage (e.g., `AzureWebJobsStorage__blobServiceUri`), you **must** set `AzureWebJobsSecretStorageType=Files`.
          
          ## Proactive Configuration in AppHost
          
          **Best Practice:** Add this setting in your AppHost BEFORE running `azd up`:
          
          ```csharp
          var functions = builder.AddAzureFunctionsProject<Projects.Functions>("functions")
              .WithHostStorage(storage)
              .WithEnvironment("AzureWebJobsSecretStorageType", "Files")  // Required for Container Apps
              // ... other configuration
          ```
          
          This ensures the environment variable is automatically included in the generated infrastructure.
          
          ## Container Apps Bicep Configuration
          
          When Aspire generates infrastructure, the Functions container app should include this environment variable. If you need to customize the generated Bicep or create it manually, the configuration looks like this:
          
          > **Note:** This example shows partial configuration. Assumes `containerAppEnv`, `storageAccount`, and `appInsights` resources are defined elsewhere in your Bicep templates.
          
          ```bicep
          resource functionsContainerApp 'Microsoft.App/containerApps@2024-03-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            identity: {
              type: 'SystemAssigned'
            }
            properties: {
              environmentId: containerAppEnv.id
              configuration: {
                ingress: {
                  external: true
                  targetPort: 8080
                }
              }
              template: {
                containers: [
                  {
                    name: 'functions-app'
                    image: containerImage
                    env: [
                      {
                        name: 'AzureWebJobsStorage__blobServiceUri'
                        value: storageAccount.properties.primaryEndpoints.blob
                      }
                      {
                        name: 'AzureWebJobsSecretStorageType'
                        value: 'Files'  // Required for Container Apps with identity-based storage
                      }
                      {
                        name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
                        value: appInsights.properties.ConnectionString
                      }
                      {
                        name: 'FUNCTIONS_EXTENSION_VERSION'
                        value: '~4'
                      }
                      {
                        name: 'FUNCTIONS_WORKER_RUNTIME'
                        value: 'dotnet-isolated'
                      }
                    ]
                  }
                ]
              }
            }
          }
          ```
          
          ## Why This Is Required
          
          - Identity-based storage URIs (e.g., `AzureWebJobsStorage__blobServiceUri`) work for runtime operations
          - However, Functions' internal secret/key management does not support these identity-based URIs
          - File-based secret storage is mandatory for Container Apps deployments with identity-based storage
          
          ## Common Error Without This Setting
          
          ```
          System.InvalidOperationException: Secret initialization from Blob storage failed due to missing both
          an Azure Storage connection string and a SAS connection uri.
          ```
          
          ## When to Use This Configuration
          
          - Deploying Azure Functions to Container Apps via .NET Aspire
          - Using `AddAzureFunctionsProject` with `WithHostStorage` in your AppHost
          - Using identity-based storage access (no connection strings)
          - Setting environment variables like `AzureWebJobsStorage__blobServiceUri`
          
        • bicep.md 11.9 KB
          # Functions Bicep Patterns — REFERENCE ONLY
          
          > ⛔ **DO NOT COPY THIS CODE DIRECTLY**
          >
          > This file contains **reference patterns** for understanding Azure Functions Bicep structure.
          > **You MUST use the composition algorithm** to generate infrastructure:
          >
          > 1. Load `templates/selection.md` to choose the correct base template
          > 2. Follow `templates/recipes/composition.md` for the exact algorithm
          > 3. Use `functions_template_get` MCP tool to list and fetch templates and write `functionFiles[]` + `projectFiles[]` directly — NEVER hand-write Bicep/Terraform and use `azd init -t <template>`/`func init`/`func new` as fallback when composing multiple recipes and required templates are not found
          >
          > Hand-writing Bicep from these patterns will result in missing RBAC, incorrect managed identity configuration, and security vulnerabilities.
          
          ## Flex Consumption (Recommended)
          
          **Use Flex Consumption for new deployments with managed identity (no connection strings).**
          
          ```bicep
          resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
            name: '${resourcePrefix}func${uniqueHash}'
            location: location
            sku: { name: 'Standard_LRS' }
            kind: 'StorageV2'
            properties: {
              minimumTlsVersion: 'TLS1_2'
              allowBlobPublicAccess: false
              allowSharedKeyAccess: false  // Enforce managed identity
            }
          }
          
          resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
            parent: storageAccount
            name: 'default'
          }
          
          resource deploymentContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
            parent: blobService
            name: 'deploymentpackage'
          }
          
          resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
            name: 'appi-${uniqueHash}'
            location: location
            kind: 'web'
            properties: {
              Application_Type: 'web'
            }
          }
          
          resource functionAppPlan 'Microsoft.Web/serverfarms@2024-04-01' = {
            name: 'plan-${uniqueHash}'
            location: location
            sku: {
              name: 'FC1'
              tier: 'FlexConsumption'
            }
            properties: {
              reserved: true
            }
          }
          
          resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            kind: 'functionapp,linux'
            identity: {
              type: 'SystemAssigned'
            }
            properties: {
              serverFarmId: functionAppPlan.id
              httpsOnly: true
              functionAppConfig: {
                deployment: {
                  storage: {
                    type: 'blobContainer'
                    value: '${storageAccount.properties.primaryEndpoints.blob}deploymentpackage'
                    authentication: {
                      type: 'SystemAssignedIdentity'
                    }
                  }
                }
                scaleAndConcurrency: {
                  maximumInstanceCount: 100
                  instanceMemoryMB: 2048
                }
                runtime: {
                  name: 'python'  // or 'node', 'dotnet-isolated'
                  version: '<version>'  // Query latest GA: https://learn.microsoft.com/en-us/azure/azure-functions/supported-languages
                }
              }
              siteConfig: {
                appSettings: [
                  {
                    name: 'AzureWebJobsStorage__blobServiceUri'
                    value: storageAccount.properties.primaryEndpoints.blob
                  }
                  {
                    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
                    value: appInsights.properties.ConnectionString
                  }
                  {
                    name: 'FUNCTIONS_EXTENSION_VERSION'
                    value: '~4'
                  }
                  {
                    name: 'FUNCTIONS_WORKER_RUNTIME'
                    value: 'python'
                  }
                ]
              }
            }
          }
          
          // Grant Function App access to Storage for runtime
          resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(storageAccount.id, functionApp.id, 'Storage Blob Data Owner')
            scope: storageAccount
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')
              principalId: functionApp.identity.principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          > 💡 **Key Points:**
          >
          > - Use `AzureWebJobsStorage__blobServiceUri` instead of connection string
          > - Set `allowSharedKeyAccess: false` for enhanced security
          > - Use `SystemAssignedIdentity` for deployment authentication
          > - Grant `Storage Blob Data Owner` role for full access to blobs, queues, and tables
          
          ## Consumption Plan (Legacy)
          
          > ⛔ **DO NOT USE** — Y1/Dynamic SKU is deprecated for new deployments.
          > **ALWAYS use Flex Consumption (FC1)** for all new Azure Functions.
          > The Y1 example below is only for reference when migrating legacy apps.
          
          **⚠️ Not recommended for new deployments. Use Flex Consumption instead.**
          
          > 💡 **OS and Slots Matter for Consumption:**
          >
          > - **Linux Consumption** (`kind: 'functionapp,linux'`, `reserved: true`): Does **not** support deployment slots.
          > - **Windows Consumption** (`kind: 'functionapp'`, no `reserved`): Supports **1 staging slot** (2 total including production).
          >   If a user specifically needs Windows Consumption with a slot, that is supported — use the Windows pattern below.
          >   For new apps needing slots, prefer **Elastic Premium (EP1)** for better performance and no cold-start issues.
          
          ### Linux Consumption (no slot support)
          
          ```bicep
          resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
            name: '${resourcePrefix}func${uniqueHash}'
            location: location
            sku: { name: 'Standard_LRS' }
            kind: 'StorageV2'
          }
          
          resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-funcplan-${uniqueHash}'
            location: location
            sku: { name: 'Y1', tier: 'Dynamic' }
            properties: { reserved: true }
          }
          
          resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            kind: 'functionapp,linux'
            identity: { type: 'SystemAssigned' }
            properties: {
              serverFarmId: functionAppPlan.id
              httpsOnly: true
              siteConfig: {
                linuxFxVersion: 'Node|24'
                appSettings: [
                  { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
                  { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
                  { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: appInsights.properties.ConnectionString }
                ]
              }
            }
          }
          ```
          
          ### Windows Consumption (supports 1 staging slot)
          
          > ⚠️ **Windows Consumption is not recommended for new projects** — consider Flex Consumption or Elastic Premium.
          > Use this pattern only for existing Windows apps or when Windows-specific features are required.
          
          ```bicep
          resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-funcplan-${uniqueHash}'
            location: location
            sku: { name: 'Y1', tier: 'Dynamic' }
            // No 'reserved: true' for Windows
          }
          
          resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            kind: 'functionapp'  // Windows (no 'linux' suffix)
            identity: { type: 'SystemAssigned' }
            properties: {
              serverFarmId: functionAppPlan.id
              httpsOnly: true
              siteConfig: {
                appSettings: [
                  { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' }
                  { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
                  { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
                  { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-prod' }
                  { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString }
                ]
              }
            }
          }
          
          // 1 staging slot is supported on Windows Consumption
          resource stagingSlot 'Microsoft.Web/sites/slots@2022-09-01' = {
            parent: functionApp
            name: 'staging'
            location: location
            kind: 'functionapp'
            properties: {
              serverFarmId: functionAppPlan.id
              siteConfig: {
                appSettings: [
                  { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~20' }
                  { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
                  { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
                  { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'WEBSITE_CONTENTSHARE', value: '${toLower(serviceName)}-staging' }  // MUST differ from production
                  { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value}' }
                  { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsights.properties.ConnectionString }
                ]
              }
            }
          }
          
          // Sticky settings — do not swap WEBSITE_CONTENTSHARE between slots
          resource slotConfigNames 'Microsoft.Web/sites/config@2022-09-01' = {
            parent: functionApp
            name: 'slotConfigNames'
            properties: {
              appSettingNames: [
                'WEBSITE_CONTENTSHARE'
                'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
              ]
            }
          }
          ```
          
          ## Service Bus Integration (Managed Identity)
          
          ```bicep
          resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' existing = {
            name: serviceBusNamespaceName
          }
          
          resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
            // ... (Function App definition from above)
            properties: {
              // ... (other properties)
              siteConfig: {
                appSettings: [
                  // Storage with managed identity
                  {
                    name: 'AzureWebJobsStorage__blobServiceUri'
                    value: storageAccount.properties.primaryEndpoints.blob
                  }
                  // Service Bus with managed identity
                  {
                    name: 'SERVICEBUS__fullyQualifiedNamespace'
                    value: '${serviceBusNamespace.name}.servicebus.windows.net'
                  }
                  {
                    name: 'SERVICEBUS_QUEUE_NAME'
                    value: serviceBusQueueName
                  }
                  // Other settings...
                ]
              }
            }
          }
          
          // Grant Service Bus Data Receiver role for triggers
          resource serviceBusReceiverRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(serviceBusNamespace.id, functionApp.id, 'Azure Service Bus Data Receiver')
            scope: serviceBusNamespace
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')
              principalId: functionApp.identity.principalId
              principalType: 'ServicePrincipal'
            }
          }
          
          // Grant Service Bus Data Sender role (if function sends messages)
          resource serviceBusSenderRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(serviceBusNamespace.id, functionApp.id, 'Azure Service Bus Data Sender')
            scope: serviceBusNamespace
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')
              principalId: functionApp.identity.principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          > 💡 **Key Points:**
          >
          > - Use `SERVICEBUS__fullyQualifiedNamespace` (double underscore) for managed identity
          > - Grant `Service Bus Data Receiver` role for reading messages
          > - Grant `Service Bus Data Sender` role for sending messages (if needed)
          > - Role assignments automatically enable connection via managed identity
          
          ## Premium Plan (No Cold Starts)
          
          ```bicep
          resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-funcplan-${uniqueHash}'
            location: location
            sku: { name: 'EP1', tier: 'ElasticPremium' }
            properties: {
              reserved: true
              minimumElasticInstanceCount: 1
            }
          }
          ```
          
          ## Functions on Azure Container Apps (Aspire)
          
          > ⚠️ **Important for .NET Aspire:** When deploying Azure Functions to Azure Container Apps with identity-based storage, you must configure `AzureWebJobsSecretStorageType=Files`.
          
          See [aspire-containerapps.md](aspire-containerapps.md) for complete guidance on Functions running on Azure Container Apps and configuration examples.
          
        • cold-start.md 6.3 KB
          # Azure Functions Cold Start Mitigation
          
          Cold starts occur when a function app must allocate infrastructure, load the runtime, and initialize your code before handling a request. Impact and mitigation options differ significantly by hosting plan.
          
          ## Cold Start Behavior by Plan
          
          Cold-start duration depends on the runtime, dependencies, package size, and initialization work. Measure the latency of the deployed app instead of relying on a fixed estimate.
          
          | Plan | Platform behavior | Primary mitigation |
          |------|-------------------|--------------------|
          | Consumption (Y1) | Scales to zero; cold starts are expected | Reduce dependencies and startup work, or move to another plan |
          | Flex Consumption (FC1) | Improved scale-from-zero behavior | Configure always-ready instances per function or trigger group |
          | Premium (EP1-EP3) | Keeps app-level always-ready instances and an HTTP prewarmed buffer | Configure the app's always-ready instance count |
          | Dedicated | Host runs continuously when `Always On` is enabled | Enable `Always On` |
          | Container Apps (Functions-on-ACA) | Scales to zero when `minReplicas` is `0` | Set `minReplicas` to `1` or higher |
          
          ## Mitigation Strategies
          
          ### Consumption Plan
          
          Consumption has no built-in always-ready setting. Reduce the work required to specialize a new instance:
          
          | Strategy | How | Trade-off |
          |----------|-----|-----------|
          | Reduce package size | Trim unused dependencies; use tree-shaking/bundling | Development effort |
          | Optimize startup code | Lazy-load heavy modules; defer non-critical connections | Code changes required |
          
          > 💡 **Tip:** A timer-based keep-alive isn't a cold-start guarantee and creates extra executions. If cold starts are a consistent problem, move to Flex Consumption or Premium. See [hosting-plans.md](hosting-plans.md) for the comparison.
          
          ### Flex Consumption Plan
          
          Configure always-ready instances for a function group with the dedicated CLI command (do not hand-edit the `functionAppConfig` ARM array — indexing into it by position can silently overwrite other always-ready groups):
          
          ```bash
          # Set 1 always-ready instance for the "http" function group
          az functionapp scale config always-ready set \
            -g $RG -n $APP \
            --settings http=1
          ```
          
          #### Bicep — Always-Ready Configuration
          
          ```bicep
          resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
            name: appName
            location: location
            kind: 'functionapp,linux'
            properties: {
              serverFarmId: flexPlan.id
              functionAppConfig: {
                runtime: {
                  name: 'node'
                  version: '<supported-major-version>'
                }
                scaleAndConcurrency: {
                  alwaysReady: [
                    { name: 'http', instanceCount: 1 }
                  ]
                  instanceMemoryMB: 2048
                  maximumInstanceCount: 100
                }
              }
            }
          }
          ```
          
          > ⚠️ **Warning:** Always-ready instances are billed continuously, separate from and in addition to on-demand instances (they don't count toward `maximumInstanceCount`). Start with 1 instance per group and scale based on observed traffic.
          
          ### Premium Plan
          
          Set the app-level always-ready count so this function app stays loaded. The HTTP prewarmed buffer defaults to one instance and usually shouldn't be changed:
          
          ```bash
          az functionapp update -g $RG -n $APP \
            --set siteConfig.minimumElasticInstanceCount=2
          ```
          
          If you need to reserve plan capacity ahead of scale-out or change its burst ceiling, configure the plan separately:
          
          ```bash
          az functionapp plan update -g $RG -n $PLAN --min-instances 2
          az functionapp plan update -g $RG -n $PLAN --max-burst 20
          ```
          
          > `--min-instances` reserves plan capacity. It doesn't replace the app-level `minimumElasticInstanceCount` setting that keeps a specific app always ready. `--min-elastic-worker-count` isn't a valid parameter for `az functionapp plan update`.
          
          ### Dedicated Plan
          
          Enable `Always On` so the app is never unloaded due to idle timeout:
          
          ```bash
          az functionapp config set -g $RG -n $APP --always-on true
          ```
          
          ### Functions on Container Apps
          
          For new deployments, use the native `Microsoft.App/containerApps` integration and set `kind: 'functionapp'`. A generic container app without this kind doesn't enable the Functions integration. The older `Microsoft.Web/sites` integration is legacy and planned for future deprecation.
          
          ```bicep
          resource functionApp 'Microsoft.App/containerApps@2024-10-02-preview' = {
            name: appName
            location: location
            kind: 'functionapp'
            properties: {
              managedEnvironmentId: containerAppsEnvironment.id
              configuration: {
                ingress: {
                  external: true
                  targetPort: 80
                }
              }
              template: {
                containers: [
                  {
                    name: appName
                    image: containerImage
                    resources: {
                      cpu: json('0.5')
                      memory: '1Gi'
                    }
                  }
                ]
                scale: {
                  minReplicas: 1
                }
              }
            }
          }
          ```
          
          > ⚠️ **Warning:** `minReplicas: 0` allows scale-to-zero; set it to `1` or higher to keep at least one replica running. Configure required Functions settings such as `AzureWebJobsStorage` through secrets or managed identity.
          
          ## Language-Specific Optimization
          
          | Language | Cold Start Tip |
          |----------|---------------|
          | .NET | Use ReadyToRun (or Native AOT where supported) compilation; avoid heavy DI registration in startup |
          | Node.js | Minimize `node_modules`; bundle with esbuild/webpack; use the latest LTS version supported by Azure Functions |
          | Python | Reduce package count; avoid importing unused modules at the top of the file |
          | Java | Prefer the latest supported Java version on Functions v4; minimize static initialization and classpath size |
          | PowerShell | Minimize modules declared in `requirements.psd1` |
          
          ## Recommendation Summary
          
          | Scenario | Recommended Plan | Cold Start Strategy |
          |----------|-----------------|----------------------|
          | Cost-sensitive, tolerates latency | Consumption | Small deployment package + minimal startup work |
          | Low latency, Linux, bursty | Flex Consumption | 1-2 always-ready instances via `az functionapp scale config always-ready set` |
          | Enterprise, strict SLA, Windows or slots needed | Premium | App-level `minimumElasticInstanceCount` + default prewarmed buffer |
          | Shared App Service plan, always running | Dedicated | `Always On = true` |
          | Containerized Functions | Container Apps (Functions-on-ACA) | `minReplicas` set to `1` or higher on a `Microsoft.App/containerApps` resource with `kind: 'functionapp'` |
          
        • durable.md 1.8 KB
          # Durable Functions
          
          Long-running orchestrations with checkpointing and state management.
          
          ## Storage Backend
          
          Durable Functions requires a storage backend to persist orchestration state, queues, and history.
          
          | Backend | Recommendation | Notes |
          |---------|---------------|-------|
          | **Durable Task Scheduler** | **✅ Required** | Fully managed Azure service. Best performance, lowest latency, built-in monitoring. Supports all languages (.NET, Python, Java, JavaScript). |
          | Azure Storage (default) | ⛔ Do not use | Legacy backend. Higher latency, harder to monitor, no built-in RBAC. Not permitted for new projects. |
          | MSSQL | Specialized | SQL Server backend. Only when SQL infrastructure already exists and user explicitly requests it. |
          
          > **⛔ MANDATORY**: You **MUST** use **Durable Task Scheduler** as the storage backend for ALL new Durable Functions projects. Do NOT use Azure Storage as the backend — it is legacy and not permitted. Generate the DTS Bicep module (`Microsoft.DurableTask/schedulers` + `taskHubs`), RBAC role assignment (Durable Task Data Contributor), and `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` app setting. See the [Durable Task Scheduler reference](../durable-task-scheduler/README.md) for setup, language-specific guidance, and Bicep templates.
          
          ## When to Use
          
          - Multi-step workflows
          - Fan-out/fan-in patterns
          - Human interaction workflows
          - Long-running processes
          
          ## Get Durable Templates
          
          Use the [Azure Functions Templates workflow](templates/README.md) to discover and fetch durable samples:
          
          **MCP Tool:** `functions_template_get(language)` → filter `resource == "durable"`
          
          **Fallback:** CDN manifest → filter `resource == "durable"`
          
          Available patterns: Fan-out/Fan-in, Order Processing (with Bicep), Distributed Tracing, Large Payload, Saga, AI Travel Planner, PDF Summarizer, .NET Aspire integration.
          
        • hosting-plans.md 6.8 KB
          # Azure Functions Hosting Plans
          
          ## Plan Comparison Matrix
          
          | Feature | Consumption (Y1) | Flex Consumption (FC1) | Premium (EP1-EP3) | Dedicated (App Service) | Container Apps |
          |---------|:-:|:-:|:-:|:-:|:-:|
          | **Max scale-out (instances)** | 200 | 1,000 | Plan-dependent (up to ~100) | Manual, per plan SKU | Up to 1,000 |
          | **Per-function scaling** | ❌ | ✅ | ❌ | ❌ | ❌ |
          | **Always-ready / min instances** | ❌ | ✅ (always-ready groups) | ✅ (min instances) | ✅ (Always On) | ✅ (min replicas) |
          | **Scale to zero** | ✅ | ✅ | ❌ (min 1 instance) | ❌ | ✅ (`minReplicas: 0`) |
          | **VNet integration (outbound)** | ❌ | ✅ | ✅ | ✅ | ✅ |
          | **Private endpoints (inbound)** | ❌ | ✅ | ✅ | ✅ | ✅ (environment-level) |
          | **Deployment slots (incl. production)** | 2 | ❌ Not supported | 3 | 1-20 (tier-dependent) | Via [revisions](https://learn.microsoft.com/azure/container-apps/revisions) |
          | **Function timeout — default / max** | 5 min / 10 min | 30 min / unbounded | 30 min / unbounded | 30 min / unbounded (requires Always On) | 30 min / unbounded |
          | **OS support** | Windows + Linux | Linux only | Windows + Linux | Windows + Linux | Linux only |
          
          > ⚠️ **"Unbounded" timeout caveat:** Flex Consumption, Premium, Dedicated, and Container Apps allow `functionTimeout` in `host.json` to be set unbounded, but the platform can still recycle a worker: a 60-minute idle timer, up to a 60-minute scale-in grace period, and a 10-minute grace period during platform upgrades. Design long-running work to be resumable (e.g., Durable Functions) rather than relying on a single uninterrupted execution.
          
          ## Instance Sizing
          
          | Plan | SKU | vCPU | Memory |
          |------|-----|------|--------|
          | Consumption | Y1 | Shared/dynamic | 1.5 GB |
          | Flex Consumption | FC1 | 0.25 / 1 / 2 (selectable) | 512 MB / 2,048 MB / 4,096 MB (selectable) |
          | Premium | EP1 | 1 | 3.5 GB |
          | Premium | EP2 | 2 | 7 GB |
          | Premium | EP3 | 4 | 14 GB |
          | Dedicated | B1/S1 | 1 | 1.75 GB |
          | Dedicated | P1v3 | 2 | 8 GB |
          
          > 💡 Flex Consumption instance memory is user-selectable per app (512 MB, 2,048 MB, or 4,096 MB), each mapping to a fixed vCPU allocation. Use 2,048 MB as the default; go smaller for high fan-out/low-per-invocation-cost workloads, larger for CPU- or memory-intensive functions. Each region has a default 250-core (512,000 MB) Flex quota shared across all Flex apps in that subscription/region — request an increase for large-scale deployments.
          
          ## Cost Models
          
          | Plan | Pricing Model | Notes |
          |------|--------------|-------|
          | Consumption | Per-execution + GB-s; free monthly grant | Lowest cost for spiky, low-volume workloads; no charge while idle |
          | Flex Consumption | Per-execution + GB-s; optional always-ready instances billed continuously | Scale-to-zero like Consumption, with opt-in always-ready base cost for latency-sensitive paths |
          | Premium (EP1) | Per-instance-hour; at least 1 instance always allocated | Fixed minimum cost even at zero traffic (no scale-to-zero) |
          | Dedicated (B1) | Per-instance-hour; plan runs continuously | Cost-effective when Functions co-locate with existing App Service Plan capacity |
          | Container Apps | Per-vCPU-second + per-GiB-second; can scale to zero | Pay only while replicas run when `minReplicas: 0` |
          
          ## Decision Criteria
          
          ```
          Need per-function scaling, Linux, and fast/large scale-out?
          ├─ Yes → Flex Consumption
          └─ No
             Need VNet integration or private endpoints?
             ├─ No → Consumption (lowest cost, simplest)
             └─ Yes
                Already running other apps on an App Service Plan, or need Windows + slots + long history of App Service features?
                ├─ Yes → Dedicated (App Service Plan)
                └─ No
                   Budget-sensitive with bursty traffic?
                   ├─ Yes + Linux → Flex Consumption
                   ├─ Yes + Windows → Premium (EP1, cheapest always-on with VNet)
                   └─ No → Premium (predictable pre-warmed latency, deployment slots)
          ```
          
          ## Plan-Specific Considerations
          
          ### Consumption (Y1)
          
          - Best for: low-traffic, event-driven workloads that tolerate occasional cold starts.
          - Limits: 10-minute max execution, no VNet integration, no per-function scaling.
          - Slots: 2 total (production + 1 staging) on Windows and Linux.
          
          ### Flex Consumption (FC1)
          
          - **Recommended default for new Functions apps.** Best for Linux workloads needing fast/large scale-out, VNet, per-function scaling, and configurable instance memory.
          - Limits: Linux only, **no deployment slots** (use [rolling/blue-green site update strategies](https://learn.microsoft.com/azure/azure-functions/flex-consumption-site-updates) instead for zero-downtime deploys).
          - Always-ready instances bypass the max-instance-count ceiling and are billed continuously — start small and measure.
          
          ### Premium (EP1–EP3)
          
          - Best for: latency-sensitive workloads, longer executions, VNet + private endpoints on Windows, or apps needing 3 deployment slots.
          - Always allocates at least 1 instance (no scale-to-zero); minimum instance count and maximum burst are both configurable.
          - Migrating an existing app between Consumption and Premium **on Windows** is supported in place via CLI/PowerShell (no new app required). This migration path is **not supported on Linux** — Linux apps require a new function app on the target plan.
          
          ### Dedicated (App Service Plan)
          
          - Best for: consolidating Functions onto App Service Plan capacity you already run, or workloads needing the broadest App Service feature set (Hybrid Connections, custom domains, `Always On`).
          - Slot count depends on the underlying App Service Plan tier (Basic = 1, Standard = 5, Premium = 20; see [App Service limits](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-app-service-limits)).
          - No automatic scale-to-zero; the plan runs (and is billed) continuously unless you scale it down manually.
          
          ### Container Apps (Functions-on-ACA)
          
          - Best for: teams standardizing on Container Apps/Dapr/microservices, or needing a custom container image for Functions.
          - For new deployments, use the native `Microsoft.App/containerApps` integration with `kind: 'functionapp'`. The older `Microsoft.Web/sites` integration is legacy and planned for future deprecation. See [cold-start.md](cold-start.md#functions-on-container-apps) for the current Bicep shape.
          - Scale-to-zero via `minReplicas: 0`; revisions replace deployment slots for staged rollout.
          
          ## Migration Notes
          
          Switching hosting plans generally requires creating a new function app, **except**: Consumption ↔ Premium migration **on Windows** is supported in place via `az functionapp update` (see [Plan migration](https://learn.microsoft.com/azure/azure-functions/functions-how-to-use-azure-function-app-settings#plan-migration)). All Linux plan changes, and any move to/from Flex Consumption, require redeploying to a new app on the target plan. See `azure-upgrade` skill for Consumption→Flex migration guidance.
          
        • README.md 3.1 KB
          # Azure Functions
          
          Serverless compute for event-driven workloads, APIs, and scheduled tasks.
          
          > **⚠️ MANDATORY: Use Composition Algorithm**
          >
          > **NEVER synthesize Bicep or Terraform from scratch for Azure Functions.**
          >
          > You MUST follow the base + recipe composition workflow:
          > 1. Load [selection.md](templates/selection.md) — decision tree for choosing base template + recipe
          > 2. Follow [composition.md](templates/recipes/composition.md) — the algorithm for fetching and composing
          >
          > This ensures proven IaC patterns, correct RBAC, and Flex Consumption defaults.
          
          ## When to Use
          
          - Event-driven workloads
          - Scheduled tasks (cron jobs)
          - HTTP APIs with variable traffic
          - Message/queue processing
          - Real-time file processing
          - MCP servers for AI agents
          - Real-time streaming and event processing
          - Orchestrations and workflows (Durable Functions)
          
          ## Service Type in azure.yaml
          
          ```yaml
          services:
            my-function:
              host: function
              project: ./src/my-function
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | Storage Account | Function runtime state |
          | App Service Plan | Hosting (Consumption or Premium) |
          | Application Insights | Monitoring |
          
          ## Hosting Plans
          
          **Use Flex Consumption for new deployments** (all AZD templates default to Flex).
          
          For the full comparison matrix (scale limits, instance sizing, deployment slots, cost model, and a decision tree), see **[hosting-plans.md](hosting-plans.md)**. For mitigating cold starts on any plan, see **[cold-start.md](cold-start.md)**.
          
          ## Runtime Stacks
          
          > **⚠️ ALWAYS QUERY OFFICIAL DOCUMENTATION FOR VERSIONS**
          >
          > Do NOT use hardcoded versions. Query for latest GA versions before generating code:
          >
          > **Primary Source:** [Azure Functions Supported Languages](https://learn.microsoft.com/en-us/azure/azure-functions/supported-languages)
          >
          > Use the azure-documentation MCP tool to fetch current supported versions:
          > ```yaml
          > intent: "Azure Functions supported language runtime versions"
          > learn: true
          > ```
          
          ### Version Selection Priority
          1. **Latest GA** — For new projects (best features, longest support window)
          2. **LTS** — For enterprise/compliance requirements
          3. **User-specified** — When explicitly requested
          
          | Language | FUNCTIONS_WORKER_RUNTIME | linuxFxVersion |
          |----------|-------------------------|----------------|
          | Node.js | `node` | `Node\|<version>` |
          | Python | `python` | `Python\|<version>` |
          | .NET | `dotnet-isolated` | `DOTNET-ISOLATED\|<version>` |
          | Java | `java` | `Java\|<version>` |
          | PowerShell | `powershell` | `PowerShell\|<version>` |
          
          ## References
          
          - **[Selection Guide](templates/selection.md)** — Start here: decision tree for base + recipe
          - **[Composition Algorithm](templates/recipes/composition.md)** — How to fetch and compose templates
          - [AZD Templates](templates/README.md) — Template overview
          - [Bicep Patterns](bicep.md)
          - [Terraform Patterns](terraform.md)
          - [Durable Functions](durable.md)
          - [Aspire + Container Apps](aspire-containerapps.md)
          - [Hosting Plans Comparison](hosting-plans.md) — Consumption vs Flex vs Premium vs Dedicated vs Container Apps
          - [Cold Start Mitigation](cold-start.md) — Per-plan strategies and CLI/Bicep examples
          
        • terraform.md 13.3 KB
          # Functions Terraform Patterns — REFERENCE ONLY
          
          > ⛔ **DO NOT COPY THIS CODE DIRECTLY**
          >
          > This file contains **reference patterns** for understanding Azure Functions Terraform structure.
          > **You MUST use the composition algorithm** to generate infrastructure:
          >
          > 1. Load `templates/selection.md` to choose the correct base template
          > 2. Follow `templates/recipes/composition.md` for the exact algorithm
          > 3. Use `functions_template_get` MCP tool (with `infrastructure: "terraform"`) to list and fetch templates and write `functionFiles[]` + `projectFiles[]` directly — NEVER hand-write Bicep/Terraform and use `azd init -t <template>`/`func init`/`func new` as fallback when composing multiple recipes and required templates are not found
          >
          > Hand-writing Terraform from these patterns will result in missing RBAC, incorrect managed identity configuration, and security vulnerabilities.
          
          ## Flex Consumption (Recommended)
          
          **Use Flex Consumption for new deployments with managed identity (no connection strings).**
          
          > **⚠️ IMPORTANT**: Flex Consumption requires **azurerm provider v4.2 or later**.
          
          ```hcl
          terraform {
            required_providers {
              azurerm = {
                source  = "hashicorp/azurerm"
                version = "~> 4.2"
              }
            }
          }
          
          provider "azurerm" {
            features {}
          }
          
          resource "azurerm_storage_account" "function_storage" {
            name                     = "${var.resource_prefix}func${var.unique_hash}"
            location                 = var.location
            resource_group_name      = azurerm_resource_group.main.name
            account_tier             = "Standard"
            account_replication_type = "LRS"
            
            min_tls_version              = "TLS1_2"
            allow_nested_items_to_be_public = false
            shared_access_key_enabled    = false  # Enforce managed identity
          }
          
          resource "azurerm_storage_container" "deployment_package" {
            name                  = "deploymentpackage"
            storage_account_id    = azurerm_storage_account.function_storage.id
            container_access_type = "private"
          }
          
          resource "azurerm_application_insights" "function_insights" {
            name                = "appi-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            application_type    = "web"
          }
          
          resource "azurerm_service_plan" "function_plan" {
            name                = "plan-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            os_type             = "Linux"
            sku_name            = "FC1"
          }
          
          resource "azurerm_linux_function_app" "function_app" {
            name                       = "${var.resource_prefix}-${var.service_name}-${var.unique_hash}"
            location                   = var.location
            resource_group_name        = azurerm_resource_group.main.name
            service_plan_id            = azurerm_service_plan.function_plan.id
            storage_account_name       = azurerm_storage_account.function_storage.name
            storage_uses_managed_identity = true
            https_only                 = true
          
            identity {
              type = "SystemAssigned"
            }
          
            function_app_config {
              deployment {
                storage {
                  type  = "blob_container"
                  value = "${azurerm_storage_account.function_storage.primary_blob_endpoint}${azurerm_storage_container.deployment_package.name}"
                  authentication {
                    type = "SystemAssignedIdentity"
                  }
                }
              }
          
              scale_and_concurrency {
                maximum_instance_count = 100
                instance_memory_mb     = 2048
              }
          
              runtime {
                name    = "python"  # or "node", "dotnet-isolated"
                version = "3.11"
              }
            }
          
            site_config {
              application_insights_connection_string = azurerm_application_insights.function_insights.connection_string
              
              application_stack {
                python_version = "3.11"  # Adjust based on runtime
              }
            }
          
            app_settings = {
              "AzureWebJobsStorage__blobServiceUri"  = azurerm_storage_account.function_storage.primary_blob_endpoint
              "FUNCTIONS_EXTENSION_VERSION"          = "~4"
              "FUNCTIONS_WORKER_RUNTIME"             = "python"
            }
          }
          
          # Grant Function App access to Storage for runtime
          resource "azurerm_role_assignment" "function_storage_access" {
            scope                = azurerm_storage_account.function_storage.id
            role_definition_name = "Storage Blob Data Owner"
            principal_id         = azurerm_linux_function_app.function_app.identity[0].principal_id
          }
          ```
          
          > 💡 **Key Points:**
          > - Use `AzureWebJobsStorage__blobServiceUri` instead of connection string
          > - Set `shared_access_key_enabled = false` for enhanced security
          > - Use `storage_uses_managed_identity = true` for deployment authentication
          > - Grant `Storage Blob Data Owner` role for full access to blobs, queues, and tables
          > - Requires azurerm provider **v4.2 or later**
          
          ### Using Azure Verified Module
          
          For production deployments, use the official Azure Verified Module:
          
          ```hcl
          module "function_app" {
            source  = "Azure/avm-res-web-site/azurerm"
            version = "~> 0.0"
          
            name                = "${var.resource_prefix}-${var.service_name}-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            
            kind    = "functionapp"
            os_type = "Linux"
          
            sku_name = "FC1"
          
            function_app_storage_account_name       = azurerm_storage_account.function_storage.name
            function_app_storage_uses_managed_identity = true
          
            site_config = {
              application_insights_connection_string = azurerm_application_insights.function_insights.connection_string
              
              application_stack = {
                python_version = "3.11"
              }
            }
          
            app_settings = {
              "AzureWebJobsStorage__blobServiceUri" = azurerm_storage_account.function_storage.primary_blob_endpoint
              "FUNCTIONS_EXTENSION_VERSION"         = "~4"
              "FUNCTIONS_WORKER_RUNTIME"            = "python"
            }
          
            identity = {
              type = "SystemAssigned"
            }
          }
          ```
          
          > 💡 **Example Reference:** [HashiCorp Flex Consumption Example](https://registry.terraform.io/modules/Azure/avm-res-web-site/azurerm/latest/examples/flex_consumption)
          
          ## Consumption Plan (Legacy)
          
          > ⛔ **DO NOT USE** — Y1/Dynamic SKU is deprecated for new deployments.
          > **ALWAYS use Flex Consumption (FC1)** for all new Azure Functions.
          > The Y1 example below is only for reference when migrating legacy apps.
          
          **⚠️ Not recommended for new deployments. Use Flex Consumption instead.**
          
          > 💡 **OS and Slots Matter for Consumption:**
          > - **Linux Consumption** (`os_type = "Linux"`): Does **not** support deployment slots.
          > - **Windows Consumption** (`os_type = "Windows"`): Supports **1 staging slot** (2 total including production).
          >   If a user specifically needs Windows Consumption with a slot, that is supported — use the Windows pattern below.
          >   For new apps needing slots, prefer **Elastic Premium (EP1)** for better performance and no cold-start issues.
          
          ### Linux Consumption (no slot support)
          
          ```hcl
          resource "azurerm_storage_account" "function_storage" {
            name                     = "${var.resource_prefix}func${var.unique_hash}"
            location                 = var.location
            resource_group_name      = azurerm_resource_group.main.name
            account_tier             = "Standard"
            account_replication_type = "LRS"
          }
          
          resource "azurerm_service_plan" "function_plan" {
            name                = "${var.resource_prefix}-funcplan-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            os_type             = "Linux"
            sku_name            = "Y1"
          }
          
          resource "azurerm_linux_function_app" "function_app" {
            name                = "${var.resource_prefix}-${var.service_name}-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            service_plan_id     = azurerm_service_plan.function_plan.id
            https_only          = true
            
            storage_account_name       = azurerm_storage_account.function_storage.name
            storage_account_access_key = azurerm_storage_account.function_storage.primary_access_key
            
            site_config {
              application_insights_connection_string = azurerm_application_insights.function_insights.connection_string
              
              application_stack {
                python_version = "3.11"
              }
            }
          
            app_settings = {
              "FUNCTIONS_EXTENSION_VERSION" = "~4"
              "FUNCTIONS_WORKER_RUNTIME"    = "python"
            }
          
            identity {
              type = "SystemAssigned"
            }
          }
          ```
          
          ### Windows Consumption (supports 1 staging slot)
          
          > ⚠️ **Windows Consumption is not recommended for new projects** — consider Flex Consumption or Elastic Premium.
          > Use this pattern only for existing Windows apps or when Windows-specific features are required.
          
          ```hcl
          resource "azurerm_service_plan" "function_plan" {
            name                = "${var.resource_prefix}-funcplan-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            os_type             = "Windows"
            sku_name            = "Y1"
          }
          
          resource "azurerm_windows_function_app" "function_app" {
            name                = "${var.resource_prefix}-${var.service_name}-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            service_plan_id     = azurerm_service_plan.function_plan.id
            https_only          = true
          
            storage_account_name       = azurerm_storage_account.function_storage.name
            storage_account_access_key = azurerm_storage_account.function_storage.primary_access_key
          
            site_config {
              application_insights_connection_string = azurerm_application_insights.function_insights.connection_string
              application_stack {
                node_version = "~20"
              }
            }
          
            app_settings = {
              "FUNCTIONS_EXTENSION_VERSION"             = "~4"
              "FUNCTIONS_WORKER_RUNTIME"                = "node"
              "WEBSITE_CONTENTSHARE"                    = "${lower(var.service_name)}-prod"  # must differ per slot; Azure Files share names are lowercase
              "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.function_storage.primary_connection_string
            }
          
            sticky_settings {
              app_setting_names = ["WEBSITE_CONTENTSHARE", "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING"]
            }
          
            identity {
              type = "SystemAssigned"
            }
          }
          
          # 1 staging slot is supported on Windows Consumption
          resource "azurerm_windows_function_app_slot" "staging" {
            name            = "staging"
            function_app_id = azurerm_windows_function_app.function_app.id
          
            storage_account_name       = azurerm_storage_account.function_storage.name
            storage_account_access_key = azurerm_storage_account.function_storage.primary_access_key
          
            site_config {
              application_insights_connection_string = azurerm_application_insights.function_insights.connection_string
              application_stack {
                node_version = "~20"
              }
            }
          
            app_settings = {
              "FUNCTIONS_EXTENSION_VERSION"             = "~4"
              "FUNCTIONS_WORKER_RUNTIME"                = "node"
              "WEBSITE_CONTENTSHARE"                    = "${var.service_name}-staging"  # MUST differ from production
              "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.function_storage.primary_connection_string
            }
          }
          ```
          
          ## Service Bus Integration (Managed Identity)
          
          ```hcl
          data "azurerm_servicebus_namespace" "example" {
            name                = var.servicebus_namespace_name
            resource_group_name = var.servicebus_resource_group
          }
          
          resource "azurerm_linux_function_app" "function_app" {
            # ... (Function App definition from above)
            
            app_settings = {
              # Storage with managed identity
              "AzureWebJobsStorage__blobServiceUri" = azurerm_storage_account.function_storage.primary_blob_endpoint
              
              # Service Bus with managed identity
              "SERVICEBUS__fullyQualifiedNamespace" = "${data.azurerm_servicebus_namespace.example.name}.servicebus.windows.net"
              "SERVICEBUS_QUEUE_NAME"               = var.servicebus_queue_name
              
              # Other settings...
              "FUNCTIONS_EXTENSION_VERSION"  = "~4"
              "FUNCTIONS_WORKER_RUNTIME"     = "python"
              "APPLICATIONINSIGHTS_CONNECTION_STRING" = azurerm_application_insights.function_insights.connection_string
            }
          }
          
          # Grant Service Bus Data Receiver role for triggers
          resource "azurerm_role_assignment" "servicebus_receiver" {
            scope                = data.azurerm_servicebus_namespace.example.id
            role_definition_name = "Azure Service Bus Data Receiver"
            principal_id         = azurerm_linux_function_app.function_app.identity[0].principal_id
          }
          
          # Grant Service Bus Data Sender role (if function sends messages)
          resource "azurerm_role_assignment" "servicebus_sender" {
            scope                = data.azurerm_servicebus_namespace.example.id
            role_definition_name = "Azure Service Bus Data Sender"
            principal_id         = azurerm_linux_function_app.function_app.identity[0].principal_id
          }
          ```
          
          > 💡 **Key Points:**
          > - Use `SERVICEBUS__fullyQualifiedNamespace` (double underscore) for managed identity
          > - Grant `Service Bus Data Receiver` role for reading messages
          > - Grant `Service Bus Data Sender` role for sending messages (if needed)
          > - Role assignments automatically enable connection via managed identity
          
          ## Premium Plan (No Cold Starts)
          
          ```hcl
          resource "azurerm_service_plan" "function_plan" {
            name                = "${var.resource_prefix}-funcplan-${var.unique_hash}"
            location            = var.location
            resource_group_name = azurerm_resource_group.main.name
            os_type             = "Linux"
            sku_name            = "EP1"  # EP1, EP2, or EP3
          }
          
          resource "azurerm_linux_function_app" "function_app" {
            # ... (rest of configuration similar to Flex Consumption)
            
            site_config {
              # Premium-specific settings
              always_on                      = true
              pre_warmed_instance_count      = 1
              elastic_instance_minimum       = 1
              
              application_stack {
                python_version = "3.11"
              }
            }
          }
          ```
          
      • key-vault
        • bicep.md 1.8 KB
          # Key Vault - Bicep Patterns
          
          ## Basic Vault
          
          ```bicep
          resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
            name: '${resourcePrefix}-kv-${uniqueHash}'
            location: location
            properties: {
              tenantId: subscription().tenantId
              sku: {
                family: 'A'
                name: 'standard'
              }
              enableRbacAuthorization: true
              enableSoftDelete: true
              softDeleteRetentionInDays: 90
              enablePurgeProtection: true
            }
          }
          ```
          
          ## Storing Secrets
          
          ```bicep
          resource secret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
            parent: keyVault
            name: 'database-connection-string'
            properties: {
              value: databaseConnectionString
            }
          }
          ```
          
          ## Role Assignment (Managed Identity)
          
          ```bicep
          resource keyVaultRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(keyVault.id, principalId, 'Key Vault Secrets User')
            scope: keyVault
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          ## Referencing in App Service / Functions
          
          ```bicep
          appSettings: [
            {
              name: 'DATABASE_URL'
              value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-connection-string)'
            }
          ]
          ```
          
          ## Referencing in Container Apps
          
          ```bicep
          secrets: [
            {
              name: 'db-connection'
              keyVaultUrl: '${keyVault.properties.vaultUri}secrets/database-connection-string'
              identity: containerApp.identity.principalId
            }
          ]
          ```
          
          ## Secret with Expiration
          
          ```bicep
          resource secret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
            parent: keyVault
            name: 'api-key'
            properties: {
              value: apiKey
              attributes: {
                exp: dateTimeToEpoch(dateTimeAdd(utcNow(), 'P90D'))
              }
            }
          }
          ```
          
        • README.md 1.4 KB
          # Azure Key Vault
          
          Centralized secrets, keys, and certificate management.
          
          ## When to Use
          
          - Storing application secrets
          - Managing certificates
          - Storing encryption keys
          - Centralizing secret management
          - Enabling secret rotation
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | None required | Key Vault is self-contained |
          | Private Endpoint | Secure access (optional) |
          
          ## SKU Selection
          
          | SKU | Features |
          |-----|----------|
          | Standard | Software-protected keys |
          | Premium | HSM-protected keys |
          
          ## RBAC Roles
          
          | Role | Permissions |
          |------|-------------|
          | Key Vault Administrator | Full access |
          | Key Vault Secrets Officer | Manage secrets |
          | Key Vault Secrets User | Read secrets |
          | Key Vault Certificates Officer | Manage certificates |
          | Key Vault Crypto Officer | Manage keys |
          
          ## Environment Variables
          
          | Variable | Value |
          |----------|-------|
          | `KEY_VAULT_URL` | `https://{vault-name}.vault.azure.net/` |
          | `KEY_VAULT_NAME` | Vault name |
          
          ## Best Practices
          
          1. **Always use RBAC** over access policies
          2. **Enable soft delete and purge protection** for production
          3. **Use managed identities** instead of storing keys in apps
          4. **Set expiration dates** on secrets
          5. **Use separate vaults** for different environments
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [SDK Access](sdk.md)
          
        • sdk.md 2 KB
          # Key Vault - SDK Patterns
          
          ## Node.js
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```javascript
          const { SecretClient } = require("@azure/keyvault-secrets");
          const { DefaultAzureCredential } = require("@azure/identity");
          
          const client = new SecretClient(
            process.env.KEY_VAULT_URL,
            new DefaultAzureCredential()
          );
          
          const secret = await client.getSecret("database-connection-string");
          console.log(secret.value);
          ```
          
          ## Python
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```python
          from azure.keyvault.secrets import SecretClient
          from azure.identity import DefaultAzureCredential
          
          client = SecretClient(
              vault_url=os.environ["KEY_VAULT_URL"],
              credential=DefaultAzureCredential()
          )
          
          secret = client.get_secret("database-connection-string")
          print(secret.value)
          ```
          
          ## .NET
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```csharp
          var client = new SecretClient(
              new Uri(Environment.GetEnvironmentVariable("KEY_VAULT_URL")),
              new DefaultAzureCredential()
          );
          
          KeyVaultSecret secret = await client.GetSecretAsync("database-connection-string");
          Console.WriteLine(secret.Value);
          ```
          
          ## Event Grid Integration (Expiry Notifications)
          
          ```bicep
          resource kvEventSubscription 'Microsoft.EventGrid/eventSubscriptions@2023-12-15-preview' = {
            name: 'secret-expiry-notification'
            scope: keyVault
            properties: {
              destination: {
                endpointType: 'WebHook'
                properties: {
                  endpointUrl: 'https://my-api.example.com/secret-rotation'
                }
              }
              filter: {
                includedEventTypes: [
                  'Microsoft.KeyVault.SecretNearExpiry'
                  'Microsoft.KeyVault.SecretExpired'
                ]
              }
            }
          }
          ```
          
      • logic-apps
        • bicep.md 1.9 KB
          # Logic Apps - Bicep Patterns
          
          ## Consumption (Multi-tenant)
          
          ```bicep
          resource logicApp 'Microsoft.Logic/workflows@2019-05-01' = {
            name: '${resourcePrefix}-logic-${uniqueHash}'
            location: location
            properties: {
              state: 'Enabled'
              definition: {
                '$schema': 'https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#'
                contentVersion: '1.0.0.0'
                triggers: {
                  manual: {
                    type: 'Request'
                    kind: 'Http'
                    inputs: {
                      schema: {}
                    }
                  }
                }
                actions: {}
              }
              parameters: {}
            }
          }
          ```
          
          ## Standard (Single-tenant)
          
          ```bicep
          resource logicAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
            name: '${resourcePrefix}-logicplan-${uniqueHash}'
            location: location
            sku: {
              name: 'WS1'
              tier: 'WorkflowStandard'
            }
            properties: {
              reserved: true
            }
          }
          
          resource logicAppStandard 'Microsoft.Web/sites@2022-09-01' = {
            name: '${resourcePrefix}-logic-${uniqueHash}'
            location: location
            kind: 'functionapp,workflowapp'
            properties: {
              serverFarmId: logicAppPlan.id
              siteConfig: {
                appSettings: [
                  {
                    name: 'FUNCTIONS_EXTENSION_VERSION'
                    value: '~4'
                  }
                  {
                    name: 'FUNCTIONS_WORKER_RUNTIME'
                    value: 'node'
                  }
                  {
                    name: 'AzureWebJobsStorage'
                    value: storageConnectionString
                  }
                ]
              }
            }
          }
          ```
          
          ## API Connection
          
          ```bicep
          resource serviceBusConnection 'Microsoft.Web/connections@2016-06-01' = {
            name: 'servicebus-connection'
            location: location
            properties: {
              displayName: 'Service Bus Connection'
              api: {
                id: subscriptionResourceId('Microsoft.Web/locations/managedApis', location, 'servicebus')
              }
              parameterValues: {
                connectionString: serviceBus.listKeys().primaryConnectionString
              }
            }
          }
          ```
          
        • README.md 1.1 KB
          # Azure Logic Apps
          
          Low-code workflow automation and integration platform.
          
          ## When to Use
          
          - Integration-heavy workloads
          - Business process automation
          - Connecting multiple SaaS services
          - Approval and human workflow processes
          - Low-code/visual workflow design
          - Event-driven orchestration
          
          ## Deployment Note
          
          Logic Apps are typically deployed as infrastructure, not application services:
          
          ```yaml
          # Logic Apps are defined in Bicep infrastructure
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | Storage Account | Workflow state (Standard only) |
          | Log Analytics | Monitoring |
          | API Connections | External service connections |
          
          ## Consumption vs Standard
          
          | Feature | Consumption | Standard |
          |---------|-------------|----------|
          | Pricing | Per execution | App Service Plan |
          | VNET | Limited | Full support |
          | State | Azure-managed | Custom storage |
          | Deployment | ARM/Bicep | VS Code deployment |
          | Multi-workflow | One per resource | Multiple per app |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Triggers](triggers.md)
          
        • triggers.md 1.8 KB
          # Logic Apps - Triggers
          
          ## HTTP Request
          
          ```json
          {
            "triggers": {
              "manual": {
                "type": "Request",
                "kind": "Http",
                "inputs": {
                  "schema": {
                    "type": "object",
                    "properties": {
                      "orderId": { "type": "string" }
                    }
                  }
                }
              }
            }
          }
          ```
          
          ## Recurrence (Schedule)
          
          ```json
          {
            "triggers": {
              "Recurrence": {
                "type": "Recurrence",
                "recurrence": {
                  "frequency": "Hour",
                  "interval": 1
                }
              }
            }
          }
          ```
          
          ## Service Bus Queue
          
          ```json
          {
            "triggers": {
              "When_a_message_is_received": {
                "type": "ApiConnection",
                "inputs": {
                  "host": {
                    "connection": {
                      "name": "@parameters('$connections')['servicebus']['connectionId']"
                    }
                  },
                  "method": "get",
                  "path": "/@{encodeURIComponent('orders')}/messages/head"
                }
              }
            }
          }
          ```
          
          ## Common Actions
          
          ### HTTP Action
          
          ```json
          {
            "HTTP": {
              "type": "Http",
              "inputs": {
                "method": "POST",
                "uri": "https://api.example.com/orders",
                "headers": {
                  "Content-Type": "application/json"
                },
                "body": "@triggerBody()"
              }
            }
          }
          ```
          
          ### Approval Email
          
          ```json
          {
            "Send_approval_email": {
              "type": "ApiConnectionWebhook",
              "inputs": {
                "host": {
                  "connection": {
                    "name": "@parameters('$connections')['office365']['connectionId']"
                  }
                },
                "body": {
                  "NotificationUrl": "@{listCallbackUrl()}",
                  "Message": {
                    "To": "approver@example.com",
                    "Subject": "Approval Required",
                    "Options": "Approve, Reject"
                  }
                },
                "path": "/approvalmail/$subscriptions"
              }
            }
          }
          ```
          
      • service-bus
        • bicep.md 3.8 KB
          # Service Bus - Bicep Patterns
          
          ## Namespace
          
          ```bicep
          resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = {
            name: '${resourcePrefix}-sb-${uniqueHash}'
            location: location
            sku: {
              name: 'Standard'
              tier: 'Standard'
            }
          }
          ```
          
          ## Queue
          
          ```bicep
          resource queue 'Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview' = {
            parent: serviceBus
            name: 'orders'
            properties: {
              maxDeliveryCount: 10
              deadLetteringOnMessageExpiration: true
              defaultMessageTimeToLive: 'P14D'
              lockDuration: 'PT5M'
            }
          }
          ```
          
          ## Topic and Subscription
          
          ```bicep
          resource topic 'Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview' = {
            parent: serviceBus
            name: 'events'
            properties: {
              defaultMessageTimeToLive: 'P14D'
            }
          }
          
          resource subscription 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview' = {
            parent: topic
            name: 'order-processor'
            properties: {
              maxDeliveryCount: 10
              deadLetteringOnMessageExpiration: true
              lockDuration: 'PT5M'
            }
          }
          ```
          
          ## Subscription Filters
          
          ### SQL Filter
          
          ```bicep
          resource filterRule 'Microsoft.ServiceBus/namespaces/topics/subscriptions/rules@2022-10-01-preview' = {
            parent: subscription
            name: 'high-priority'
            properties: {
              filterType: 'SqlFilter'
              sqlFilter: {
                sqlExpression: 'priority = \'high\''
              }
            }
          }
          ```
          
          ### Correlation Filter
          
          ```bicep
          resource correlationRule 'Microsoft.ServiceBus/namespaces/topics/subscriptions/rules@2022-10-01-preview' = {
            parent: subscription
            name: 'orders-only'
            properties: {
              filterType: 'CorrelationFilter'
              correlationFilter: {
                label: 'order'
              }
            }
          }
          ```
          
          ## Managed Identity Access
          
          ### Service Bus Data Receiver (for triggers/consumers)
          
          ```bicep
          resource serviceBusReceiverRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(serviceBus.id, principalId, 'Azure Service Bus Data Receiver')
            scope: serviceBus
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          ### Service Bus Data Sender (for producers)
          
          ```bicep
          resource serviceBusSenderRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(serviceBus.id, principalId, 'Azure Service Bus Data Sender')
            scope: serviceBus
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          ### Both Sender and Receiver
          
          ```bicep
          // Grant both sender and receiver roles for bidirectional messaging
          resource serviceBusReceiverRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(serviceBus.id, principalId, 'receiver')
            scope: serviceBus
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0')
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          
          resource serviceBusSenderRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(serviceBus.id, principalId, 'sender')
            scope: serviceBus
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          > 💡 **Role Selection:**
          > - Use **Data Receiver** for Function triggers or message consumers
          > - Use **Data Sender** for applications that send messages
          > - Use **both roles** for bidirectional communication
          > - Roles can be scoped to namespace (all queues/topics) or specific queue/topic
          
        • patterns.md 4.4 KB
          # Service Bus - Messaging Patterns
          
          ## Point-to-Point (Queue)
          
          ```
          Producer → Queue → Consumer
          ```
          
          Use for: Work distribution, command processing
          
          ## Pub/Sub (Topic + Subscriptions)
          
          ```
          Publisher → Topic → Subscription A → Consumer A
                           → Subscription B → Consumer B
          ```
          
          Use for: Event broadcasting, multiple consumers
          
          ## SDK Patterns
          
          ### Managed Identity (Recommended)
          
          #### Node.js
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```javascript
          const { ServiceBusClient } = require("@azure/service-bus");
          const { DefaultAzureCredential } = require("@azure/identity");
          
          const credential = new DefaultAzureCredential();
          const fullyQualifiedNamespace = process.env.SERVICEBUS_NAMESPACE + ".servicebus.windows.net";
          const client = new ServiceBusClient(fullyQualifiedNamespace, credential);
          
          // Send
          const sender = client.createSender("orders");
          await sender.sendMessages({ body: { orderId: "123" } });
          
          // Receive
          const receiver = client.createReceiver("orders");
          const messages = await receiver.receiveMessages(10);
          for (const message of messages) {
            await receiver.completeMessage(message);
          }
          ```
          
          #### Python
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```python
          from azure.servicebus import ServiceBusClient, ServiceBusMessage
          from azure.identity import DefaultAzureCredential
          
          credential = DefaultAzureCredential()
          fully_qualified_namespace = f"{os.environ['SERVICEBUS_NAMESPACE']}.servicebus.windows.net"
          client = ServiceBusClient(fully_qualified_namespace, credential)
          
          # Send
          sender = client.get_queue_sender("orders")
          with sender:
              sender.send_messages(ServiceBusMessage('{"orderId": "123"}'))
          
          # Receive
          receiver = client.get_queue_receiver("orders")
          with receiver:
              messages = receiver.receive_messages(max_message_count=10, max_wait_time=5)
              for message in messages:
                  print(message)
                  receiver.complete_message(message)
          ```
          
          #### .NET
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```csharp
          using Azure.Identity;
          using Azure.Messaging.ServiceBus;
          
          var credential = new DefaultAzureCredential();
          var fullyQualifiedNamespace = $"{Environment.GetEnvironmentVariable("SERVICEBUS_NAMESPACE")}.servicebus.windows.net";
          var client = new ServiceBusClient(fullyQualifiedNamespace, credential);
          
          // Send
          var sender = client.CreateSender("orders");
          await sender.SendMessageAsync(new ServiceBusMessage("{\"orderId\": \"123\"}"));
          
          // Receive
          var receiver = client.CreateReceiver("orders");
          var messages = await receiver.ReceiveMessagesAsync(maxMessages: 10);
          foreach (var message in messages)
          {
              await receiver.CompleteMessageAsync(message);
          }
          ```
          
          > 💡 **Required Permissions:**
          > - `Azure Service Bus Data Sender` (69a216fc-b8fb-44d8-bc22-1f3c2cd27a39) - for sending
          > - `Azure Service Bus Data Receiver` (4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0) - for receiving
          
          ### Connection String (Legacy)
          
          #### Node.js
          
          ```javascript
          const { ServiceBusClient } = require("@azure/service-bus");
          
          const client = new ServiceBusClient(process.env.SERVICEBUS_CONNECTION_STRING);
          
          // Send
          const sender = client.createSender("orders");
          await sender.sendMessages({ body: { orderId: "123" } });
          
          // Receive
          const receiver = client.createReceiver("orders");
          const messages = await receiver.receiveMessages(10);
          for (const message of messages) {
            await receiver.completeMessage(message);
          }
          ```
          
          #### Python
          
          ```python
          from azure.servicebus import ServiceBusClient, ServiceBusMessage
          
          client = ServiceBusClient.from_connection_string(
              os.environ["SERVICEBUS_CONNECTION_STRING"]
          )
          sender = client.get_queue_sender("orders")
          
          with sender:
              sender.send_messages(ServiceBusMessage('{"orderId": "123"}'))
          ```
          
          #### .NET
          
          ```csharp
          var client = new ServiceBusClient(
              Environment.GetEnvironmentVariable("SERVICEBUS_CONNECTION_STRING")
          );
          var sender = client.CreateSender("orders");
          
          await sender.SendMessageAsync(new ServiceBusMessage("{\"orderId\": \"123\"}"));
          ```
          
          ## Dead Letter Handling
          
          ```javascript
          const dlqReceiver = client.createReceiver("orders", {
              subQueueType: "deadLetter"
          });
          ```
          
        • README.md 1.5 KB
          # Azure Service Bus
          
          Enterprise messaging with queues and pub/sub topics.
          
          ## When to Use
          
          - Reliable message delivery
          - Pub/sub messaging patterns
          - Message ordering requirements
          - Dead-letter handling
          - Transaction support
          - Enterprise integration
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | None required | Service Bus is self-contained |
          | Key Vault | Store connection strings (legacy) |
          
          ## SKU Selection
          
          | SKU | Features | Use Case |
          |-----|----------|----------|
          | Basic | Queues only, 256KB messages | Simple messaging |
          | Standard | Topics, 256KB messages | Pub/sub patterns |
          | Premium | 100MB messages, VNET, zones | Enterprise, high throughput |
          
          ## Environment Variables
          
          ### Managed Identity (Recommended)
          
          | Variable | Value |
          |----------|-------|
          | `SERVICEBUS__fullyQualifiedNamespace` | `<namespace>.servicebus.windows.net` |
          | `SERVICEBUS_NAMESPACE` | Namespace name (for SDK) |
          | `SERVICEBUS_QUEUE` | Queue name |
          
          **Required RBAC roles:**
          - `Azure Service Bus Data Sender` (69a216fc-b8fb-44d8-bc22-1f3c2cd27a39) - for sending
          - `Azure Service Bus Data Receiver` (4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0) - for receiving
          
          ### Connection String (Legacy)
          
          | Variable | Value |
          |----------|-------|
          | `SERVICEBUS_CONNECTION_STRING` | Connection string (Key Vault) |
          | `SERVICEBUS_NAMESPACE` | Namespace name |
          | `SERVICEBUS_QUEUE` | Queue name |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Messaging Patterns](patterns.md)
          
      • sql-database
        • scripts
          • grant-sql-access.ps1 3.4 KB · in bundle
          • grant-sql-access.sh 3.5 KB
            #!/bin/bash
            # Grant Azure SQL data-plane access to the App Service / Container App managed identity.
            #
            # USAGE: Copy this file to scripts/grant-sql-access.sh in your project root and add
            #        a postprovision hook in azure.yaml:
            #
            #   hooks:
            #     postprovision:
            #       posix:
            #         shell: sh
            #         run: ./scripts/grant-sql-access.sh
            #       windows:
            #         shell: pwsh
            #         run: ./scripts/grant-sql-access.ps1
            #
            # ENVIRONMENT VARIABLES (sourced from azd env):
            #   SQL_SERVER           - SQL server name (without .database.windows.net)
            #   SQL_DATABASE         - Database name
            #   AZURE_RESOURCE_GROUP - Resource group name
            #   SERVICE_WEB_NAME     - App Service name (used when set, takes priority)
            #   SERVICE_API_NAME     - API service name (fallback when SERVICE_WEB_NAME is not set)
            #   SQL_GRANT_DDLADMIN   - Set to "true" to also grant db_ddladmin (needed for EF migrations)
            
            set -e
            
            # Safely load azd environment variables without eval
            while IFS= read -r line; do
              [ -n "$line" ] || continue
              key=${line%%=*}
              value=${line#*=}
              case "$value" in
                \"*\") value=${value#\"}; value=${value%\"} ;;
                \'*\') value=${value#\'}; value=${value%\'} ;;
              esac
              export "$key=$value"
            done < <(azd env get-values)
            
            # Determine app identity name (App Service uses SERVICE_WEB_NAME, APIs use SERVICE_API_NAME)
            APP_NAME=${SERVICE_WEB_NAME:-$SERVICE_API_NAME}
            
            if [ -z "$APP_NAME" ]; then
              echo "ERROR: Neither SERVICE_WEB_NAME nor SERVICE_API_NAME is set in azd environment." >&2
              exit 1
            fi
            
            echo "Granting SQL data-plane access to managed identity: $APP_NAME"
            
            # Build idempotent SQL grant queries (reader + writer, required for all apps)
            SQL_QUERIES="
              IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = '$APP_NAME')
                CREATE USER [$APP_NAME] FROM EXTERNAL PROVIDER;
            
              IF NOT EXISTS (
                SELECT 1 FROM sys.database_role_members drm
                JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id
                JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id
                WHERE r.name = 'db_datareader' AND m.name = '$APP_NAME'
              )
                ALTER ROLE db_datareader ADD MEMBER [$APP_NAME];
            
              IF NOT EXISTS (
                SELECT 1 FROM sys.database_role_members drm
                JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id
                JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id
                WHERE r.name = 'db_datawriter' AND m.name = '$APP_NAME'
              )
                ALTER ROLE db_datawriter ADD MEMBER [$APP_NAME];
            "
            
            # Optionally grant db_ddladmin (needed when EF Core migrations run at startup or via hook)
            SQL_GRANT_DDLADMIN="${SQL_GRANT_DDLADMIN:-false}"
            if [ "$SQL_GRANT_DDLADMIN" = "true" ]; then
              SQL_QUERIES="$SQL_QUERIES
              IF NOT EXISTS (
                SELECT 1 FROM sys.database_role_members drm
                JOIN sys.database_principals r ON drm.role_principal_id = r.principal_id
                JOIN sys.database_principals m ON drm.member_principal_id = m.principal_id
                WHERE r.name = 'db_ddladmin' AND m.name = '$APP_NAME'
              )
                ALTER ROLE db_ddladmin ADD MEMBER [$APP_NAME];
            "
            fi
            
            # Ensure sqlcmd is available to execute the query
            if ! command -v sqlcmd >/dev/null 2>&1; then
              echo "ERROR: 'sqlcmd' is not installed or not on PATH." >&2
              echo "Install the modern go-sqlcmd from https://github.com/microsoft/go-sqlcmd and retry." >&2
              exit 1
            fi
            
            # script will exit on error due to "set -e"
            sqlcmd \
              -S "${SQL_SERVER}.database.windows.net" \
              -d "$SQL_DATABASE" \
              --authentication-method ActiveDirectoryDefault \
              -Q "$SQL_QUERIES"
            
            echo "SQL access granted successfully."
            
        • auth.md 4 KB
          # SQL Database - Entra ID Authentication
          
          ## Entra ID Admin Configuration (User)
          
          **Recommended for development** — Uses signed-in user as admin.
          
          ```bicep
          param principalId string
          param principalName string
          @allowed(['User', 'Group', 'Application'])
          param principalType string = 'User'
          
          resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
            name: '${resourcePrefix}-sql-${uniqueHash}'
            location: location
            properties: {
              administrators: {
                administratorType: 'ActiveDirectory'
                principalType: principalType
                login: principalName
                sid: principalId
                tenantId: subscription().tenantId
                azureADOnlyAuthentication: true
              }
              minimalTlsVersion: '1.2'
            }
          }
          ```
          
          > ⚠️ **Warning:** If deploying from CI/CD with a service principal, set `principalType` to `'Application'`. The default `'User'` only works for interactive (human) deployments.
          
          **Get signed-in user info:**
          ```bash
          az ad signed-in-user show --query "{id:id, name:displayName}" -o json
          ```
          
          **Set as azd environment variables:**
          ```bash
          PRINCIPAL_INFO=$(az ad signed-in-user show --query "{id:id, name:displayName}" -o json)
          azd env set AZURE_PRINCIPAL_ID $(echo $PRINCIPAL_INFO | jq -r '.id')
          azd env set AZURE_PRINCIPAL_NAME $(echo $PRINCIPAL_INFO | jq -r '.name')
          ```
          
          > 💡 **Tip:** Set these immediately after `azd init` to avoid deployment failures.
          
          ## Entra ID Admin Configuration (Group)
          
          **Recommended for production** — Uses Entra group for admin access.
          
          ```bicep
          resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
            name: '${resourcePrefix}-sql-${uniqueHash}'
            location: location
            properties: {
              administrators: {
                administratorType: 'ActiveDirectory'
                principalType: 'Group'
                login: 'SQL Admins'
                sid: entraGroupObjectId
                tenantId: subscription().tenantId
                azureADOnlyAuthentication: true
              }
              minimalTlsVersion: '1.2'
            }
          }
          ```
          
          ## Managed Identity Access
          
          Grant app managed identity access via SQL:
          
          ```sql
          CREATE USER [my-container-app] FROM EXTERNAL PROVIDER;
          ALTER ROLE db_datareader ADD MEMBER [my-container-app];
          ALTER ROLE db_datawriter ADD MEMBER [my-container-app];
          ```
          
          ## Common Database Roles
          
          | Role | Permissions |
          |------|-------------|
          | `db_datareader` | Read all tables |
          | `db_datawriter` | Insert, update, delete |
          | `db_ddladmin` | Create/modify schema |
          | `db_owner` | Full access |
          
          ## Connection Strings
          
          ### Entra ID Authentication (Recommended)
          
          > ⚠️ **MANDATORY**: When generating SQL connection strings for apps with Entra-only auth, ALWAYS include an `Authentication` parameter. Use `Authentication=Active Directory Default` for general scenarios (App Service, local dev) or `Authentication=Active Directory Managed Identity` when a user-assigned managed identity with a specific `User Id` is required (e.g., Azure Functions SQL bindings). Do NOT use manual `AccessToken` assignment, `DefaultAzureCredential().GetToken()` patterns, or bare connection strings without an authentication parameter. The `Authentication` parameter is required in both the Bicep infrastructure output (e.g., App Service `connectionStrings` property) and in any application configuration files (e.g., `appsettings.json`).
          
          ```
          Server=tcp:{server}.database.windows.net,1433;Database={database};Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;
          ```
          
          **Required for .NET applications:**
          - `Microsoft.Data.SqlClient` (v5.1.0+)
          - `Azure.Identity` (for local development)
          
          ### Legacy SQL Authentication (⛔ DO NOT USE - EVER)
          
          > ❌ **PROHIBITED — NEVER generate connection strings with `User ID` and `Password` parameters, and NEVER generate `administratorLogin` or `administratorLoginPassword` Bicep properties.**
          > This applies unconditionally — do not include them in any conditional, ternary, or optional branch.
          > SQL admin authentication is disabled in all subscriptions with Entra-only policies and violates Azure security best practices.
          > Always use an Entra ID `Authentication=Active Directory Default` or `Authentication=Active Directory Managed Identity` connection string above.
          
        • bicep.md 7.8 KB
          # SQL Database - Bicep Patterns
          
          > ⛔ **CRITICAL — SQL Server Bicep MUST use Entra-only authentication. NEVER include `administratorLogin` or `administratorLoginPassword` anywhere in a Bicep file — not even inside a conditional (`condition ? { ... } : { administratorLoginPassword: ... }`) branch. If either property name appears anywhere in the file, the deployment will be rejected. Always use the pattern below.**
          
          ## Basic Setup (Entra-Only Authentication)
          
          **Required approach** — Uses Microsoft Entra ID authentication only. Required for subscriptions with Entra-only policies; SQL admin authentication is disabled by policy in those environments.
          
          ```bicep
          param principalId string
          param principalName string
          @allowed(['User', 'Group', 'Application'])
          param principalType string = 'User'
          
          resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
            name: '${resourcePrefix}-sql-${uniqueHash}'
            location: location
            properties: {
              administrators: {
                administratorType: 'ActiveDirectory'
                principalType: principalType
                login: principalName
                sid: principalId
                tenantId: subscription().tenantId
                azureADOnlyAuthentication: true
              }
              minimalTlsVersion: '1.2'
            }
          }
          
          resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
            parent: sqlServer
            name: 'appdb'
            location: location
            sku: {
              name: 'Basic'
              tier: 'Basic'
            }
            properties: {
              collation: 'SQL_Latin1_General_CP1_CI_AS'
              maxSizeBytes: 2147483648  // 2 GB
            }
          }
          
          resource sqlFirewallAzure 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {
            parent: sqlServer
            name: 'AllowAzureServices'
            properties: {
              startIpAddress: '0.0.0.0'
              endIpAddress: '0.0.0.0'
            }
          }
          ```
          
          **Set Entra admin parameters:**
          
          1. Get current user info:
          ```bash
          az ad signed-in-user show --query "{id:id, name:displayName}" -o json
          ```
          
          > ⚠️ **Warning:** If deploying from CI/CD with a service principal, set `principalType` to `'Application'`. The default `'User'` only works for interactive (human) deployments. Mismatched `principalType` causes `UnmatchedPrincipalType` errors during provisioning.
          
          2. Set as azd environment variables:
          ```bash
          PRINCIPAL_INFO=$(az ad signed-in-user show --query "{id:id, name:displayName}" -o json)
          azd env set AZURE_PRINCIPAL_ID $(echo $PRINCIPAL_INFO | jq -r '.id')
          azd env set AZURE_PRINCIPAL_NAME $(echo $PRINCIPAL_INFO | jq -r '.name')
          ```
          
          > 💡 **Tip:** Set these variables immediately after `azd init` to avoid deployment failures. The Bicep `principalId` and `principalName` parameters will automatically use these environment variables.
          
          ## ⚠️ MANDATORY: Connection String with Entra Auth Parameter
          
          > **CRITICAL:** When outputting SQL connection strings in Bicep (e.g., in App Service `connectionStrings` or as outputs), ALWAYS include an `Authentication` parameter. Use `Authentication=Active Directory Default` for general scenarios or `Authentication=Active Directory Managed Identity` when a user-assigned managed identity with a specific `User Id` is required. A bare `Server=tcp:...;Database=...;` connection string without this parameter will not authenticate via Entra ID and the app will fail to connect.
          
          **Correct pattern in Bicep (Active Directory Default):**
          
          ```bicep
          connectionStrings: [
            {
              name: 'DefaultConnection'
              connectionString: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;'
              type: 'SQLAzure'
            }
          ]
          ```
          
          **With user-assigned managed identity:**
          
          ```bicep
          connectionStrings: [
            {
              name: 'DefaultConnection'
              connectionString: 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Database=${sqlDatabase.name};Authentication=Active Directory Managed Identity;User Id=${uamiClientId};Encrypt=True;TrustServerCertificate=False;'
              type: 'SQLAzure'
            }
          ]
          ```
          
          > ❌ **DO NOT** generate connection strings without an `Authentication=Active Directory Default` or `Authentication=Active Directory Managed Identity` parameter.
          > ❌ **DO NOT** use manual `AccessToken` assignment or `DefaultAzureCredential().GetToken()` in application code as an alternative to the connection string parameter.
          
          ## Serverless Configuration
          
          ```bicep
          resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
            parent: sqlServer
            name: 'appdb'
            location: location
            sku: {
              name: 'GP_S_Gen5'
              tier: 'GeneralPurpose'
              family: 'Gen5'
              capacity: 2
            }
            properties: {
              autoPauseDelay: 60  // minutes
              minCapacity: json('0.5')
            }
          }
          ```
          
          ## Private Endpoint
          
          ```bicep
          resource sqlPrivateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
            name: '${sqlServer.name}-pe'
            location: location
            properties: {
              subnet: {
                id: subnet.id
              }
              privateLinkServiceConnections: [
                {
                  name: '${sqlServer.name}-connection'
                  properties: {
                    privateLinkServiceId: sqlServer.id
                    groupIds: ['sqlServer']
                  }
                }
              ]
            }
          }
          ```
          
          ## ⛔ MANDATORY: SQL Data-Plane Access via postprovision Hook
          
          > **CRITICAL:** ARM/Bicep role assignments (`SQL DB Contributor`) only grant **control-plane** access. They do **not** grant the app **data-plane** access to the database. Without the T-SQL grant below, apps using `Authentication=Active Directory Default` will crash on startup with a login failure.
          >
          > **When you generate SQL + Managed Identity infrastructure you MUST also:**
          > 1. Add a `postprovision` hook to `azure.yaml` that runs the SQL grant script
          > 2. Copy the pre-built scripts from [scripts/grant-sql-access.sh](scripts/grant-sql-access.sh) and [scripts/grant-sql-access.ps1](scripts/grant-sql-access.ps1) to `scripts/` in the user's project root
          
          **azure.yaml hooks section (add or merge):**
          
          ```yaml
          hooks:
            postprovision:
              posix:
                shell: sh
                run: ./scripts/grant-sql-access.sh
              windows:
                shell: pwsh
                run: ./scripts/grant-sql-access.ps1
          ```
          
          **Copy the pre-built scripts** — Read [scripts/grant-sql-access.sh](scripts/grant-sql-access.sh) and [scripts/grant-sql-access.ps1](scripts/grant-sql-access.ps1) and write them verbatim to the project's `scripts/` folder. Do not regenerate them from scratch.
          
          Key behaviours of the scripts:
          - Installs the `rdbms-connect` Azure CLI extension if not already present (required for `az sql db query`)
          - Loads `azd env get-values` safely (no `eval`)
          - Grants `db_datareader` + `db_datawriter` by default (idempotent)
          - Set `SQL_GRANT_DDLADMIN=true` in the azd env to also grant `db_ddladmin` (needed for EF Core migrations)
          - `SERVICE_WEB_NAME` takes priority over `SERVICE_API_NAME` when resolving the app identity
          
          > 💡 Make executable: `chmod +x scripts/*.sh`
          
          > 💡 If the app also uses EF Core migrations, combine this with the migration hook — see the `azure-deploy` skill's `references/recipes/azd/sql-managed-identity.md`.
          
          ## Legacy SQL Authentication (⛔ DO NOT USE - EVER)
          
          > ❌ **PROHIBITED — NEVER generate `administratorLogin` or `administratorLoginPassword` properties.**
          > This applies unconditionally: do NOT include them in direct assignments, conditional expressions, ternary branches, parameter defaults, or any other construct. The **text** of these property names must not appear anywhere in a generated `.bicep` file.
          > SQL admin authentication is disabled in all subscriptions with Entra-only policies and violates Azure security best practices.
          > Always use the Entra-only authentication pattern at the top of this file.
          >
          > ❌ **DO NOT generate conditional patterns like:**
          > ```bicep
          > properties: useEntraAuth ? {
          >   administrators: {
          >     azureADOnlyAuthentication: true
          >     // ...
          >   }
          > } : {
          >   administratorLogin: sqlAdminLogin          // ❌ PROHIBITED
          >   administratorLoginPassword: sqlAdminPassword  // ❌ PROHIBITED
          > }
          > ```
          > Use only the unconditional Entra-only pattern. No fallback. No toggle. No conditional.
          
        • README.md 1.9 KB
          # Azure SQL Database
          
          Managed relational database with ACID compliance and full SQL Server compatibility.
          
          ## When to Use
          
          - Relational data with ACID requirements
          - Complex queries and joins
          - Existing SQL Server workloads
          - Reporting and analytics
          - Strong schema enforcement
          
          ## Authentication
          
          **Default:** Entra-only authentication (recommended)
          - Required for subscriptions with Entra-only policies
          - More secure than SQL authentication
          - Eliminates password management
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | Key Vault | Store connection strings |
          | Private Endpoint | Secure access (optional) |
          
          ## SKU Selection
          
          | Tier | Use Case | Features |
          |------|----------|----------|
          | **Basic** | Dev/test, light workloads | 5 DTUs, 2GB |
          | **Standard** | Production workloads | 10-3000 DTUs |
          | **Premium** | High-performance | In-memory OLTP |
          | **Serverless** | Variable workloads | Auto-pause, auto-scale |
          | **Hyperscale** | Large databases | 100TB+, instant backup |
          
          ## Environment Variables
          
          | Variable | Value | When to Set |
          |----------|-------|-------------|
          | `AZURE_PRINCIPAL_ID` | Current user's object ID | After `azd init`, before `azd provision` |
          | `AZURE_PRINCIPAL_NAME` | Current user's display name | After `azd init`, before `azd provision` |
          | `SQL_SERVER` | `{server}.database.windows.net` | Runtime (from Bicep outputs) |
          | `SQL_DATABASE` | Database name | Runtime (from Bicep outputs) |
          | `SQL_CONNECTION_STRING` | Full connection string (Key Vault) | Runtime (from Bicep outputs) |
          
          **Set principal variables:**
          ```bash
          PRINCIPAL_INFO=$(az ad signed-in-user show --query "{id:id, name:displayName}" -o json)
          azd env set AZURE_PRINCIPAL_ID $(echo $PRINCIPAL_INFO | jq -r '.id')
          azd env set AZURE_PRINCIPAL_NAME $(echo $PRINCIPAL_INFO | jq -r '.name')
          ```
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Entra ID Auth](auth.md)
          - [SDK Patterns](sdk.md)
          
        • sdk.md 1.9 KB
          # SQL Database - SDK Patterns
          
          ## Node.js (mssql)
          
          ```javascript
          const sql = require('mssql');
          
          const config = {
            server: process.env.SQL_SERVER,
            database: process.env.SQL_DATABASE,
            authentication: {
              type: 'azure-active-directory-default'
            },
            options: {
              encrypt: true
            }
          };
          
          const pool = await sql.connect(config);
          ```
          
          ## Python (pyodbc)
          
          > **Auth:** `DefaultAzureCredential` is for local development. See [auth-best-practices.md](../../auth-best-practices.md) for production patterns.
          
          ```python
          import pyodbc
          from azure.identity import DefaultAzureCredential
          
          credential = DefaultAzureCredential()
          token = credential.get_token("https://database.windows.net/.default")
          
          conn = pyodbc.connect(
              f"Driver={{ODBC Driver 18 for SQL Server}};"
              f"Server={os.environ['SQL_SERVER']};"
              f"Database={os.environ['SQL_DATABASE']};"
              f"Authentication=ActiveDirectoryMsi"
          )
          ```
          
          ## .NET (Entity Framework Core)
          
          **Required NuGet Packages:**
          ```bash
          dotnet add package Microsoft.EntityFrameworkCore.SqlServer
          dotnet add package Microsoft.Data.SqlClient --version 5.1.0
          dotnet add package Azure.Identity
          ```
          
          **Connection string (Entra ID):**
          ```
          Server=tcp:{server}.database.windows.net,1433;Database={database};Authentication=Active Directory Default;Encrypt=True;
          ```
          
          **Configuration:**
          ```csharp
          services.AddDbContext<AppDbContext>(options =>
              options.UseSqlServer(
                  Configuration.GetConnectionString("DefaultConnection"),
                  sqlOptions => sqlOptions.EnableRetryOnFailure()
              ));
          ```
          
          **appsettings.json:**
          ```json
          {
            "ConnectionStrings": {
              "DefaultConnection": "Server=tcp:myserver.database.windows.net,1433;Database=mydb;Authentication=Active Directory Default;Encrypt=True;"
            }
          }
          ```
          
          ## Connection String Format
          
          ```
          Server=tcp:{server}.database.windows.net,1433;Database={database};Authentication=Active Directory Default;Encrypt=True;
          ```
          
      • static-web-apps
        • bicep.md 1.1 KB
          # Static Web Apps - Bicep Patterns
          
          ## Basic Resource
          
          ```bicep
          resource staticWebApp 'Microsoft.Web/staticSites@2022-09-01' = {
            name: '${resourcePrefix}-${serviceName}-${uniqueHash}'
            location: location
            sku: {
              name: 'Standard'
              tier: 'Standard'
            }
            properties: {
              buildProperties: {
                appLocation: '/'
                apiLocation: 'api'
                outputLocation: 'dist'
              }
            }
          }
          ```
          
          ## Custom Domain
          
          ```bicep
          resource customDomain 'Microsoft.Web/staticSites/customDomains@2022-09-01' = {
            parent: staticWebApp
            name: 'www.example.com'
            properties: {}
          }
          ```
          
          ## Application Settings
          
          For the integrated API:
          
          ```bicep
          resource staticWebAppSettings 'Microsoft.Web/staticSites/config@2022-09-01' = {
            parent: staticWebApp
            name: 'appsettings'
            properties: {
              DATABASE_URL: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=db-url)'
            }
          }
          ```
          
          ## Deployment Token
          
          > ⚠️ **Security Warning:** Do NOT expose deployment tokens in Bicep outputs.
          
          See [deployment.md](deployment.md) for secure token handling.
          
        • deployment.md 1.9 KB
          # Static Web Apps - Deployment
          
          ## azd Deploy (Default)
          
          Standard deployment via Azure Developer CLI:
          
          ```bash
          azd deploy
          ```
          
          ## GitHub-Linked Deployments
          
          For CI/CD builds on Azure (instead of azd deploy):
          
          ```bicep
          properties: {
            repositoryUrl: 'https://github.com/owner/repo'
            branch: 'main'
            buildProperties: {
              appLocation: 'src'
              apiLocation: 'api'
              outputLocation: 'dist'
            }
          }
          ```
          
          ## Deployment Token
          
          > ⚠️ **Security Warning:** Do NOT expose deployment tokens in ARM/Bicep outputs. Deployment outputs are visible in Azure portal deployment history and logs.
          
          **Recommended approach** - retrieve token via Azure CLI and store directly in secret store:
          
          ```bash
          # Capture token to variable (never echo or log)
          DEPLOYMENT_TOKEN=$(az staticwebapp secrets list --name <app-name> --query "properties.apiKey" -o tsv)
          
          # Store directly in Key Vault
          az keyvault secret set --vault-name <vault-name> --name swa-deployment-token --value "$DEPLOYMENT_TOKEN" --output none
          ```
          
          **Do NOT do this** (exposes token in deployment history):
          ```bicep
          // ❌ INSECURE - token visible in deployment history
          // output deploymentToken string = staticWebApp.listSecrets().properties.apiKey
          ```
          
          ## Terraform Deployment
          
          > ⚠️ **Use `azurerm_static_web_app`** — NOT Storage Account `static_website`.
          > Storage Account static websites require anonymous blob access, which is blocked
          > by enterprise Azure Policies. See [terraform.md](terraform.md) for correct patterns.
          
          Terraform-based deployments use `azd deploy` the same way as Bicep:
          
          ```bash
          azd deploy
          ```
          
          The `azd-service-name` tag on the `azurerm_static_web_app` resource tells azd where to deploy.
          
          **Do NOT do this** (exposes token in Terraform state):
          ```hcl
          # ❌ INSECURE - token stored in Terraform state
          # output "deployment_token" {
          #   value     = azurerm_static_web_app.web.api_key
          #   sensitive = true
          # }
          ```
          
        • README.md 1.4 KB
          # Azure Static Web Apps
          
          Serverless hosting for static sites and SPAs with integrated APIs.
          
          ## When to Use
          
          - Single Page Applications (React, Vue, Angular)
          - Static sites (HTML/CSS/JS)
          - JAMstack applications
          - Sites with serverless API backends
          - Documentation sites
          
          ## Service Type in azure.yaml
          
          ```yaml
          services:
            my-web:
              host: staticwebapp
              project: ./src/web
          ```
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | None required | Static Web Apps is fully managed |
          | Application Insights | Monitoring (optional) |
          
          ## SKU Selection
          
          | SKU | Features |
          |-----|----------|
          | Free | 2 custom domains, 0.5GB storage, shared bandwidth |
          | Standard | 5 custom domains, 2GB storage, SLA, auth customization |
          
          ## Build Configuration
          
          | Framework | outputLocation |
          |-----------|----------------|
          | React | `build` |
          | Vue | `dist` |
          | Angular | `dist/my-app` |
          | Next.js (Static) | `out` |
          
          ## API Integration
          
          Integrated Functions API structure:
          
          ```
          project/
          ├── src/           # Frontend
          └── api/           # Azure Functions API
              ├── hello/
              │   └── index.js
              └── host.json
          ```
          
          ## References
          
          - [Region Availability](region-availability.md)
          - [Bicep Patterns](bicep.md)
          - [Terraform Patterns](terraform.md)
          - [Routing and Auth](routing.md)
          - [Deployment](deployment.md)
          
        • region-availability.md 695 B
          # SWA Region Availability
          
          ⚠️ **NOT available in many common regions** — Check before deployment.
          
          | ✅ Available | ❌ NOT Available (will FAIL) |
          |-------------|------------------------------|
          | `westus2` | `eastus` |
          | `centralus` | `northeurope` |
          | `eastus2` | `southeastasia` |
          | `westeurope` | `uksouth` |
          | `eastasia` | `canadacentral` |
          | | `australiaeast` |
          | | `westus3` |
          
          ## Recommended Regions
          
          | Pattern | Use |
          |---------|-----|
          | SWA only | `westus2`, `centralus`, `eastus2`, `westeurope`, `eastasia` |
          | SWA + backend | `westus2`, `centralus`, `eastus2`, `westeurope`, `eastasia` |
          | SWA + Azure OpenAI | `eastus2` (only region with full overlap) |
          
        • routing.md 1.3 KB
          # Static Web Apps - Routing & Authentication
          
          ## Route Configuration
          
          Create `staticwebapp.config.json` in the app root:
          
          ```json
          {
            "routes": [
              {
                "route": "/api/*",
                "allowedRoles": ["authenticated"]
              }
            ],
            "navigationFallback": {
              "rewrite": "/index.html",
              "exclude": ["/api/*", "/*.{png,jpg,gif}"]
            },
            "responseOverrides": {
              "404": {
                "rewrite": "/404.html"
              }
            }
          }
          ```
          
          ## Authentication
          
          ### Built-in Providers
          
          ```json
          {
            "routes": [
              {
                "route": "/admin/*",
                "allowedRoles": ["admin"]
              }
            ],
            "auth": {
              "identityProviders": {
                "azureActiveDirectory": {
                  "registration": {
                    "openIdIssuer": "https://login.microsoftonline.com/{tenant-id}",
                    "clientIdSettingName": "AAD_CLIENT_ID",
                    "clientSecretSettingName": "AAD_CLIENT_SECRET"
                  }
                }
              }
            }
          }
          ```
          
          ### Supported Providers
          
          - Azure Active Directory / Entra ID
          - GitHub
          - Twitter
          - Custom OpenID Connect
          
          ## Role-Based Access
          
          ```json
          {
            "routes": [
              { "route": "/admin/*", "allowedRoles": ["admin"] },
              { "route": "/account/*", "allowedRoles": ["authenticated"] },
              { "route": "/*", "allowedRoles": ["anonymous"] }
            ]
          }
          ```
          
        • terraform.md 3.5 KB
          # Static Web Apps — Terraform Patterns — REFERENCE ONLY
          
          > ⛔ **DO NOT COPY THIS CODE DIRECTLY**
          >
          > This file contains **reference patterns** for understanding Azure Static Web Apps Terraform structure.
          > Infrastructure for Static Web Apps must be composed using the Azure Prepare skill workflow, not copied directly from this reference.
          >
          > When composing infrastructure:
          > - Start from an approved base template for Static Web Apps defined by your platform or template team.
          > - Use `azurerm_static_web_app` — **NEVER** use Storage Account `static_website` for static web app hosting.
          >
          > Hand-writing Terraform from these patterns will result in missing tags, broken azd deploy, and policy violations.
          
          > ⚠️ **WARNING: Do NOT use Storage Account static website hosting.**
          > Storage Account `static_website` requires anonymous blob access, which violates
          > enterprise Azure Policies (`RequestDisallowedByPolicy: "Anonymous blob access is not allowed"`).
          > Always use `azurerm_static_web_app` instead — it is fully managed and policy-compliant.
          
          ## Basic Resource
          
          ```hcl
          resource "azurerm_static_web_app" "web" {
            name                = "swa-${var.environment_name}-${var.service_name}-${var.unique_hash}"
            resource_group_name = azurerm_resource_group.main.name
            location            = var.location
          
            # sku_tier defaults to "Free"; set to "Standard" for production features
            sku_tier = "Standard"
          
            # Required for azd deploy to find this resource
            tags = merge(var.tags, {
              "azd-service-name" = var.service_name
            })
          }
          ```
          
          > ⚠️ **Region availability is limited.** Check [region-availability.md](region-availability.md) before selecting a region.
          
          > 💡 **Key Points:**
          > - No Storage Account, Service Plan, or other supporting resources required
          > - Static Web Apps is a fully managed service — storage is built-in
          > - The `azd-service-name` tag is **required** for `azd deploy` to locate the resource
          
          ## Custom Domain
          
          ```hcl
          resource "azurerm_static_web_app_custom_domain" "example" {
            static_web_app_id = azurerm_static_web_app.web.id
            domain_name       = "www.example.com"
            validation_type   = "cname-delegation"
          }
          ```
          
          ## Application Settings
          
          For the integrated API backend:
          
          ```hcl
          resource "azurerm_static_web_app_function_app_registration" "api" {
            static_web_app_id = azurerm_static_web_app.web.id
            function_app_id   = azurerm_linux_function_app.api.id
          }
          ```
          
          Environment variables for the SWA (available to both frontend and managed Functions API):
          
          ```hcl
          resource "azurerm_static_web_app" "web" {
            # ... (base configuration from above)
          
            app_settings = {
              "DATABASE_URL" = "@Microsoft.KeyVault(VaultName=${azurerm_key_vault.kv.name};SecretName=db-url)"
            }
          }
          ```
          
          ## Outputs
          
          ```hcl
          output "WEB_URL" {
            value = azurerm_static_web_app.web.default_host_name
          }
          
          output "STATIC_WEB_APP_NAME" {
            value = azurerm_static_web_app.web.name
          }
          ```
          
          > 💡 **Tip:** Output names in UPPERCASE are automatically set as azd environment variables.
          
          ## Deployment Token
          
          > ⚠️ **Security Warning:** Do NOT expose deployment tokens in Terraform outputs.
          
          See [deployment.md](deployment.md) for secure token handling.
          
          ## azure.yaml Integration
          
          ```yaml
          services:
            web:
              project: ./src/web
              language: js
              host: staticwebapp
              dist: dist
          ```
          
          ## References
          
          - [AZD + Terraform](../../recipes/azd/terraform.md) — Full azd+Terraform project setup
          - [Region Availability](region-availability.md) — SWA is NOT available in all regions
          - [Routing and Auth](routing.md)
          - [Deployment](deployment.md)
          
      • storage
        • access.md 3.7 KB
          # Storage - Access Patterns
          
          ## Prerequisites for Granting Storage Access
          
          > ⚠️ **Important**: To assign storage roles to managed identities, you need:
          > - **User Access Administrator** or **Owner** role on the Storage Account (or parent resource group/subscription)
          > - The role must include the `Microsoft.Authorization/roleAssignments/write` permission
          
          **Common scenarios**:
          - Granting Storage Blob Data Owner to a Web App or Function App's managed identity (System Assigned or User Assigned)
          - Adding read/write access to blobs, queues, and tables for application workloads
          - Allowing user identities (developers, data admins) access in dev/test environments
          - Allowing applications to access storage using managed identity instead of connection strings
          
          **Scope best practices**:
          - Grant roles at the **smallest scope possible** (e.g., specific storage account, not resource group or subscription)
          - Avoid broad scopes (Resource Group, Subscription, Tenant) unless absolutely necessary
          - Prefer resource-level assignments for production workloads
          
          **Managed identity types**:
          - **System Assigned**: Automatically created with the resource (Web App, Function). Default when using `DefaultAzureCredential`.
          - **User Assigned**: Standalone identity that can be shared across resources. Requires additional configuration:
            - Set `AZURE_CLIENT_ID` app setting to the User Assigned Managed Identity's client ID
            - Configure identity in Bicep with both `type: 'SystemAssigned, UserAssigned'` and `userAssignedIdentities`
          
          If you encounter `AuthorizationFailed` errors when assigning roles, ensure you have User Access Administrator or Owner permissions at the target scope.
          
          ## Managed Identity Role Assignment
          
          ```bicep
          resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
            name: guid(storageAccount.id, principalId, 'Storage Blob Data Contributor')
            scope: storageAccount
            properties: {
              roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
              principalId: principalId
              principalType: 'ServicePrincipal'
            }
          }
          ```
          
          ## Storage Roles
          
          | Role | Permissions |
          |------|-------------|
          | Storage Blob Data Reader | Read blobs |
          | Storage Blob Data Contributor | Read/write blobs |
          | Storage Queue Data Contributor | Read/write queues |
          | Storage Table Data Contributor | Read/write tables |
          
          ## SDK Connection Patterns
          
          ### Node.js
          
          ```javascript
          const { BlobServiceClient } = require("@azure/storage-blob");
          
          const blobServiceClient = BlobServiceClient.fromConnectionString(
            process.env.AZURE_STORAGE_CONNECTION_STRING
          );
          const containerClient = blobServiceClient.getContainerClient("uploads");
          ```
          
          ### Python
          
          ```python
          from azure.storage.blob import BlobServiceClient
          
          blob_service_client = BlobServiceClient.from_connection_string(
              os.environ["AZURE_STORAGE_CONNECTION_STRING"]
          )
          container_client = blob_service_client.get_container_client("uploads")
          ```
          
          ### .NET
          
          ```csharp
          var blobServiceClient = new BlobServiceClient(
              Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING")
          );
          var containerClient = blobServiceClient.GetBlobContainerClient("uploads");
          ```
          
          ## Managed Identity Access
          
          Use `DefaultAzureCredential` for local development (in production, use `ManagedIdentityCredential` — see [auth-best-practices.md](../../auth-best-practices.md)):
          
          ```javascript
          const { DefaultAzureCredential } = require("@azure/identity");
          const { BlobServiceClient } = require("@azure/storage-blob");
          
          const client = new BlobServiceClient(
            `https://${accountName}.blob.core.windows.net`,
            new DefaultAzureCredential()
          );
          ```
          
        • bicep.md 1.8 KB
          # Storage - Bicep Patterns
          
          ## Storage Account
          
          ```bicep
          resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
            name: '${resourcePrefix}stor${uniqueHash}'
            location: location
            sku: {
              name: 'Standard_LRS'
            }
            kind: 'StorageV2'
            properties: {
              accessTier: 'Hot'
              allowBlobPublicAccess: false
              minimumTlsVersion: 'TLS1_2'
              supportsHttpsTrafficOnly: true
            }
          }
          ```
          
          ## Blob Container
          
          ```bicep
          resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
            parent: storageAccount
            name: 'default'
            properties: {
              deleteRetentionPolicy: {
                enabled: true
                days: 7
              }
            }
          }
          
          resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
            parent: blobService
            name: 'uploads'
            properties: {
              publicAccess: 'None'
            }
          }
          ```
          
          ## Queue
          
          ```bicep
          resource queueService 'Microsoft.Storage/storageAccounts/queueServices@2023-01-01' = {
            parent: storageAccount
            name: 'default'
          }
          
          resource queue 'Microsoft.Storage/storageAccounts/queueServices/queues@2023-01-01' = {
            parent: queueService
            name: 'orders'
          }
          ```
          
          ## Table
          
          ```bicep
          resource tableService 'Microsoft.Storage/storageAccounts/tableServices@2023-01-01' = {
            parent: storageAccount
            name: 'default'
          }
          
          resource table 'Microsoft.Storage/storageAccounts/tableServices/tables@2023-01-01' = {
            parent: tableService
            name: 'logs'
          }
          ```
          
          ## File Share
          
          ```bicep
          resource fileService 'Microsoft.Storage/storageAccounts/fileServices@2023-01-01' = {
            parent: storageAccount
            name: 'default'
          }
          
          resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-01-01' = {
            parent: fileService
            name: 'shared'
            properties: {
              shareQuota: 100  // GB
            }
          }
          ```
          
        • README.md 1.5 KB
          # Azure Storage
          
          Scalable cloud storage for blobs, files, queues, and tables.
          
          ## When to Use
          
          - Blob storage (files, images, videos)
          - File shares (SMB/NFS)
          - Queue storage (simple messaging)
          - Table storage (NoSQL key-value)
          - Static website hosting
          
          ## Required Supporting Resources
          
          | Resource | Purpose |
          |----------|---------|
          | None required | Storage is self-contained |
          | Key Vault | Store connection strings |
          | Private Endpoint | Secure access (optional) |
          
          ## SKU Selection
          
          | SKU | Replication | Use Case |
          |-----|-------------|----------|
          | Standard_LRS | Local (3 copies) | Dev/test, non-critical |
          | Standard_ZRS | Zone-redundant | Production, regional HA |
          | Standard_GRS | Geo-redundant | DR requirements |
          | Premium_LRS | Premium SSD | High performance |
          
          ## Storage Types
          
          | Type | Best For |
          |------|----------|
          | Blob | Files, images, videos, backups, logs |
          | Queue | Simple message queuing, decoupling |
          | Table | NoSQL key-value data |
          | File Share | Lift-and-shift, SMB/NFS access |
          
          ## Access Tiers
          
          | Tier | Use Case |
          |------|----------|
          | Hot | Frequent access |
          | Cool | Infrequent access (30+ days) |
          | Archive | Rare access (180+ days) |
          
          ## Environment Variables
          
          | Variable | Value |
          |----------|-------|
          | `AZURE_STORAGE_CONNECTION_STRING` | Connection string (Key Vault) |
          | `AZURE_STORAGE_ACCOUNT` | Account name |
          | `AZURE_STORAGE_CONTAINER` | Container name |
          
          ## References
          
          - [Bicep Patterns](bicep.md)
          - [Access Patterns](access.md)
          
    • analyze.md 4 KB
      # Analyze Workspace
      
      ## ⛔ MANDATORY FIRST — Specialized Technology Delegation
      
      **STOP. Before choosing a mode, check the user's prompt for specialized technology keywords.**
      If matched, invoke the corresponding skill **immediately** — it has tested templates and correct SDK usage.
      
      > ⚠️ **Re-entry guard**: If azure-prepare was invoked as a **resume** from a specialized skill, **skip this check** and go directly to Step 4.
      
      | User prompt mentions | Action |
      |---------------------|--------|
      | Azure Functions, function app, serverless function, timer trigger, func new | Stay in **azure-prepare**. When selecting compute, **prefer Azure Functions** templates and best practices, then continue from Step 4. |
      
      > ⚠️ Check the user's **prompt text** — not just existing code. This is critical for greenfield projects with no codebase. See [full routing table](specialized-routing.md).
      
      If no match, continue below.
      
      ---
      
      ## Three Modes — Always Choose One
      
      > **⛔ IMPORTANT**: Always go through one of these three paths. Having `azure.yaml` does NOT mean you skip to validate — the user may want to modify or extend the app.
      
      | Mode | When to Use |
      |------|-------------|
      | **NEW** | Empty workspace, or user wants to create a new app |
      | **MODIFY** | Existing Azure app, user wants to add features/components |
      | **MODERNIZE** | Existing non-Azure app, user wants to migrate to Azure |
      
      ## Decision Tree
      
      ```
      What does the user want to do?
      │
      ├── Create new application → Mode: NEW
      │
      ├── Add/change features to existing app
      │   ├── Has azure.yaml/infra? → Mode: MODIFY
      │   └── No Azure config? → Mode: MODERNIZE (add Azure support first)
      │
      └── Migrate/modernize for Azure
          ├── Cross-cloud migration (AWS/GCP/Lambda)? → **Invoke azure-cloud-migrate skill** (do NOT continue in azure-prepare)
          └── On-prem or generic modernization → Mode: MODERNIZE
      ```
      
      ## Mode: NEW
      
      Creating a new Azure application from scratch.
      
      **Actions:**
      1. Confirm project type with user
      2. Gather requirements → [requirements.md](requirements.md)
      3. Select technology stack
      4. Update plan
      
      ## Mode: MODIFY
      
      Adding components/services to an existing Azure application.
      
      **Actions:**
      1. Scan existing codebase → [scan.md](scan.md)
      2. Identify existing Azure configuration
      3. Gather requirements for new components
      4. Update plan
      
      ## Mode: MODERNIZE
      
      Converting an existing application to run on Azure.
      
      **Actions:**
      1. Full codebase scan → [scan.md](scan.md)
      2. Analyze existing infrastructure (Docker, CI/CD, etc.)
      3. Gather requirements → [requirements.md](requirements.md)
      4. Map existing components to Azure services
      5. Update plan
      
      ## Detection Signals
      
      | Signal | Indicates |
      |--------|-----------|
      | `azure.yaml` exists | AZD project (MODIFY mode likely) |
      | `infra/*.bicep` exists | Bicep IaC |
      | `infra/*.tf` exists | Terraform IaC |
      | `Dockerfile` exists | Containerized app |
      | No Azure files | NEW or MODERNIZE mode |
      
      ---
      
      ## ⛔ MANDATORY for Azure Functions: Load Composition Rules BEFORE Execution
      
      **If the target compute is Azure Functions**, you MUST load the composition algorithm before generating ANY infrastructure:
      
      1. Load `services/functions/templates/selection.md` — decision tree for base template + recipe
      2. Load `services/functions/templates/recipes/composition.md` — the exact algorithm to follow
      3. Use `functions_template_get` MCP tool to list and fetch templates and write `functionFiles[]` + `projectFiles[]` directly — NEVER hand-write Bicep/Terraform and use `azd init -t <template>`/`func init`/`func new` as fallback when composing multiple recipes and required templates are not found
      
      > ⚠️ **Critical**: The Functions `bicep.md` and `terraform.md` files are **REFERENCE DOCUMENTATION**, not templates to copy. Hand-writing infrastructure from these patterns results in missing RBAC, incorrect managed identity configuration, and security vulnerabilities.
      
      For other compute targets (Container Apps, App Service, Static Web Apps), load their respective README files in `services/` for guidance.
      
    • apim.md 5.7 KB
      # APIM Deployment Guide
      
      Deploy Azure API Management (APIM) as part of your Azure infrastructure.
      
      > **For AI Gateway configuration** (policies, backends, semantic caching), use the **azure-aigateway** skill after deployment.
      
      ---
      
      ## When to Deploy APIM
      
      | Scenario | APIM Tier | Notes |
      |----------|-----------|-------|
      | AI Gateway for model governance | Standard v2 or Premium v2 | Semantic caching requires v2 SKUs |
      | API consolidation | Standard v2 | Single entry point for microservices |
      | MCP tool hosting | Standard v2 | Rate limiting and auth for AI tools |
      | Development / Testing | Developer | Not for production |
      | High-volume production | Premium v2 | Multi-region, higher limits |
      
      ---
      
      ## Quick Deploy (Azure CLI)
      
      ### 1. Create APIM Instance
      
      ```bash
      az apim create \
        --name <apim-name> \
        --resource-group <rg> \
        --location <location> \
        --publisher-name "<your-org>" \
        --publisher-email "<admin@org.com>" \
        --sku-name "StandardV2" \
        --sku-capacity 1
      
      # ⚠ APIM provisioning takes 30-45 minutes for Standard/Premium tiers
      ```
      
      ### 2. Enable Managed Identity
      
      ```bash
      az apim update --name <apim-name> --resource-group <rg> \
        --set identity.type=SystemAssigned
      ```
      
      ### 3. Get Gateway URL
      
      ```bash
      az apim show --name <apim-name> --resource-group <rg> \
        --query "gatewayUrl" -o tsv
      ```
      
      ---
      
      ## Bicep Template
      
      ```bicep
      @description('Name of the API Management instance')
      param apimName string
      
      @description('Location for the APIM instance')
      param location string = resourceGroup().location
      
      @description('Publisher organization name')
      param publisherName string
      
      @description('Publisher email address')
      param publisherEmail string
      
      @description('SKU name (StandardV2 recommended for AI Gateway)')
      @allowed(['Developer', 'StandardV2', 'PremiumV2'])
      param skuName string = 'StandardV2'
      
      @description('Number of scale units')
      param skuCapacity int = 1
      
      resource apim 'Microsoft.ApiManagement/service@2023-09-01-preview' = {
        name: apimName
        location: location
        sku: {
          name: skuName
          capacity: skuCapacity
        }
        identity: {
          type: 'SystemAssigned'
        }
        properties: {
          publisherName: publisherName
          publisherEmail: publisherEmail
        }
      }
      
      output apimId string = apim.id
      output gatewayUrl string = apim.properties.gatewayUrl
      output principalId string = apim.identity.principalId
      ```
      
      ### With Azure OpenAI Backend (AI Gateway Pattern)
      
      ```bicep
      @description('Name of the Azure OpenAI resource')
      param aoaiName string
      
      @description('Resource group of the Azure OpenAI resource')
      param aoaiResourceGroup string = resourceGroup().name
      
      // Reference existing Azure OpenAI
      resource aoai 'Microsoft.CognitiveServices/accounts@2024-04-01-preview' existing = {
        name: aoaiName
        scope: resourceGroup(aoaiResourceGroup)
      }
      
      // APIM Backend pointing to Azure OpenAI
      resource openaiBackend 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
        parent: apim
        name: 'openai-backend'
        properties: {
          protocol: 'http'
          url: '${aoai.properties.endpoint}openai'
          tls: {
            validateCertificateChain: true
            validateCertificateName: true
          }
        }
      }
      
      // Grant APIM access to Azure OpenAI
      resource cognitiveServicesUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
        name: guid(apim.id, aoai.id, 'Cognitive Services User')
        scope: aoai
        properties: {
          roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')
          principalId: apim.identity.principalId
          principalType: 'ServicePrincipal'
        }
      }
      ```
      
      ---
      
      ## Terraform Module
      
      ```hcl
      resource "azurerm_api_management" "apim" {
        name                = var.apim_name
        location            = var.location
        resource_group_name = var.resource_group_name
        publisher_name      = var.publisher_name
        publisher_email     = var.publisher_email
        sku_name            = "${var.sku_name}_${var.sku_capacity}"
      
        identity {
          type = "SystemAssigned"
        }
      }
      
      output "gateway_url" {
        value = azurerm_api_management.apim.gateway_url
      }
      
      output "principal_id" {
        value = azurerm_api_management.apim.identity[0].principal_id
      }
      ```
      
      ---
      
      ## Post-Deployment Steps
      
      After APIM is deployed:
      
      1. **Configure AI backends** → Use **azure-aigateway** skill
      2. **Import APIs** → `az apim api import` or portal
      3. **Apply policies** → Invoke **azure-aigateway** skill for AI governance policies
      4. **Enable monitoring** → Connect Application Insights
      5. **Secure endpoints** → Configure subscriptions and RBAC
      
      ---
      
      ## SKU Selection Guide
      
      | Feature | Developer | Standard v2 | Premium v2 |
      |---------|-----------|-------------|------------|
      | GenAI policies | ✅ | ✅ | ✅ |
      | Semantic caching | ❌ | ✅ | ✅ |
      | VNet integration | ❌ | ✅ | ✅ |
      | Multi-region | ❌ | ❌ | ✅ |
      | SLA | None | 99.95% | 99.99% |
      | Scale units | 1 | 1-10 | 1-12 per region |
      | Provisioning time | ~30 min | ~30 min | ~45 min |
      
      > **Recommendation**: Use **Standard v2** for most AI Gateway scenarios. Use **Premium v2** only for multi-region or high-compliance requirements.
      
      ---
      
      ## Naming Conventions
      
      | Resource | Pattern | Example |
      |----------|---------|---------|
      | APIM Instance | `apim-<app>-<env>` | `apim-myapp-prod` |
      | API | `<api-name>-api` | `openai-api` |
      | Backend | `<service>-backend` | `openai-backend` |
      | Product | `<tier>-product` | `premium-product` |
      | Subscription | `<consumer>-sub` | `frontend-sub` |
      
      ---
      
      ## References
      
      - [APIM v2 Overview](https://learn.microsoft.com/azure/api-management/v2-service-tiers-overview)
      - [APIM Bicep Reference](https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service)
      - [APIM Terraform](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management)
      - [GenAI Gateway Capabilities](https://learn.microsoft.com/azure/api-management/genai-gateway-capabilities)
      
    • architecture.md 6.4 KB
      # Architecture Planning
      
      Select hosting stack and map components to Azure services.
      
      ## Stack Selection
      
      | Stack           | Best For                                               | Azure Services                    |
      | --------------- | ------------------------------------------------------ | --------------------------------- |
      | **Containers**  | Docker experience, complex dependencies, microservices | Container Apps, AKS, ACR          |
      | **Serverless**  | Event-driven, variable traffic, cost optimization      | Functions, Logic Apps, Event Grid |
      | **App Service** | Traditional web apps, PaaS preference                  | App Service, Static Web Apps      |
      
      ### Decision Factors
      
      | Factor                   | Containers |          Serverless          | App Service |
      | ------------------------ | :--------: | :--------------------------: | :---------: |
      | Docker experience        |     ✓✓     |                              |             |
      | Event-driven             |     ✓      |              ✓✓              |             |
      | Variable traffic         |            |              ✓✓              |      ✓      |
      | Complex dependencies     |     ✓✓     |                              |      ✓      |
      | Long-running processes   |     ✓✓     |    ✓ (Durable Functions)     |      ✓      |
      | Workflow / orchestration |            | ✓✓ (Durable Functions + DTS) |             |
      | Minimal ops overhead     |            |              ✓✓              |      ✓      |
      
      ### Container Hosting: Container Apps vs AKS
      
      | Factor                    |       Container Apps        |                 AKS                 |
      | ------------------------- | :-------------------------: | :---------------------------------: |
      | **Scale to zero**         |             ✓✓              |                                     |
      | **Kubernetes API access** |                             |                 ✓✓                  |
      | **Custom operators/CRDs** |                             |                 ✓✓                  |
      | **Service mesh**          |       Dapr (built-in)       |                Istio                |
      | **Networking/dataplane**  |  Managed platform defaults  |     Azure CNI powered by Cilium     |
      | **GPU workloads**         |                             |                 ✓✓                  |
      | **Best for**              | Microservices, event-driven | Full K8s control, complex workloads |
      
      #### When to Use Container Apps
      
      - Microservices without Kubernetes complexity
      - Event-driven workloads (KEDA built-in)
      - Need scale-to-zero for cost optimization
      - Teams without Kubernetes expertise
      
      #### When to Use AKS
      
      - Need Kubernetes API/kubectl access
      - Require custom operators or CRDs
      - Service mesh requirements (Istio, Linkerd)
      - GPU/ML workloads
      - Complex networking or multi-tenant architectures
      
      > **AKS Planning:** For AKS SKU selection (Automatic vs Standard), networking, identity, scaling, and security configuration, invoke the **azure-kubernetes** skill.
      
      ## Service Mapping
      
      ### Hosting
      
      | Component Type           | Primary Service   | Alternatives                                     |
      | ------------------------ | ----------------- | ------------------------------------------------ |
      | SPA Frontend             | Static Web Apps   | Blob + CDN                                       |
      | SSR Web App              | Container Apps    | App Service, AKS                                 |
      | REST/GraphQL API         | Container Apps    | App Service, Functions, AKS                      |
      | Background Worker        | Container Apps    | Functions, AKS                                   |
      | Scheduled Task           | Functions (Timer) | Container Apps Jobs, Kubernetes CronJob (on AKS) |
      | Event Processor          | Functions         | Container Apps, AKS + KEDA                       |
      | Microservices (full K8s) | AKS               | Container Apps                                   |
      | GPU/ML Workloads         | AKS               | Azure ML                                         |
      
      ### Data
      
      | Need       | Primary      | Reference                                       | Alternatives      |
      | ---------- | ------------ | ----------------------------------------------- | ----------------- |
      | Relational | Azure SQL    | [SQL Database](services/sql-database/README.md) | PostgreSQL, MySQL |
      | Document   | Cosmos DB    | [Cosmos DB](services/cosmos-db/README.md)       | MongoDB           |
      | Cache      | Redis Cache  |                                                 |                   |
      | Files      | Blob Storage | [Storage](services/storage/README.md)           | Files Storage     |
      | Search     | AI Search    |                                                 |                   |
      
      ### Integration
      
      | Need          | Service     |
      | ------------- | ----------- |
      | Message Queue | Service Bus |
      | Pub/Sub       | Event Grid  |
      | Streaming     | Event Hubs  |
      
      ### Workflow & Orchestration
      
      | Need                                | Service                                        | Notes                                                                                                                                                       |
      | ----------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
      | Multi-step workflow / orchestration | **Durable Functions + Durable Task Scheduler** | DTS is the **required** managed backend for Durable Functions. Do NOT use Azure Storage or MSSQL backends. See [durable.md](services/functions/durable.md). |
      | Low-code / visual workflow          | Logic Apps                                     | For integration-heavy, low-code scenarios                                                                                                                   |
      
      ### Supporting (Always Include)
      
      | Service              | Purpose                 |
      | -------------------- | ----------------------- |
      | Log Analytics        | Centralized logging     |
      | Application Insights | Monitoring, APM         |
      | Key Vault            | Secrets management      |
      | Managed Identity     | Service-to-service auth |
      
      ---
      
      ## Document Architecture
      
      Record selections in `.azure/deployment-plan.md` with rationale for each choice.
      
    • aspire.md 18.2 KB
      # .NET Aspire Projects
      
      > ⛔ **CRITICAL - READ THIS FIRST**
      >
      > For .NET Aspire projects, **NEVER manually create azure.yaml or infra/ files.**
      > Always use `azd init --from-code` which auto-detects the AppHost and generates everything correctly.
      >
      > **Failure to follow this causes:** "Could not find a part of the path 'infra\main.bicep'" error.
      
      Guidance for preparing .NET Aspire applications for Azure deployment.
      
      **📖 For detailed AZD workflow:** See [recipes/azd/aspire.md](recipes/azd/aspire.md)
      
      ## What is .NET Aspire?
      
      .NET Aspire is an opinionated, cloud-ready stack for building observable, production-ready distributed applications. Aspire projects use an AppHost orchestrator to define and configure the application's components, services, and dependencies.
      
      ## Detection
      
      A .NET Aspire project is identified by:
      
      | Indicator | Description |
      |-----------|-------------|
      | `*.AppHost.csproj` | AppHost orchestrator project file |
      | `Aspire.Hosting` package | Core Aspire hosting package reference |
      | `Aspire.Hosting.AppHost` | Alternative Aspire hosting package |
      
      **Example project structure:**
      ```
      orleans-voting/
      ├── OrleansVoting.sln
      ├── OrleansVoting.AppHost/
      │   └── OrleansVoting.AppHost.csproj   ← AppHost indicator
      ├── OrleansVoting.Web/
      ├── OrleansVoting.Api/
      └── OrleansVoting.Grains/
      ```
      
      ## Azure Preparation Workflow
      
      ### Step 1: Detection
      
      When scanning the codebase (per [scan.md](scan.md)), detect Aspire by:
      
      ```bash
      # Check for AppHost project
      find . -name "*.AppHost.csproj"
      
      # Or check for Aspire.Hosting package reference
      grep -r "Aspire.Hosting" . --include="*.csproj"
      ```
      
      ### ⛔ Step 1a: Pre-Check for Custom/Non-Deployable Resources (MANDATORY)
      
      **Before running `azd init --from-code`, scan the AppHost source code to understand whether the app may contain local-only custom resources.**
      
      ```bash
      # Find the AppHost project and scan only its source directory
      APPHOST_PROJECT=$(find . -name "*.AppHost.csproj" | head -1)
      APPHOST_DIR=$(dirname "$APPHOST_PROJECT")
      grep -r "ExcludeFromManifest" "$APPHOST_DIR" --include="*.cs" | head -20
      ```
      
      **PowerShell:**
      ```powershell
      # Find the AppHost project and scan only its source directory
      $appHostProject = Get-ChildItem -Recurse -Filter "*.AppHost.csproj" | Select-Object -First 1
      $appHostDir = $appHostProject.DirectoryName
      Get-ChildItem -Path $appHostDir -Recurse -Filter "*.cs" | Select-String "ExcludeFromManifest" | Select-Object -First 20
      ```
      
      This scan is informational. `.ExcludeFromManifest()` can appear alongside deployable resources, so a positive match does **not** immediately block deployment. What matters is the final `azure.yaml` output after `azd init --from-code` completes:
      
      - If `azd init` **fails** with `unsupported resource type` → see Step 2 error guidance below.
      - If `azd init` **succeeds** but `azure.yaml` has an empty or missing `services` section → see Step 4a below.
      
      > 💡 **Why scan early:** Knowing that `.ExcludeFromManifest()` is present gives useful context when azd errors or generates an empty manifest — it confirms the app intentionally targets local development rather than Azure deployment.
      
      ### Step 2: Initialize with azd
      
      **CRITICAL: For Aspire projects, use `azd init --from-code -e <environment-name>` instead of creating azure.yaml manually.**
      
      **⚠️ ALWAYS include the `-e <environment-name>` flag:** Without it, `azd init` will fail in non-interactive environments (agents, CI/CD) with the error: `no default response for prompt 'Enter a unique environment name:'`
      
      The `--from-code` flag:
      - Auto-detects the AppHost orchestrator
      - Reads the Aspire service definitions
      - Generates appropriate `azure.yaml` and infrastructure
      - Works in non-interactive/CI environments when combined with `-e` flag
      
      ```bash
      # Non-interactive initialization for Aspire projects (REQUIRED for agents)
      ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
      azd init --from-code -e "$ENV_NAME"
      ```
      
      **Why both flags are required:**
      - `--from-code`: Tells azd to detect the AppHost automatically (no "How do you want to initialize?" prompt)
      - `-e <name>`: Provides environment name upfront (no "Enter environment name:" prompt)
      - Together, they enable fully non-interactive operation essential for automation, agents, and CI/CD pipelines
      
      **⛔ If `azd init --from-code` fails with "unsupported resource type":**
      
      This error means the AppHost contains custom Aspire resource types that azd cannot process for Azure deployment:
      
      1. ⛔ **Do NOT attempt to fix this error by modifying source code** — do not add `.ExcludeFromManifest()` calls or otherwise patch the AppHost
      2. ⛔ **Do NOT proceed with deployment** — the application is designed for local development only
      3. ✅ Record a blocker: "AppHost contains custom Aspire resource types (`unsupported resource type`) that cannot be deployed to Azure"
      4. ✅ Inform the user: this application uses custom Aspire resource authoring patterns intended for local tooling, not cloud deployment
      
      > ⚠️ **Why modifying source code is forbidden:** Adding `.ExcludeFromManifest()` may suppress the error and allow `azd init` to succeed, but the deployment outcome will not reflect the application's actual intent. The custom resources are deliberately designed to be local-only.
      
      ### Step 3: Configure Subscription and Location
      
      > **⛔ CRITICAL**: After `azd init --from-code` completes, you **MUST** immediately set the user-confirmed subscription and location.
      >
      > **DO NOT** skip this step or delay it until validation. The `azd init` command creates an environment but does NOT inherit the Azure CLI's subscription. If you skip this step, azd will use its own default subscription, which may differ from the user's confirmed choice.
      
      **Set the subscription and location immediately after initialization:**
      
      ```bash
      # Set the user-confirmed subscription ID
      azd env set AZURE_SUBSCRIPTION_ID <subscription-id>
      
      # Set the location
      azd env set AZURE_LOCATION <location>
      ```
      
      **Verify the configuration:**
      
      ```bash
      azd env get-values
      ```
      
      Confirm that `AZURE_SUBSCRIPTION_ID` and `AZURE_LOCATION` match the user's confirmed choices from [Azure Context](azure-context.md).
      
      ### Step 4: What azd Generates
      
      `azd init --from-code` creates:
      
      | Artifact | Location | Description |
      |----------|----------|-------------|
      | `azure.yaml` | Project root | Service definitions from AppHost |
      | `infra/` | Project root | Bicep templates for Azure resources |
      | `.azure/` | Project root | Environment configuration |
      
      ### ⛔ Step 4a: Validate Generated Output
      
      **MANDATORY: After `azd init --from-code` completes, verify the generated `azure.yaml` contains deployable services.**
      
      ```bash
      # Check if azure.yaml has a non-empty services section
      cat azure.yaml
      ```
      
      **If the `services` section is empty or missing:** The AppHost has no deployable resources. This happens when all resources use `.ExcludeFromManifest()` (e.g., custom resource demonstrations, local-only tooling). In this case:
      
      1. ⛔ **Do NOT proceed with deployment** — there is nothing to deploy
      2. ✅ Keep the plan status in a valid state (for example, leave it as **Planning**) and record a blocker in the plan body with the reason: "Application contains only custom/demo Aspire resources with no Azure-deployable services"
      3. ✅ Inform the user that this application is designed for local development and cannot be meaningfully deployed to Azure
      4. ⛔ Do NOT manually create Bicep, Dockerfiles, or azure.yaml to work around this — the absence of services is the correct result
      
      **Example generated azure.yaml:**
      ```yaml
      name: orleans-voting
      # metadata section is auto-generated by azd init --from-code
      
      services:
        web:
          project: ./OrleansVoting.Web
          language: dotnet
          host: containerapp
      
        api:
          project: ./OrleansVoting.Api
          language: dotnet
          host: containerapp
      ```
      
      ### ⛔ Step 4b: Fix Azure Functions Secret Storage (MANDATORY for Aspire + Functions)
      
      **MANDATORY: After `azd init --from-code` succeeds, check if the AppHost contains Azure Functions and fix secret storage configuration.**
      
      This step **MUST** run BEFORE `azd up` or `azd provision`. Skipping it causes a runtime failure: `Secret initialization from Blob storage failed`.
      
      **1. Detect Azure Functions in the AppHost:**
      
      ```bash
      APPHOST_DIR=$(dirname "$(find . -name '*.AppHost.csproj' | head -1)")
      grep -n "AddAzureFunctionsProject" "$APPHOST_DIR"/*.cs
      ```
      
      **PowerShell:**
      ```powershell
      $appHostDir = (Get-ChildItem -Recurse -Filter "*.AppHost.csproj" | Select-Object -First 1).DirectoryName
      Get-ChildItem -Path $appHostDir -Filter "*.cs" | Select-String "AddAzureFunctionsProject"
      ```
      
      **If `AddAzureFunctionsProject` is NOT found → skip this step.**
      
      **2. Check if `AzureWebJobsSecretStorageType` is already configured:**
      
      ```bash
      grep -n "AzureWebJobsSecretStorageType" "$APPHOST_DIR"/*.cs
      ```
      
      **PowerShell:**
      ```powershell
      Get-ChildItem -Path $appHostDir -Filter "*.cs" | Select-String "AzureWebJobsSecretStorageType"
      ```
      
      **If already present → skip this step.**
      
      **3. Add the environment variable to the Functions builder chain:**
      
      Use the `edit` tool to add `.WithEnvironment("AzureWebJobsSecretStorageType", "Files")` to the `AddAzureFunctionsProject` builder chain in the AppHost source file.
      
      **Before:**
      ```csharp
      var functions = builder.AddAzureFunctionsProject<Projects.MyFunctions>("functions")
          .WithHostStorage(storage)
          .WithReference(queues);
      ```
      
      **After:**
      ```csharp
      var functions = builder.AddAzureFunctionsProject<Projects.MyFunctions>("functions")
          .WithHostStorage(storage)
          .WithEnvironment("AzureWebJobsSecretStorageType", "Files")
          .WithReference(queues);
      ```
      
      > 💡 **Tip:** Place `.WithEnvironment(...)` immediately after `.WithHostStorage(...)` for clarity.
      
      > ⚠️ **Why this is required:** When Aspire uses `WithHostStorage(storage)`, it configures identity-based storage URIs (e.g., `AzureWebJobsStorage__blobServiceUri`). Azure Functions' secret/key manager does **not** support these identity-based URIs — it requires either a connection string or file-based storage. Setting `AzureWebJobsSecretStorageType=Files` switches to file-system key storage, bypassing the incompatible blob dependency.
      
      See [aspire-functions-secrets reference](services/functions/aspire-containerapps.md) for additional details.
      
      ## Flags Reference
      
      ### azd init for Aspire
      
      | Flag | Required | Description |
      |------|----------|-------------|
      | `--from-code` | ✅ Yes | Auto-detect AppHost, no interactive prompts |
      | `-e <name>` | ✅ Yes | Environment name (required for non-interactive) |
      | `--no-prompt` | Optional | Skip additional confirmations |
      
      **Complete initialization sequence:**
      ```bash
      # 1. Initialize the environment
      ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
      azd init --from-code -e "$ENV_NAME"
      
      # 2. IMMEDIATELY set the user-confirmed subscription
      azd env set AZURE_SUBSCRIPTION_ID <subscription-id>
      
      # 3. Set the location
      azd env set AZURE_LOCATION <location>
      
      # 4. Verify configuration
      azd env get-values
      ```
      
      ## Common Aspire Samples
      
      | Sample | Repository | Notes |
      |--------|------------|-------|
      | orleans-voting | [dotnet/aspire-samples](https://github.com/dotnet/aspire-samples/tree/main/samples/orleans-voting) | Orleans cluster with voting app |
      | AspireYarp | [dotnet/aspire-samples](https://github.com/dotnet/aspire-samples/tree/main/samples/AspireYarp) | YARP reverse proxy |
      | AspireWithDapr | [dotnet/aspire-samples](https://github.com/dotnet/aspire-samples/tree/main/samples/AspireWithDapr) | Dapr integration |
      | eShop | [dotnet/eShop](https://github.com/dotnet/eShop) | Reference microservices app |
      
      ## Troubleshooting
      
      ### Error: "no default response for prompt 'Enter a unique environment name:'"
      
      **Cause:** Missing `-e` flag when running `azd init --from-code` in non-interactive environment  
      **Solution:** Always include the `-e <environment-name>` flag
      
      ```bash
      # ❌ Wrong - fails in non-interactive environments (agents, CI/CD)
      azd init --from-code
      
      # ✅ Correct - provides environment name upfront
      ENV_NAME="$(basename "$PWD" | tr '[:upper:]' '[:lower:]' | tr ' _' '-')-dev"
      azd init --from-code -e "$ENV_NAME"
      ```
      
      **Important:** This error typically occurs when:
      - Running in an agent or automation context
      - No TTY is available for interactive prompts
      - The `-e` flag was omitted
      
      ### Error: "no default response for prompt 'How do you want to initialize your app?'"
      
      **Cause:** Missing `--from-code` flag  
      **Solution:** Add `--from-code` to the `azd init` command
      
      ```bash
      # ❌ Wrong - requires interactive prompt
      azd init -e "my-env"
      
      # ✅ Correct - auto-detects AppHost
      azd init --from-code -e "my-env"
      ```
      
      ### No AppHost detected
      
      **Symptoms:** `azd init --from-code` doesn't find the AppHost
      
      **Solutions:**
      1. Verify AppHost project exists: `find . -name "*.AppHost.csproj"`
      2. Check project builds: `dotnet build`
      3. Ensure Aspire.Hosting package is referenced in AppHost project
      
      ### Error: "unsupported resource type" during manifest generation
      
      **Symptoms:** `azd init --from-code` fails with output like:
      ```
      error: unsupported resource type: <custom-resource-type>
      ```
      or the manifest generation step errors on child resources (e.g., ClockHand, or other custom resource types defined in the AppHost).
      
      **Cause:** The AppHost contains custom Aspire resource types that azd cannot convert to Azure deployable resources. These custom types are typically:
      - Demonstration resources showing developers how to build Aspire extensions for local tooling
      - Resources that wrap local services without Azure equivalents
      - Custom child resources (e.g., subcomponents of a custom Aspire integration)
      
      **Resolution:**
      
      1. ⛔ **Do NOT attempt to fix this error by modifying source code** — do not add `.ExcludeFromManifest()` calls or otherwise patch the AppHost
      2. ⛔ **Do NOT proceed with deployment** — this is a deployment blocker, not a recoverable error
      3. ✅ Record a blocker in the deployment plan: "AppHost contains custom Aspire resource types not supported for Azure deployment (unsupported resource type)"
      4. ✅ Inform the user that this application is designed for local development and cannot be meaningfully deployed to Azure
      
      > ⚠️ **Why this is a hard stop:** Custom resource types that produce "unsupported resource type" errors are intentionally not deployable. Adding `.ExcludeFromManifest()` to suppress the error may allow `azd init` to succeed, but the resulting deployment would not represent the application's actual functionality.
      
      ### Azure Functions: Secret initialization from Blob storage failed
      
      **Symptoms:** Azure Functions app fails at startup with error:
      ```
      System.InvalidOperationException: Secret initialization from Blob storage failed due to missing both
      an Azure Storage connection string and a SAS connection uri.
      ```
      
      **Cause:** When using `AddAzureFunctionsProject` with `WithHostStorage(storage)`, Aspire configures identity-based storage access (managed identity). However, Azure Functions' internal secret management does not support identity-based URIs and requires file-based secret storage for Container Apps deployments.
      
      **Solution:** Add `AzureWebJobsSecretStorageType=Files` environment variable to the Functions resource in the AppHost **before running `azd up`**:
      
      ```csharp
      var functions = builder.AddAzureFunctionsProject<Projects.ImageGallery_Functions>("functions")
                             .WithReference(queues)
                             .WithReference(blobs)
                             .WaitFor(storage)
                             .WithRoleAssignments(storage, ...)
                             .WithHostStorage(storage)
                             .WithEnvironment("AzureWebJobsSecretStorageType", "Files")  // Required for Container Apps
                             .WithUrlForEndpoint("http", u => u.DisplayText = "Functions App");
      ```
      
      > 💡 **Why this is required:**
      > - `WithHostStorage(storage)` sets identity-based URIs like `AzureWebJobsStorage__blobServiceUri`
      > - This is correct and secure for runtime storage operations
      > - However, Functions' secret/key management doesn't support these URIs
      > - File-based secrets are mandatory for Container Apps deployments
      
      > ⚠️ **Important:** This is required when:
      > - Using `AddAzureFunctionsProject` in Aspire
      > - Using `WithHostStorage()` with identity-based storage
      > - Deploying to Azure Container Apps (the default for Aspire Functions)
      
      **Generated Infrastructure Note:**
      
      If you need to modify the generated Container Apps infrastructure directly, ensure the Functions container app has this environment variable:
      
      ```bicep
      resource functionsContainerApp 'Microsoft.App/containerApps@2024-03-01' = {
        properties: {
          template: {
            containers: [
              {
                env: [
                  {
                    name: 'AzureWebJobsSecretStorageType'
                    value: 'Files'
                  }
                  // ... other environment variables
                ]
              }
            ]
          }
        }
      }
      ```
      
      ### Error: azd uses wrong subscription despite user confirmation
      
      **Symptoms:** `azd provision --preview` shows a different subscription than the one the user confirmed
      
      **Cause:** The `AZURE_SUBSCRIPTION_ID` was not set immediately after `azd init --from-code`. The Azure CLI and azd can have different default subscriptions.
      
      **Solution:** Always set the subscription immediately after initialization:
      
      ```bash
      # After azd init --from-code completes:
      azd env set AZURE_SUBSCRIPTION_ID <user-confirmed-subscription-id>
      azd env set AZURE_LOCATION <location>
      
      # Verify before proceeding:
      azd env get-values
      ```
      
      **Prevention:** Follow the complete initialization sequence in the [Flags Reference](#azd-init-for-aspire) section above.
      
      ## References
      
      - [.NET Aspire Documentation](https://learn.microsoft.com/en-us/dotnet/aspire/)
      - [Azure Developer CLI (azd)](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/)
      - [Aspire Samples Repository](https://github.com/dotnet/aspire-samples)
      - [azd + Aspire Integration](https://learn.microsoft.com/en-us/dotnet/aspire/deployment/azure/aca-deployment-azd-in-depth)
      
      ## Next Steps
      
      After `azd init --from-code`:
      1. Review generated `azure.yaml` and `infra/` files (if present)
      2. Set AZURE_SUBSCRIPTION_ID and AZURE_LOCATION with `azd env set`
      3. Customize infrastructure as needed
      4. Proceed to **azure-validate** skill
      5. Deploy with **azure-deploy** skill
      
      > ⚠️ **Important for Container Apps:** If using Aspire with Container Apps, azure-validate will check and help set up required environment variables after provisioning.
      
    • auth-best-practices.md 6.1 KB
      # Azure Authentication Best Practices
      
      > Source: [Microsoft — Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) and [Azure Identity client libraries](https://learn.microsoft.com/dotnet/azure/sdk/authentication/).
      
      ## Golden Rule
      
      Use **managed identities** and **Azure RBAC** in production. Reserve `DefaultAzureCredential` for **local development only**.
      
      ## Authentication by Environment
      
      | Environment | Recommended Credential | Why |
      |---|---|---|
      | **Production (Azure-hosted)** | `ManagedIdentityCredential` (system- or user-assigned) | No secrets to manage; auto-rotated by Azure |
      | **Production (on-premises)** | `ClientCertificateCredential` or `WorkloadIdentityCredential` | Deterministic; no fallback chain overhead |
      | **CI/CD pipelines** | `AzurePipelinesCredential` / `WorkloadIdentityCredential` | Scoped to pipeline identity |
      | **Local development** | `DefaultAzureCredential` | Chains CLI, PowerShell, and VS Code credentials for convenience |
      
      ## Why Not `DefaultAzureCredential` in Production?
      
      1. **Unpredictable fallback chain** — walks through multiple credential types, adding latency and making failures harder to diagnose.
      2. **Broad surface area** — checks environment variables, CLI tokens, and other sources that should not exist in production.
      3. **Non-deterministic** — which credential actually authenticates depends on the environment, making behavior inconsistent across deployments.
      4. **Performance** — each failed credential attempt adds network round-trips before falling back to the next.
      
      ## Production Patterns
      
      ### .NET
      
      ```csharp
      using Azure.Identity;
      
      var credential = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") == "Development"
          ? new DefaultAzureCredential()                          // local dev — uses CLI/VS credentials
          : new ManagedIdentityCredential();                      // production — deterministic, no fallback chain
      // For user-assigned identity: new ManagedIdentityCredential("<client-id>")
      ```
      
      ### TypeScript / JavaScript
      
      ```typescript
      import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
      
      const credential = process.env.NODE_ENV === "development"
        ? new DefaultAzureCredential()                          // local dev — uses CLI/VS credentials
        : new ManagedIdentityCredential();                      // production — deterministic, no fallback chain
      // For user-assigned identity: new ManagedIdentityCredential("<client-id>")
      ```
      
      ### Python
      
      ```python
      import os
      from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
      
      credential = (
          DefaultAzureCredential()                              # local dev — uses CLI/VS credentials
          if os.getenv("AZURE_FUNCTIONS_ENVIRONMENT") == "Development"
          else ManagedIdentityCredential()                      # production — deterministic, no fallback chain
      )
      # For user-assigned identity: ManagedIdentityCredential(client_id="<client-id>")
      ```
      
      ### Java
      
      ```java
      import com.azure.identity.DefaultAzureCredentialBuilder;
      import com.azure.identity.ManagedIdentityCredentialBuilder;
      
      var credential = "Development".equals(System.getenv("AZURE_FUNCTIONS_ENVIRONMENT"))
          ? new DefaultAzureCredentialBuilder().build()          // local dev — uses CLI/VS credentials
          : new ManagedIdentityCredentialBuilder().build();      // production — deterministic, no fallback chain
      // For user-assigned identity: new ManagedIdentityCredentialBuilder().clientId("<client-id>").build()
      ```
      
      ## Local Development Setup
      
      `DefaultAzureCredential` is ideal for local dev because it automatically picks up credentials from developer tools:
      
      1. **Azure CLI** — `az login`
      2. **Azure Developer CLI** — `azd auth login`
      3. **Azure PowerShell** — `Connect-AzAccount`
      4. **Visual Studio / VS Code** — sign in via Azure extension
      
      ```typescript
      import { DefaultAzureCredential } from "@azure/identity";
      
      // Local development only — uses CLI/PowerShell/VS Code credentials
      const credential = new DefaultAzureCredential();
      ```
      
      ## Environment-Aware Pattern
      
      Detect the runtime environment and select the appropriate credential. The key principle: use `DefaultAzureCredential` only when running locally, and a specific credential in production.
      
      > **Tip:** Azure Functions sets `AZURE_FUNCTIONS_ENVIRONMENT` to `"Development"` when running locally. For App Service or containers, use any environment variable you control (e.g. `NODE_ENV`, `ASPNETCORE_ENVIRONMENT`).
      
      ```typescript
      import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
      
      function getCredential() {
        if (process.env.NODE_ENV === "development") {
          return new DefaultAzureCredential();          // picks up az login / VS Code creds
        }
        return process.env.AZURE_CLIENT_ID
          ? new ManagedIdentityCredential(process.env.AZURE_CLIENT_ID)  // user-assigned
          : new ManagedIdentityCredential();                            // system-assigned
      }
      ```
      
      ## Security Checklist
      
      - [ ] Use managed identity for all Azure-hosted apps
      - [ ] Never hardcode credentials, connection strings, or keys
      - [ ] Apply least-privilege RBAC roles at the narrowest scope
      - [ ] Use `ManagedIdentityCredential` (not `DefaultAzureCredential`) in production
      - [ ] Store any required secrets in Azure Key Vault
      - [ ] Rotate secrets and certificates on a schedule
      - [ ] Enable Microsoft Defender for Cloud on production resources
      
      ## Further Reading
      
      - [Passwordless connections overview](https://learn.microsoft.com/azure/developer/intro/passwordless-overview)
      - [Managed identities overview](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview)
      - [Azure RBAC overview](https://learn.microsoft.com/azure/role-based-access-control/overview)
      - [.NET authentication guide](https://learn.microsoft.com/dotnet/azure/sdk/authentication/)
      - [Python identity library](https://learn.microsoft.com/python/api/overview/azure/identity-readme)
      - [JavaScript identity library](https://learn.microsoft.com/javascript/api/overview/azure/identity-readme)
      - [Java identity library](https://learn.microsoft.com/java/api/overview/azure/identity-readme)
      
    • azure-context.md 5.9 KB
      # Azure Context (Subscription & Location)
      
      Detect and confirm Azure subscription and location before generating artifacts. Run region capacity check for customer selected location
      
      ---
      
      ## Step 1: Check for Existing AZD Environment
      
      If the project already uses AZD, check for an existing environment with values already set:
      
      ```bash
      azd env list
      ```
      
      **If an environment is selected** (marked with `*`), check its values:
      
      ```bash
      azd env get-values
      ```
      
      If `AZURE_SUBSCRIPTION_ID` and `AZURE_LOCATION` are already set, use `ask_user` to confirm reuse:
      
      ```
      Question: "I found an existing AZD environment with these settings. Would you like to continue with them?"
      
        Environment: {env-name}
        Subscription: {subscription-name} ({subscription-id})
        Location: {location}
      
      Choices: [
        "Yes, use these settings (Recommended)",
        "No, let me choose different settings"
      ]
      ```
      
      If user confirms → skip to **Record in Plan**. Otherwise → continue to Step 2.
      
      ---
      
      ## Step 2: Detect Defaults
      
      Check for user-configured defaults:
      
      ```bash
      azd config get defaults
      ```
      
      Returns JSON with any configured defaults:
      ```json
      {
        "subscription": "25fd0362-aa79-488b-b37b-d6e892009fdf",
        "location": "eastus2"
      }
      ```
      
      Use these as **recommended** values if present.
      
      If no defaults, fall back to az CLI:
      ```bash
      az account show --query "{name:name, id:id}" -o json
      ```
      
      ## Step 3: Confirm Subscription with User
      
      Use `ask_user` with the **actual subscription name and ID**:
      
      ✅ **Correct:**
      ```
      Question: "Which Azure subscription would you like to deploy to?"
      Choices: [
        "Use current: jongdevdiv (25fd0362-aa79-488b-b37b-d6e892009fdf) (Recommended)",
        "Let me specify a different subscription"
      ]
      ```
      
      ❌ **Wrong** (never do this):
      ```
      Choices: [
        "Use default subscription",  // ← Does not show actual name
        "Let me specify"
      ]
      ```
      
      If user wants a different subscription:
      ```bash
      az account list --output table
      ```
      
      ---
      
      ## Step 4: Confirm Location with User
      
      1. Consult [Region Availability](region-availability.md) for services with limited availability
      2. Present only regions that support ALL selected services
      3. Use `ask_user`:
      4. After customer selected region, do provisioning limit check, consult [Resource Limits and Quotas](resources-limits-quotas.md). For this also invoke azure-quotas
      
      ```
      Question: "Which Azure region would you like to deploy to?"
      Based on your architecture ({list services}), these regions support all services:
      Choices: [
        "eastus2 (Recommended)",
        "westus2",
        "westeurope"
      ]
      ```
      
      ⚠️ Do NOT include regions that don't support all services — deployment will fail.
      
      ---
      ## Step 5: Check Resource Provisioning Limits
      
      1. **List resource types and quantities** that will be deployed from the planned architecture (e.g., 2x Standard D4s v3 VMs, 1x VNet, 3x Storage Accounts)
      
      2. **Determine limits for each resource type** using the user-selected subscription and region:
         - Reference [./resources-limits-quotas.md](./resources-limits-quotas.md) for documented limits
         - Use **azure-quotas** skill to check current quotas and usage for the selected subscription and region
         - If `az quota list` returns `BadRequest` error, the resource provider doesn't support quota API
      
      3. **For resources that don't support quota API** (e.g., Microsoft.DocumentDB, or when you get `BadRequest` from `az quota list`):
         - Invoke **azure-resource-lookup** skill to count existing deployments of that resource type in the selected subscription and region
         - Use the count to calculate: `Total After Deployment = Current Count + Planned Deployment`
         - Reference [Azure service limits documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits) for the limit value
         - Document in provisioning checklist as "Fetched from: azure-resource-lookup + Official docs"
      
      4. **Validate deployment capacity**:
         - Compare planned deployment quantities against available quota (limit - current usage)
         - If **insufficient capacity** is found, notify the customer and return to **Step 4** to select a different region
         - Use **azure-quotas** skill to compare capacity across multiple regions and recommend alternatives
      
      ## Record in Plan
      
      After confirmation, record in `.azure/deployment-plan.md`:
      
      ```markdown
      ## Azure Context
      - **Subscription**: jongdevdiv (25fd0362-aa79-488b-b37b-d6e892009fdf)
      - **Location**: eastus2
      ```
      
      ---
      
      ## Step 6: Apply to AZD Environment
      
      > **⛔ CRITICAL for Aspire and azd projects**: After user confirms subscription and location, you **MUST** set these values in the azd environment immediately after running `azd init` or `azd env new`. Always use `--no-prompt` with these commands to prevent interactive prompts from blocking execution.
      >
      > **DO NOT** wait until validation or deployment. The Azure CLI and azd maintain separate configuration contexts.
      
      **For Aspire projects using `azd init --from-code`:**
      
      ```bash
      # 1. Run azd init
      azd init --from-code -e <environment-name> --no-prompt
      
      # 2. IMMEDIATELY set the user-confirmed subscription
      azd env set AZURE_SUBSCRIPTION_ID <subscription-id>
      
      # 3. Set the location
      azd env set AZURE_LOCATION <location>
      
      # 4. Verify
      azd env get-values
      ```
      
      **For non-Aspire projects using `azd env new`:**
      
      ```bash
      # 1. Create environment
      azd env new <environment-name> --no-prompt
      
      # 2. IMMEDIATELY set the user-confirmed subscription
      azd env set AZURE_SUBSCRIPTION_ID <subscription-id>
      
      # 3. Set the location
      azd env set AZURE_LOCATION <location>
      
      # 4. Verify
      azd env get-values
      ```
      
      **Why this is critical:**
      - `az account show` returns the Azure CLI's default subscription
      - `azd` maintains its own configuration with potentially different defaults
      - If you don't set `AZURE_SUBSCRIPTION_ID` explicitly, azd will use its own default
      - This can result in deploying to the wrong subscription despite user confirmation
      
    • functional-verification.md 3.2 KB
      # Functional Verification
      
      Verify that the application works correctly — both UI and backend — before proceeding to validation and deployment. This step prevents deploying broken or incomplete functionality to Azure.
      
      ## When to Verify
      
      After generating all artifacts (code, infrastructure, configuration) and applying security hardening — but **before** marking the plan as `Ready for Validation`.
      
      ## Verification Checklist
      
      Use `ask_user` to confirm functional testing with the user:
      
      ```
      "Before we proceed to deploy, would you like to verify the app works as expected?
      We can test both the UI and backend to catch issues before they reach Azure."
      ```
      
      ### Backend Verification
      
      | Check | How |
      |-------|-----|
      | **App starts without errors** | Run the app and confirm no startup crashes or missing dependencies |
      | **API endpoints respond** | Test core routes (e.g., `curl` health, list, create endpoints) |
      | **Data operations work** | Verify CRUD operations against storage, database, or other services |
      | **Authentication flows** | Confirm auth works (tokens, managed identity fallback, login/logout) |
      | **Error handling** | Verify error responses are meaningful (not unhandled exceptions) |
      
      ### UI Verification
      
      | Check | How |
      |-------|-----|
      | **Page loads** | Open the app in a browser and confirm the UI renders |
      | **Interactive elements work** | Test buttons, forms, file inputs, navigation links |
      | **Data displays correctly** | Verify lists, images, and dynamic content render from the backend |
      | **User workflows complete** | Walk through the core user journey end-to-end (e.g., upload → view → delete) |
      
      ## Decision Tree
      
      ```
      App artifacts generated?
      ├── Yes → Ask user: "Would you like to verify functionality?"
      │   ├── User says yes
      │   │   ├── App can run locally? → Run locally, verify backend + UI
      │   │   ├── API-only / no UI? → Test endpoints with curl or similar
      │   │   └── Static site? → Open in browser, verify rendering
      │   │   Then:
      │   │   ├── Works → Proceed to Update Plan (step 6)
      │   │   └── Issues found → Fix issues, re-test
      │   └── User says no / skip → Proceed to Update Plan (step 6)
      └── No → Go back to Generate Artifacts (step 3)
      ```
      
      ## Running Locally
      
      For apps that can run locally, help the user start the app based on the detected runtime:
      
      | Runtime | Command | Notes |
      |---------|---------|-------|
      | Node.js | `npm install && npm start` | Set `PORT=3000` if not configured |
      | Python | `pip install -r requirements.txt && python app.py` | Use virtual environment |
      | .NET | `dotnet run` | Check `launchSettings.json` for port |
      | Java | `mvn spring-boot:run` or `gradle bootRun` | Check `application.properties` |
      
      > ⚠️ **Warning:** For apps using Azure services (e.g., Blob Storage, Cosmos DB), local testing requires the user to be authenticated via `az login` with sufficient RBAC roles, or to have local emulators configured (e.g., Azurite for Storage).
      
      ## Record in Plan
      
      After functional verification, add a note to `.azure/deployment-plan.md`:
      
      ```markdown
      ## Functional Verification
      - Status: Verified / Skipped
      - Backend: Tested / Not applicable
      - UI: Tested / Not applicable
      - Notes: <any issues found and resolved>
      ```
      
    • generate.md 5.4 KB
      # Artifact Generation
      
      Generate infrastructure and configuration files based on selected recipe.
      
      ## ⛔ CRITICAL: Check for .NET Aspire Projects FIRST
      
      **MANDATORY: Before generating any files, detect .NET Aspire projects:**
      
      ```bash
      # Method 1: Find AppHost project files
      find . -name "*.AppHost.csproj" -o -name "*AppHost.csproj"
      
      # Method 2: Search for Aspire packages
      grep -r "Aspire\.Hosting\|Aspire\.AppHost\.Sdk" . --include="*.csproj"
      ```
      
      **If Aspire is detected:**
      1. ⛔ **STOP** - Do NOT manually create `azure.yaml`
      2. ⛔ **STOP** - Do NOT manually create `infra/` files
      3. ✅ **USE** - `azd init --from-code -e <env-name>` instead
      4. 📖 **READ** - [aspire.md](aspire.md) and [recipes/azd/aspire.md](recipes/azd/aspire.md) for complete guidance
      
      **Why this is critical:**
      - Aspire AppHost auto-generates infrastructure from code
      - Manual `azure.yaml` without `services` section causes "infra\main.bicep not found" error
      - `azd init --from-code` correctly detects AppHost and generates proper configuration
      
      > ⚠️ **Manually creating azure.yaml for Aspire projects is the most common deployment failure.** Always use `azd init --from-code`.
      
      ## Check for Other Special Patterns
      
      After verifying the project is NOT Aspire, check for these patterns:
      
      | Pattern | Detection | Action |
      |---------|-----------|--------|
      | **Complex existing codebase** | Multiple services, existing structure | Consider `azd init --from-code` |
      | **Existing azure.yaml** | File already present | MODIFY mode - update existing config |
      
      > **CRITICAL:** After running `azd init --from-code`, you **MUST** immediately set the user-confirmed subscription with `azd env set AZURE_SUBSCRIPTION_ID <id>`. Do NOT skip this step. See [aspire.md](aspire.md) Step 3 for the complete sequence.
      
      ## CRITICAL: Research Must Be Complete
      
      **DO NOT generate any files without first completing the [Research Components](research.md) step.**
      
      The research step loads service-specific references and invokes related skills to gather best practices. Apply all research findings to generated artifacts.
      
      ## Research Checklist
      
      1. ✅ Completed [Research Components](research.md) step
      2. ✅ Loaded all relevant `services/*.md` references
      3. ✅ Invoked related skills for specialized guidance
      4. ✅ Documented findings in `.azure/deployment-plan.md`
      
      ## Generation Order
      
      | Order | Artifact | Notes |
      |-------|----------|-------|
      | 1 | Application config (azure.yaml) | AZD only—defines services and hosting |
      | 2 | Application code scaffolding | Entry points, health endpoints, config |
      | 3 | Dockerfiles | If containerized |
      | 4 | Infrastructure (Bicep/Terraform) | IaC templates in `./infra/` |
      | 5 | CI/CD pipelines | If requested |
      
      ## Recipe-Specific Generation
      
      Load the appropriate recipe for detailed generation steps:
      
      | Recipe | Guide |
      |--------|-------|
      | AZD | [AZD Recipe](recipes/azd/README.md) |
      | AZCLI | [AZCLI Recipe](recipes/azcli/README.md) |
      | Bicep | [Bicep Recipe](recipes/bicep/README.md) |
      | Terraform | [Terraform Recipe](recipes/terraform/README.md) |
      
      ## Common Standards
      
      ### File Structure
      
      ```
      project-root/
      ├── .azure/
      │   └── deployment-plan.md
      ├── infra/
      │   ├── main.bicep (or main.tf)
      │   └── modules/
      ├── src/
      │   └── <component>/
      │       └── Dockerfile
      └── azure.yaml (AZD only)
      ```
      
      ### Directory Creation
      
      > ⚠️ **Warning:** The `create` tool fails with `Parent directory does not exist` when intermediate directories are missing. Always create the full directory tree before writing files.
      
      **Before creating nested files** (e.g., `src/frontend/src/App.jsx`), create all parent directories first:
      
      ```bash
      mkdir -p src/frontend/src src/api
      ```
      
      - Use **absolute paths** in `mkdir -p` when the working directory may differ from the project root
      - Create directories for **all components** in a single command before writing any files
      - Do **not** rely on the `create` tool to create parent directories — it will not
      
      ### Security Requirements
      
      - No hardcoded secrets
      - Use Key Vault for sensitive values
      - Managed Identity for service auth
      - HTTPS only, TLS 1.2+
      - SQL Server Bicep MUST use Entra-only auth — omit `administratorLogin` and `administratorLoginPassword` entirely, including from conditional/ternary branches (see [services/sql-database/bicep.md](services/sql-database/bicep.md)). These property names must not appear anywhere in a generated `.bicep` file.
      - **SQL + Managed Identity: MUST add postprovision hook** — ARM role assignments only grant control-plane access; you MUST also generate `scripts/grant-sql-access.sh` + `.ps1` and add a `postprovision` hook in `azure.yaml` to run T-SQL grants. See [services/sql-database/bicep.md](services/sql-database/bicep.md).
      - **App Service Bicep: MUST include `azd-service-name` tag** — Every App Service `Microsoft.Web/sites` resource MUST have `tags: union(tags, { 'azd-service-name': serviceName })`. Without this tag, `azd deploy` cannot locate the resource. See [services/app-service/bicep.md](services/app-service/bicep.md).
      
      ### Runtime Configuration
      
      Apply language-specific production settings for containerized apps:
      
      | Runtime | Reference |
      |---------|-----------|
      | Node.js/Express | [runtimes/nodejs.md](runtimes/nodejs.md) |
      
      ## After Generation
      
      1. Update `.azure/deployment-plan.md` with generated file list
      2. Run validation checks
      3. Proceed to **azure-validate** skill
      
    • global-rules.md 2 KB
      # Global Rules
      
      > **MANDATORY** — These rules apply to ALL skills. Violations are unacceptable.
      
      ## Rule 1: Destructive Actions Require User Confirmation
      
      ⛔ **ALWAYS use `ask_user`** before ANY destructive action.
      
      ### What is Destructive?
      
      | Category | Examples |
      |----------|----------|
      | **Delete** | `az group delete`, `azd down`, `rm -rf`, delete resource |
      | **Overwrite** | Replace existing files, overwrite config, reset settings |
      | **Irreversible** | Purge Key Vault, delete storage account, drop database |
      | **Cost Impact** | Provision expensive resources, scale up significantly |
      | **Security** | Expose secrets, change access policies, modify RBAC |
      
      ### How to Confirm
      
      ```
      ask_user(
        question: "This will permanently delete resource group 'rg-myapp'. Continue?",
        choices: ["Yes, delete it", "No, cancel"]
      )
      ```
      
      ### No Exceptions
      
      - Do NOT assume user wants to delete/overwrite
      - Do NOT proceed based on "the user asked to deploy" (deploy ≠ delete old)
      - Do NOT batch destructive actions without individual confirmation
      - ⛔ Do NOT delete user project or workspace directories (e.g., `rm -rf <project-dir>`) even when adding features, converting, or migrating — use MODIFY mode to edit existing files instead. File deletions within a project (e.g., removing build artifacts or temp files) are permitted when appropriate.
      - ⛔ `azd init -t <template>` (and any `azd init` command with a template argument) is for NEW projects only — run it **only** in an empty/new directory. If the user explicitly requests re-initialization of an existing project, create a separate new directory, run the template there, and then migrate changes into the existing project with user-confirmed edits. Never run `azd init -t` directly in a non-empty existing workspace. `azd init` without a template argument may be used in existing workspaces when appropriate.
      
      ---
      
      ## Rule 2: Never Assume Subscription or Location
      
      ⛔ **ALWAYS use `ask_user`** to confirm:
      - Azure subscription (show actual name and ID)
      - Azure region/location
      
    • plan-template.md 10.1 KB
      # Plan Template
      
      Create `.azure/deployment-plan.md` using this template. This file is **mandatory** and serves as the source of truth for the entire workflow.
      
      ## ⛔ BLOCKING REQUIREMENTS
      
      1. You **MUST** create this plan file BEFORE generating any code, infrastructure, or configuration.
      2. You **MUST** complete Step 6 Phase 2 (Provisioning Limit Checklist) with NO "_TBD_" entries remaining before presenting the plan to the user.
      3. Present the plan to the user and get approval before proceeding to execution.
      4. You **MUST NOT** skip any part of the plan.
      
      ---
      
      ## Template
      
      ```markdown
      # Azure Deployment Plan
      
      > **Status:** Planning | Approved | Executing | Ready for Validation | Validated | Deployed
      
      Generated: {timestamp}
      
      ---
      
      ## 1. Project Overview
      
      **Goal:** {what the user wants to build/deploy}
      
      **Path:** New Project | Add Components | Modernize Existing
      
      ---
      
      ## 2. Requirements
      
      | Attribute | Value |
      |-----------|-------|
      | Classification | POC / Development / Production |
      | Scale | Small / Medium / Large |
      | Budget | Cost-Optimized / Balanced / Performance |
      | **Subscription** | {subscription-name-or-id} ⚠️ MUST confirm with user |
      | **Location** | {azure-region} ⚠️ MUST confirm with user |
      
      ---
      
      ## 3. Components Detected
      
      | Component | Type | Technology | Path |
      |-----------|------|------------|------|
      | {name} | Frontend / API / Worker | {stack} | {path} |
      
      ---
      
      ## 4. Recipe Selection
      
      **Selected:** AZD / AZCLI / Bicep / Terraform
      
      **Rationale:** {why this recipe was chosen}
      
      ---
      
      ## 5. Architecture
      
      **Stack:** Containers / Serverless / App Service
      
      ### Service Mapping
      
      | Component | Azure Service | SKU |
      |-----------|---------------|-----|
      | {component} | {azure-service} | {sku} |
      
      ### Supporting Services
      
      | Service | Purpose |
      |---------|---------|
      | Log Analytics | Centralized logging |
      | Application Insights | Monitoring & APM |
      | Key Vault | Secrets management |
      | Managed Identity | Service-to-service auth |
      
      ---
      
      ## 6. Provisioning Limit Checklist
      
      **Purpose:** Validate that the selected subscription and region have sufficient quota/capacity for all resources to be deployed.
      
      > **⚠️ REQUIRED:** This is a **TWO-PHASE** process. Complete both phases before proceeding.
      
      ### Phase 1: Prepare Resource Inventory
      
      List all resources to be deployed with their types and quantities. Leave quota/limit columns empty.
      
      | Resource Type | Number to Deploy | Total After Deployment | Limit/Quota | Notes |
      |---------------|------------------|------------------------|-------------|-------|
      | {ARM-resource-type} | {count} | _To be filled in Phase 2_ | _To be filled in Phase 2_ | _To be filled in Phase 2_ |
      
      **Example format:**
      
      | Resource Type | Number to Deploy | Total After Deployment | Limit/Quota | Notes |
      |---------------|------------------|------------------------|-------------|-------|
      | Microsoft.App/managedEnvironments | 1 | _TBD_ | _TBD_ | _TBD_ |
      | Microsoft.Compute/virtualMachines (Standard_D4s_v3) | 3 | _TBD_ | _TBD_ | _TBD_ |
      | Microsoft.Network/publicIPAddresses | 2 | _TBD_ | _TBD_ | _TBD_ |
      | Microsoft.DocumentDB/databaseAccounts | 1 | _TBD_ | _TBD_ | _TBD_ |
      | Microsoft.Storage/storageAccounts | 2 | _TBD_ | _TBD_ | _TBD_ |
      
      ### Phase 2: Fetch Quotas and Validate Capacity
      
      **Action:** **MUST invoke azure-quotas skill first** to populate the remaining columns with actual quota data using Azure quota CLI. Only use fallback methods if quota CLI is not supported.
      
      > **⚠️ IMPORTANT:** Process **ONE resource type at a time**. Do NOT try to apply all steps to all resources at once. Complete steps 1-7 for the first resource, then move to the next resource, and so on.
      
      For each resource type:
      
      1. **Check if quota CLI is supported** - Run `az quota list --scope /subscriptions/{subscription-id}/providers/{ProviderNamespace}/locations/{region}` to verify the provider is supported. If you encounter issues or need help finding the correct resource name, invoke the azure-quotas skill for troubleshooting.
      2. **Get current usage and limit**:
         - **If quota CLI is supported**:
           - Get limit: `az quota show --resource-name {quota-resource-name} --scope /subscriptions/{subscription-id}/providers/{ProviderNamespace}/locations/{region}`
           - Get current usage: `az quota usage show --resource-name {quota-resource-name} --scope /subscriptions/{subscription-id}/providers/{ProviderNamespace}/locations/{region}`
         - **If quota CLI is NOT supported** (returns `BadRequest`):
           - Get current usage: `az graph query -q "resources | where type == '{resource-type}' and location == '{location}' | count"` (requires `az extension add --name resource-graph`)
           - Get limit: [Azure service limits documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits)
      3. **Calculate total** - Add "Number to Deploy" + current usage = "Total After Deployment"
      4. **Verify capacity** - Ensure "Total After Deployment" ≤ "Limit/Quota"
      5. **Document source** - Note whether data came from "azure-quotas (resource-name)" or "Azure Resource Graph + Official docs"
      
      **Completed example:**
      
      | Resource Type | Number to Deploy | Total After Deployment | Limit/Quota | Notes |
      |---------------|------------------|------------------------|-------------|-------|
      | Microsoft.App/managedEnvironments | 1 | 1 | 50 | Fetched from: azure-quotas (ManagedEnvironmentCount) |
      | Microsoft.Compute/virtualMachines (Standard_D4s_v3) | 3 | 15 | 350 vCPUs | Fetched from: azure-quotas (standardDSv3Family) |
      | Microsoft.Network/publicIPAddresses | 2 | 5 | 100 | Fetched from: azure-quotas (PublicIPAddresses) |
      | Microsoft.DocumentDB/databaseAccounts | 1 | 1 | 50 per region | Fetched from: Official docs (quota CLI not supported) |
      | Microsoft.Storage/storageAccounts | 2 | 8 | 250 per region | Fetched from: Official docs |
      
      **Status:** ✅ All resources within limits | ⚠️ Near limit (>80%) | ❌ Insufficient capacity
      
      > **⛔ CRITICAL:** You **CANNOT** present this plan to the customer if ANY cells contain "_TBD_" or "_To be filled in Phase 2_". Phase 2 **MUST** be completed with actual quota data before user presentation.
      
      **Notes:**
      - **MUST use azure-quotas skill first** to check providers via quota CLI (`az quota` commands) - Microsoft.Compute, Microsoft.Network, Microsoft.App, etc.
      - Azure quota CLI is **ALWAYS preferred over REST API** for checking quotas
      - **ONLY for unsupported providers** (e.g., Microsoft.DocumentDB returns `BadRequest`), use fallback methods: [Azure service limits documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits)
      - If any resource exceeds limits, return to Step 2 to select a different region or request quota increase
      
      ---
      
      ## 7. Execution Checklist
      
      ### Phase 1: Planning
      - [ ] Analyze workspace
      - [ ] Gather requirements
      - [ ] Confirm subscription and location with user
      - [ ] Prepare resource inventory (Step 6 Phase 1: list resource types and deployment quantities)
      - [ ] Fetch quotas and validate capacity (Step 6 Phase 2: invoke azure-quotas skill to use quota CLI)
      - [ ] Scan codebase
      - [ ] Select recipe
      - [ ] Plan architecture
      - [ ] **User approved this plan**
      
      ### Phase 2: Execution
      - [ ] Research components (load references, invoke skills)
      - [ ] **⛔ For Azure Functions: Load composition rules** (`services/functions/templates/selection.md` → `services/functions/templates/recipes/composition.md`) and use `functions_template_get` MCP tool to list and fetch templates, then write `functionFiles[]` + `projectFiles[]` directly — NEVER hand-write Bicep/Terraform and use `azd init -t <template>`/`func init`/`func new` as fallback when composing multiple recipes and required templates are not found
      - [ ] For other services: Generate infrastructure files following service-specific guidance
      - [ ] Apply recipes for integrations (if needed)
      - [ ] Generate application configuration
      - [ ] Generate Dockerfiles (if containerized)
      - [ ] **⛔ Update plan status to "Ready for Validation"** — Use the `edit` tool to change the Status line in `.azure/deployment-plan.md`. This step is MANDATORY before invoking azure-validate.
      
      ### Phase 3: Validation
      - [ ] **PREREQUISITE:** Plan status MUST be "Ready for Validation" (Phase 2 last step)
      - [ ] Invoke azure-validate skill
      - [ ] All validation checks pass
        - [ ] _Replace this with recipe validation steps_
      - [ ] Update plan status to "Validated"
      - [ ] Record validation proof below
      
      ### Phase 4: Deployment
      - [ ] Invoke azure-deploy skill
      - [ ] Deployment successful
      - [ ] Report deployed endpoint URLs
      - [ ] Update plan status to "Deployed"
      
      ---
      
      ## 7. Validation Proof
      
      > **⛔ REQUIRED**: The azure-validate skill MUST populate this section before setting status to `Validated`. If this section is empty and status is `Validated`, the validation was bypassed improperly.
      
      | Check | Command Run | Result | Timestamp |
      |-------|-------------|--------|-----------|
      | {check-name} | {actual command executed} | ✅ Pass / ❌ Fail | {timestamp} |
      
      **Validated by:** azure-validate skill
      **Validation timestamp:** {timestamp}
      
      ---
      
      ## 8. Files to Generate
      
      | File | Purpose | Status |
      |------|---------|--------|
      | `.azure/deployment-plan.md` | This plan | ✅ |
      | `azure.yaml` | AZD configuration | ⏳ |
      | `infra/main.bicep` | Infrastructure | ⏳ |
      | `src/{component}/Dockerfile` | Container build | ⏳ |
      
      ---
      
      ## 9. Next Steps
      
      > Current: {current phase}
      
      1. {next action}
      2. {following action}
      ```
      
      ---
      
      ## Instructions
      
      1. **Create the plan first** — Fill in all sections based on analysis
      2. **Complete quota validation** — Ensure Step 6 Phase 2 is completed with NO "_TBD_" entries. **MUST use azure-quotas skill** as the primary method to fetch actual quota/usage data via quota CLI (`az quota` commands) for all resources. Use fallback methods ONLY when provider returns `BadRequest`.
      3. **Present to user** — Show the completed plan and ask for approval. **DO NOT** present if Step 6 contains any "_TBD_" or "_To be filled in Phase 2_" entries.
      4. **Update as you go** — Check off items in the execution checklist
      5. **Track status** — Update the Status field at the top as you progress
      
      The plan is the **single source of truth** for azure-validate and azure-deploy skills.
      
    • recipe-selection.md 3.3 KB
      # Recipe Selection
      
      Choose the deployment recipe based on project needs and existing tooling.
      
      ## ⛔ Special Cases: Detect First
      
      **Before selecting a recipe, check for these special project types:**
      
      | Project Type | Detection | Recipe Selection |
      |--------------|-----------|------------------|
      | **.NET Aspire** | `*.AppHost.csproj` or `Aspire.Hosting` package | **AZD (auto via `azd init --from-code`)** → [aspire.md](aspire.md) |
      
      > 💡 **Tip:** .NET Aspire projects always use AZD recipe with auto-generated configuration. Do not manually select recipe or create artifacts.
      
      ## Quick Decision
      
      **Default: AZD** unless specific requirements indicate otherwise.
      
      > 💡 **Tip:** azd supports both Bicep and Terraform as IaC providers. When Terraform is mentioned for Azure deployment, **default to azd+Terraform** for the best developer experience.
      
      ## Decision Criteria
      
      | Choose | When |
      |--------|------|
      | **AZD (Bicep)** | New projects, multi-service apps, want simplest deployment (`azd up`) |
      | **AZD (Terraform)** | **DEFAULT for Terraform** - Want Terraform IaC + azd simplicity, Azure deployment with Terraform |
      | **AZCLI** | Existing az scripts, need imperative control, custom pipelines, AKS |
      | **Bicep** | IaC-first approach, no CLI wrapper needed, direct ARM deployment |
      | **Terraform** | Multi-cloud deployments (non-Azure-first), complex TF workflows incompatible with azd, explicitly requested |
      
      ## Auto-Detection
      
      | Found in Workspace | Suggested Recipe |
      |--------------------|------------------|
      | `azure.yaml` with `infra.provider: terraform` | AZD (Terraform) |
      | `azure.yaml` (Bicep or no provider specified) | AZD (Bicep) |
      | `*.tf` files (no azure.yaml) | **AZD (Terraform) - DEFAULT** (unless multi-cloud) |
      | `infra/*.bicep` (no azure.yaml) | Bicep or AZCLI |
      | Existing `az` scripts | AZCLI |
      | None | AZD (Bicep) - default |
      
      ## Recipe Comparison
      
      | Feature | AZD (Bicep) | AZD (Terraform) | AZCLI | Bicep | Terraform |
      |---------|-------------|-----------------|-------|-------|-----------|
      | Config file | azure.yaml | azure.yaml + *.tf | scripts | *.bicep | *.tf |
      | IaC language | Bicep | Terraform | N/A | Bicep | Terraform |
      | Deploy command | `azd up` | `azd up` | `az` commands | `az deployment` | `terraform apply` |
      | Dockerfile gen | Auto | Auto | Manual | Manual | Manual |
      | Environment mgmt | Built-in | Built-in | Manual | Manual | Workspaces |
      | CI/CD gen | Built-in | Built-in | Manual | Manual | Manual |
      | Multi-cloud | No | Yes | No | No | Yes |
      | Learning curve | Low | Low-Medium | Medium | Medium | Medium |
      
      ## Record Selection
      
      Document in `.azure/deployment-plan.md`:
      
      ```markdown
      ## Recipe: AZD (Terraform)
      
      **Rationale:**
      - Team has Terraform expertise
      - Want multi-cloud IaC flexibility
      - But prefer azd's simple deployment workflow
      - Multi-service app (API + Web)
      ```
      
      Or for pure Terraform:
      
      ```markdown
      ## Recipe: Terraform
      
      **Rationale:**
      - Multi-cloud deployment (AWS + Azure)
      - Complex Terraform modules incompatible with azd conventions
      - Existing Terraform CI/CD pipeline
      ```
      
      ## Recipe References
      
      - [AZD Recipe](recipes/azd/README.md)
      - [AZCLI Recipe](recipes/azcli/README.md)
      - [Bicep Recipe](recipes/bicep/README.md)
      - [Terraform Recipe](recipes/terraform/README.md)
      
    • region-availability.md 1.7 KB
      # Azure Region Availability Index
      
      > **AUTHORITATIVE SOURCE** — Consult service-specific files BEFORE recommending any region.
      >
      > Official reference: https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/table
      
      ## How to Use
      
      1. Check if your architecture includes any **limited availability** services below
      2. If yes → consult the service-specific file or use the MCP tool to list supported regions with sufficient quota for that service, and only offer regions that support ALL services
      3. If all services are "available everywhere" → offer common regions
      
      ## MCP Tools Used
      
      | Tool | Purpose |
      |------|---------|
      | `mcp_azure_mcp_quota` | Check Azure region availability and quota by setting `command` to `quota_usage_check` or `quota_region_availability_list` |
      
      ---
      
      ## Services with LIMITED Region Availability
      
      | Service | Availability | Details |
      |---------|--------------|---------|
      | Static Web Apps | Limited (5 regions) | [Region Details](services/static-web-apps/region-availability.md) |
      | Azure AI Foundry | Very limited (by model) | [Region Details](services/foundry/region-availability.md) |
      | Azure Kubernetes Service (AKS) | Limited in some regions | To get available regions with enough quota, use `mcp_azure_mcp_quota` tool. |
      | Azure Database for PostgreSQL | Limited in some regions | To get available regions with enough quota, use `mcp_azure_mcp_quota` tool. |
      
      ---
      
      ## Services Available in Most Regions
      
      These services are available in all major Azure regions — no special consideration needed:
      
      - Container Apps
      - Azure Functions
      - App Service
      - Azure SQL Database
      - Cosmos DB
      - Key Vault
      - Storage Account
      - Service Bus
      - Event Grid
      - Application Insights / Log Analytics
      
      
      
    • requirements.md 2.6 KB
      # Requirements Gathering
      
      Collect project requirements through conversation before making architecture decisions.
      
      ## Categories
      
      ### 1. Classification
      
      | Type | Description | Implications |
      |------|-------------|--------------|
      | POC | Proof of concept | Minimal infra, cost-optimized |
      | Development | Internal tooling | Balanced, team-focused |
      | Production | Customer-facing | Full reliability, monitoring |
      
      ### 2. Scale
      
      | Scale | Users | Considerations |
      |-------|-------|----------------|
      | Small | <1K | Single region, basic SKUs |
      | Medium | 1K-100K | Auto-scaling, multi-zone |
      | Large | 100K+ | Multi-region, premium SKUs |
      
      ### 3. Budget
      
      | Profile | Focus |
      |---------|-------|
      | Cost-Optimized | Minimize spend, lower SKUs |
      | Balanced | Value for money, standard SKUs |
      | Performance | Maximum capability, premium SKUs |
      
      ### 4. Compliance
      
      | Requirement | Impact |
      |-------------|--------|
      | Data residency | Region constraints |
      | Industry regulations | Security controls |
      | Internal policies | Approval workflows |
      
      ### 5. Subscription Policies
      
      After the user confirms a subscription, query Azure Policy assignments to discover enforcement constraints before making architecture decisions.
      
      ```
      mcp_azure_mcp_policy(command: "policy_assignment_list", subscription: "<subscriptionId>")
      ```
      
      | Policy Constraint | Impact |
      |-------------------|--------|
      | Blocked resource types or SKUs | Exclude from architecture |
      | Required tags | Add to all Bicep/Terraform resources |
      | Allowed regions | Restrict location choices |
      | Network restrictions (e.g., no public endpoints) | Adjust networking and access patterns |
      | Storage policies (e.g., deny shared key access) | Use policy-compliant auth |
      | Naming conventions | Apply to resource naming |
      
      > ⚠️ **Warning:** Skipping this step can cause deployment failures when Azure Policy denies resource creation. Checking policies here prevents wasted work in architecture and generation phases.
      
      Record discovered policy constraints in `.azure/deployment-plan.md` under a **Policy Constraints** section so they feed into architecture decisions.
      
      ## Gather via Conversation
      
      Use `ask_user` tool to confirm each of these with the user:
      
      1. Project classification (POC/Dev/Prod)
      2. Expected scale
      3. Budget constraints
      4. Compliance requirements (including data residency preferences)
      5. Subscription and policy constraints (confirm subscription, then check policies automatically)
      6. Architecture preferences (if any)
      
      ## Document in Plan
      
      Record all requirements in `.azure/deployment-plan.md` immediately after gathering.
      
    • research.md 8.7 KB
      # Research Components
      
      After architecture planning, research each selected component to gather best practices before generating artifacts.
      
      ## Process
      
      1. **Identify Components** — List all Azure services from architecture plan
      2. **Load Service References** — For each service, load `services/<service>/README.md` first, then specific references as needed
      3. **Check Resource Naming Rules** — For each resource type, check [resource naming rules](https://learn.microsoft.com/azure/azure-resource-manager/management/resource-name-rules) for valid characters, length limits, and uniqueness scopes
      4. **Load Recipe References** — Load the selected recipe's guide (e.g., [AZD](recipes/azd/README.md)) and its IAC rules, MCP best practices, and schema tools listed in its "Before Generation" table
      5. **Check Region Availability** — Verify all selected services are available in the target region per [region-availability.md](region-availability.md)
      6. **Check Provisioning Limits** — Invoke **azure-quotas** skill to validate that the selected subscription and region have sufficient quota/capacity for all planned resources. Complete [Step 6 of the plan template](plan-template.md#6-provisioning-limit-checklist) in two phases: (1) prepare resource inventory with deployment quantities, (2) fetch quotas and validate capacity using azure-quotas skill
      7. **Load Runtime References** — For containerized apps, load language-specific production settings (e.g., [Node.js](runtimes/nodejs.md))
      8. **Invoke Related Skills** — For deeper guidance, invoke mapped skills from the table below
      9. **Document Findings** — Record key insights in `.azure/deployment-plan.md`
      
      ## Service-to-Reference Mapping
      
      | Azure Service | Reference | Related Skills |
      |---------------|-----------|----------------|
      | **Hosting** | | |
      | Container Apps | [Container Apps](services/container-apps/README.md) | `azure-diagnostics`, `azure-observability`, `azure-nodejs-production` |
      | App Service | [App Service](services/app-service/README.md) | `azure-diagnostics`, `azure-observability`, `azure-nodejs-production` |
      | Azure Functions | [Functions](services/functions/README.md) | — |
      | Static Web Apps | [Static Web Apps](services/static-web-apps/README.md) | — |
      | AKS | [AKS](services/aks/README.md) | `azure-networking` |
      | **Data** | | |
      | Azure SQL | [SQL Database](services/sql-database/README.md) | — |
      | Cosmos DB | [Cosmos DB](services/cosmos-db/README.md) | — |
      | PostgreSQL | — | — |
      | Storage (Blob/Files) | [Storage](services/storage/README.md) | `azure-storage` |
      | **Messaging** | | |
      | Service Bus | [Service Bus](services/service-bus/README.md) | — |
      | Event Grid | [Event Grid](services/event-grid/README.md) | — |
      | Event Hubs | — | — |
      | **Integration** | | |
      | API Management | [APIM](apim.md) | `azure-aigateway` (invoke for AI Gateway policies) |
      | Logic Apps | [Logic Apps](services/logic-apps/README.md) | — |
      | **Workflow & Orchestration** | | |
      | Durable Functions | [Durable Functions](services/functions/durable.md), [Durable Task Scheduler](services/durable-task-scheduler/README.md) | — |
      | Durable Task Scheduler | [Durable Task Scheduler](services/durable-task-scheduler/README.md) | — |
      | **Security & Identity** | | |
      | Key Vault | [Key Vault](services/key-vault/README.md) | `azure-keyvault-expiration-audit` |
      | Managed Identity | — | `entra-app-registration` |
      | **Observability** | | |
      | Application Insights | [App Insights](services/app-insights/README.md) | `appinsights-instrumentation` (invoke for instrumentation) |
      | Log Analytics | — | `azure-observability`, `azure-kusto` |
      | **AI Services** | | |
      | Azure OpenAI | [Foundry](services/foundry/README.md) | `microsoft-foundry` (invoke for AI patterns and model guidance) |
      | AI Search | — | `azure-ai` (invoke for search configuration) |
      
      ## Research Instructions
      
      ### Step 1: Load Internal References (Progressive Loading)
      
      For each selected service, load the README.md first, then load specific files as needed:
      
      ```
      Selected: Container Apps, Cosmos DB, Key Vault
      
      → Load: services/container-apps/README.md (overview)
        → If need Bicep: services/container-apps/bicep.md
        → If need Terraform: services/container-apps/terraform.md
        → If need scaling: services/container-apps/scaling.md
        → If need health probes: services/container-apps/health-probes.md
      
      → Load: services/cosmos-db/README.md (overview)
        → If need partitioning: services/cosmos-db/partitioning.md
        → If need SDK: services/cosmos-db/sdk.md
      
      → Load: services/key-vault/README.md (overview)
        → If need SDK: services/key-vault/sdk.md
      ```
      
      ### Step 2: Invoke Related Skills (When Deeper Guidance Needed)
      
      Invoke related skills for specialized scenarios:
      
      | Scenario | Action |
      |----------|--------|
      | Using Azure Functions | Stay in **azure-prepare** — load [selection.md](services/functions/templates/selection.md) → Follow [composition.md](services/functions/templates/recipes/composition.md) algorithm |
      | PostgreSQL with passwordless auth | Handle directly without a separate skill |
      | Need detailed security hardening | Handle directly with service-specific security guidance and platform best practices |
      | Setting up App Insights instrumentation | `appinsights-instrumentation` |
      | Building AI applications | `microsoft-foundry` |
      | Estimate a planned deployment's cost | `cost-estimation` from the separately installed `azure-cost` plugin |
      
      **Skill/Reference Invocation Pattern:**
      
      For **Azure Functions**:
      1. Load: [selection.md](services/functions/templates/selection.md) (decision tree)
      2. Follow: [composition.md](services/functions/templates/recipes/composition.md) (algorithm)
      3. Result: Base template + recipe composition (never synthesize IaC)
      
      For **PostgreSQL**:
      1. Handle passwordless auth patterns directly without a separate skill
      
      ### Step 3: Document in Plan
      
      Add research findings to `.azure/deployment-plan.md` under a `## Research Summary` section with source references and key insights per component.
      
      ## Common Research Patterns
      
      ### Web Application + API + Database (Cosmos DB)
      
      1. Load: [services/container-apps/README.md](services/container-apps/README.md) → [bicep.md](services/container-apps/bicep.md) or [terraform.md](services/container-apps/terraform.md), [scaling.md](services/container-apps/scaling.md)
      2. Load: [services/cosmos-db/README.md](services/cosmos-db/README.md) → [partitioning.md](services/cosmos-db/partitioning.md)
      3. Load: [services/key-vault/README.md](services/key-vault/README.md)
      4. Invoke: `azure-observability` (monitoring setup)
      5. Review service-specific security guidance directly before generation
      
      ### Container Apps + API + SQL Database
      
      1. Load: [services/container-apps/README.md](services/container-apps/README.md) → [bicep.md](services/container-apps/bicep.md) or [terraform.md](services/container-apps/terraform.md), [scaling.md](services/container-apps/scaling.md)
      2. Load: [services/sql-database/README.md](services/sql-database/README.md) → [bicep.md](services/sql-database/bicep.md), [auth.md](services/sql-database/auth.md)
      3. Load: [services/key-vault/README.md](services/key-vault/README.md)
      4. Review [auth.md](services/sql-database/auth.md) directly for Entra-only auth configuration
      
      ### App Service + API + SQL Database
      
      1. Load: [services/app-service/README.md](services/app-service/README.md) → [bicep.md](services/app-service/bicep.md)
      2. Load: [services/sql-database/README.md](services/sql-database/README.md) → [bicep.md](services/sql-database/bicep.md), [auth.md](services/sql-database/auth.md)
      3. Load: [services/key-vault/README.md](services/key-vault/README.md)
      4. Review [auth.md](services/sql-database/auth.md) directly for Entra-only auth configuration
      
      ### Serverless Event-Driven
      
      1. Load: [services/functions/README.md](services/functions/README.md) (contains mandatory composition workflow)
      2. Load: [services/event-grid/README.md](services/event-grid/README.md) or [services/service-bus/README.md](services/service-bus/README.md) (if using messaging)
      3. Load: [services/storage/README.md](services/storage/README.md) (if using queues/blobs)
      4. Invoke: `azure-observability` (distributed tracing)
      
      ### AI Application
      
      1. Invoke: `microsoft-foundry` (AI patterns and best practices)
      2. Load: [services/container-apps/README.md](services/container-apps/README.md) → [bicep.md](services/container-apps/bicep.md) or [terraform.md](services/container-apps/terraform.md)
      3. Load: [services/cosmos-db/README.md](services/cosmos-db/README.md) → [partitioning.md](services/cosmos-db/partitioning.md) (vector storage)
      4. Review Key Vault and Foundry references directly for API key management
      
      ## After Research
      
      Proceed to **Generate Artifacts** step with research findings applied.
      
    • resources-limits-quotas.md 13 KB
      # Azure Resource Limits and Quotas
      
      Check Azure resource availability during azure-prepare workflow. Validate after customer selects region.
      
      ## Types
      
      1. **Hard Limits** - Fixed constraints that cannot be changed
      2. **Quotas** - Subscription limits that can be increased via support request
      
      **CLI First:** Always use `az quota` CLI for quota checks. Provides better error handling and consistent output. "No Limit" in REST/Portal doesn't mean unlimited - verify with service docs.
      
      ## Hard Limits
      
      Fixed service constraints (cannot be changed).
      
      **Check via**: `azure__documentation` tool or azure-provisioning-limit skill
      
      **Examples**: Cosmos DB item size (2 MB), Container Apps HTTP timeout (240s), App Service Free tier deployment slots (0)
      
      **Process**:
      1. Identify services and resource sizes needed
      2. Look up limits in documentation
      3. Compare plan vs limits
      4. If exceeded: redesign or change tier
      
      ## Quotas
      
      Subscription/regional limits that can be increased via support request.
      
      **Check via**: `az quota` CLI (install: `az extension add --name quota`)
      
      **Examples**: AKS clusters (5,000/region), Storage accounts (250/region), Container Apps environments (50/region)
      
      **Key Concept**: No 1:1 mapping between ARM resource types and quota names.
      - ARM: `Microsoft.App/managedEnvironments` → Quota: `ManagedEnvironmentCount`
      - ARM: `Microsoft.Compute/virtualMachines` → Quota: `standardDSv3Family`, `cores`, `virtualMachines`
      
      **Process**:
      1. Install extension: `az extension add --name quota`
      2. Discover quota names: `az quota list --scope /subscriptions/{id}/providers/{Provider}/locations/{region}`
      3. Check usage: `az quota usage show --resource-name {name} --scope ...`
      4. Check limit: `az quota show --resource-name {name} --scope ...`
      5. Calculate: Available = Limit - Current Usage
      6. If exceeded: Request increase via `az quota update`
      
      **Unsupported Providers** (BadRequest error):
      
      Not all providers support quota API. If `az quota list` fails with BadRequest, use fallback:
      
      1. Get current usage:
         ```bash
         # Option A: Azure Resource Graph (recommended)
         az extension add --name resource-graph
         az graph query -q "resources | where type == '{type}' and location == '{loc}' | count"
         
         # Option B: Resource list
         az resource list --subscription "{id}" --resource-type "{Type}" --location "{loc}" | jq 'length'
         ```
      2. Get limit from [service documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits)
      3. Calculate: Available = Documented Limit - Current Usage
      
      **Known Support Status**:
      - ❌ Microsoft.DocumentDB (Cosmos DB)
      - ✅ Microsoft.Compute, Microsoft.Network, Microsoft.App, Microsoft.Storage, Microsoft.MachineLearningServices
      
      ## Workflow
      
      **Phase 1: Identify & Check Hard Limits**
      1. Analyze app requirements and select Azure services
      2. Determine resource counts, sizes, tiers, throughput
      3. Check hard limits via azure-provisioning-limit skill or documentation
      4. Validate plan against limits; redesign if needed
      
      **Phase 2: Check Quotas After Region Selection**
      1. Get customer subscription and region preference
      2. For each service/region, check quota:
         - Use `az quota usage list` and `az quota show`
         - Calculate available capacity
      3. If quota exceeded: request increase or choose different region
      
      **Phase 3: Validate Region**
      - Confirm sufficient quota in selected region
      - Request increases if needed
      - Only proceed after validation complete
      
      ## Limit Scopes
      
      | Scope | Example |
      |-------|---------|
      | Subscription | 50 Cosmos DB accounts (any region) |
      | Regional | 250 storage accounts per region |
      | Resource | 500 apps per Container Apps environment |
      
      ## Service Patterns
      
      | Service | Hard Limits (examples) | Quota Check | Notes |
      |---------|------------------------|-------------|-------|
      | **Cosmos DB** | Item: 2MB, Partition key: 2KB, Serverless storage: 50GB | ❌ Not supported. Use Resource Graph + [docs](https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits). Default: 50 accounts/region | Query: `az graph query -q "resources \| where type == 'microsoft.documentdb/databaseaccounts' and location == 'eastus' \| count"` |
      | **AKS** | Pods/node (Azure CNI): 250, Node pools/cluster: 100 | ✅ `az quota` supported | Provider: Microsoft.ContainerService |
      | **Storage** | Block blob: 190.7 TiB, Page blob: 8 TiB | ✅ Quota: `StorageAccounts` (limit: 250/region) | Provider: Microsoft.Storage |
      | **Container Apps** | Revisions/app: 100, HTTP timeout: 240s | ✅ Quota: `ManagedEnvironmentCount` (limit: 50/region) | Provider: Microsoft.App |
      | **Functions** | Timeout (Consumption): 10 min, Queue msg: 64KB | ✅ Check function apps quota | Provider: Microsoft.Web |
      
      ## CLI Reference
      
      **Prerequisites**: `az extension add --name quota`
      
      **Discovery**: List quotas to find resource names
      ```bash
      az quota list --scope /subscriptions/{id}/providers/{provider}/locations/{location}
      ```
      
      **Check Usage**:
      ```bash
      az quota usage show --resource-name {quota-name} --scope /subscriptions/{id}/providers/{provider}/locations/{location}
      ```
      
      **Check Limit**:
      ```bash
      az quota show --resource-name {quota-name} --scope /subscriptions/{id}/providers/{provider}/locations/{location}
      ```
      
      **Request Increase**:
      ```bash
      az quota update --resource-name {quota-name} --scope /subscriptions/{id}/providers/{provider}/locations/{location} --limit-object value={new-limit} --resource-type {type}
      ```
      
      ## azure-prepare Integration
      
      **When to Check**:
      1. After selecting services - Check hard limits
      2. After customer selects region - Check quotas
      3. Before generating infrastructure code - Validate availability
      
      **Required Steps**:
      
      **Phase 1 - Planning**:
      - Select Azure services
      - Check hard limits (service documentation)
      - Create provisioning limit checklist (leave quota columns as "_TBD_")
      
      **Phase 2 - Execution**:
      - Get subscription and region preference
      - **Must invoke azure-quotas skill** - Process ONE resource type at a time:
        a. Try `az quota list` first (required)
        b. If supported: Use `az quota usage show` and `az quota show`
        c. If NOT supported (BadRequest): Use Resource Graph + service docs
        d. Calculate available capacity
        e. Document in checklist (no "_TBD_" entries allowed)
        f. If insufficient: Request increase or change region
      
      **Phase 3 - Generate Artifacts**:
      - Only proceed after Phase 2 complete (all quotas validated)
      
      ## Error Messages
      
      | Error | Type | Action |
      |-------|------|--------|
      | "Quota exceeded" | Quota | Use azure-quotas to request increase |
      | "(BadRequest) Bad request" | Unsupported provider | Use [service limits docs](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits) |
      | "Limit exceeded" | Hard Limit | Redesign or change tier |
      | "Maximum size exceeded" | Hard Limit | Split data or use alternative storage |
      | "Too many requests" | Rate Limit | Implement retry logic or increase tier |
      | "Cannot exceed X" | Hard Limit | Stay within limit or use multiple resources |
      | "Subscription limit reached" | Quota | Request quota increase using azure-quotas skill |
      | "Regional capacity" | Quota | Choose different region or request increase |
      
      ## Best Practices
      
      1. **MUST use Azure CLI quota API first**: `az quota` commands are MANDATORY as the primary method for checking quotas - only use fallback methods (REST API, Portal, docs) when quota API returns `BadRequest`
      2. **Don't trust "No Limit" values**: If REST API or Portal shows "No Limit" or unlimited, verify with official service documentation - it likely means the quota API doesn't support that resource type, not that capacity is unlimited
      3. **Always check after customer selects region**: Validates availability and allows time for quota requests
      4. **Use the discovery workflow**: Never assume quota resource names - always run `az quota list` first to discover correct names
      5. **Check both usage and limit**: Run `az quota usage show` AND `az quota show` to calculate available capacity
      6. **Handle unsupported providers gracefully**: If you get `BadRequest` error, fall back to official documentation (Azure Resource Graph + docs)
      7. **Request quota increases proactively**: If selected region lacks capacity, submit request before deployment
      8. **Have alternative regions ready**: If quota increase denied, suggest backup regions
      9. **Document capacity assumptions**: Note quota availability and source in `.azure/deployment-plan.md`
      10. **Design for limits**: Architecture should account for both hard limits and quotas
      11. **Monitor usage trends**: Regular quota checks help predict future needs
      12. **Use lower environments wisely**: Dev/test environments count against quotas
      
      ## Quick Reference Limits
      
      For complete quota checking workflow and commands, invoke the **azure-quotas** skill.
      
      > **Note:** These are typical default limits. Always verify actual quotas using `az quota show` for your specific subscription and region.
      
      Common quotas to check:
      
      ### Subscription Level
      - Cosmos DB accounts: 50 per region (check via docs - quota API not supported)
      - SQL logical servers: 250 per region
      - Service Bus namespaces: 100-1,000 (tier dependent)
      
      ### Regional Level  
      - Storage accounts: 250 per region (quota resource name: `StorageAccounts`)
      - AKS clusters: 5,000 per region (quota resource name: varies by configuration)
      - Container Apps environments: 50 per region (quota resource name: `ManagedEnvironmentCount`)
      - Function apps: 200 per region (Consumption)
      
      ### Resource Level
      - Cosmos DB containers per account: Unlimited (subject to storage)
      - Apps per Container Apps environment: 500
      - Databases per SQL server: 500
      - Queues/topics per Service Bus namespace: 10,000
      
      ## Related Documentation
      
      - **azure-quotas skill** - Complete quota checking workflow and CLI commands (invoke the **azure-quotas** skill)
      - [Azure subscription limits](https://learn.microsoft.com/azure/azure-resource-manager/management/azure-subscription-service-limits) - Official Microsoft documentation
      - [Azure Quotas Overview](https://learn.microsoft.com/en-us/azure/quotas/quotas-overview) - Understanding quotas and limits
      - [azure-context.md](azure-context.md) - How to confirm subscription and region
      - [architecture.md](architecture.md) - Architecture planning workflow
      
      ## Example: Complete Check Workflow
      
      ```bash
      # Scenario: Deploying app with Cosmos DB, Storage, and Container Apps
      # Customer selected region: East US
      
      # 1. Check Hard Limits (from azure-provisioning-limit skill)
      # Cosmos DB: Item size max 2 MB ✓
      # Storage: Blob size max 190.7 TiB ✓
      # Container Apps: Timeout 240 sec ✓
      
      # 2. Get Customer's Region Preference
      # Customer: "I prefer East US"
      
      # 3. Check Quotas for Customer's Selected Region (East US)
      
      # 3a. Cosmos DB - NOT SUPPORTED by quota API
      az quota list \
        --scope /subscriptions/abc-123/providers/Microsoft.DocumentDB/locations/eastus
      # Error: (BadRequest) Bad request
      
      # Fallback: Get current usage with Azure Resource Graph
      # Install extension first (if needed)
      az extension add --name resource-graph
      
      az graph query -q "resources | where type == 'microsoft.documentdb/databaseaccounts' and location == 'eastus' | count"
      # Result: 3 database accounts currently deployed
      
      # Or use Azure CLI resource list
      az resource list \
        --subscription "abc-123" \
        --resource-type "Microsoft.DocumentDB/databaseAccounts" \
        --location "eastus" | jq 'length'
      # Result: 3
      
      # Get limit from documentation: 50 database accounts per region
      # Calculate: Available = 50 - 3 = 47 ✓
      # Document as: "Fetched from: Azure Resource Graph + Official docs"
      
      # 3b. Storage Accounts
      # Step 1: Discover resource name
      az quota list \
        --scope /subscriptions/abc-123/providers/Microsoft.Storage/locations/eastus
      
      # Step 2: Check usage (use discovered name "StorageAccounts")
      az quota usage show \
        --resource-name StorageAccounts \
        --scope /subscriptions/abc-123/providers/Microsoft.Storage/locations/eastus
      # Current: 180
      
      # Step 3: Check limit
      az quota show \
        --resource-name StorageAccounts \
        --scope /subscriptions/abc-123/providers/Microsoft.Storage/locations/eastus
      # Limit: 250
      # Available: 250 - 180 = 70 ✓
      
      # 3c. Container Apps
      # Step 1: Discover resource name
      az quota list \
        --scope /subscriptions/abc-123/providers/Microsoft.App/locations/eastus
      # Shows: "ManagedEnvironmentCount"
      
      # Step 2: Check usage
      az quota usage show \
        --resource-name ManagedEnvironmentCount \
        --scope /subscriptions/abc-123/providers/Microsoft.App/locations/eastus
      # Current: 8
      
      # Step 3: Check limit
      az quota show \
        --resource-name ManagedEnvironmentCount \
        --scope /subscriptions/abc-123/providers/Microsoft.App/locations/eastus
      # Limit: 50
      # Available: 50 - 8 = 42 ✓
      
      # 4. Validate Availability
      # ✅ All services have sufficient quota in East US
      # ✅ Proceed with deployment
      
      # Alternative: If quotas were insufficient
      # ❌ Container Apps: 49/50 (only 1 available, need 3)
      # Action: Request quota increase
      # 
      # az quota update \
      #   --resource-name ManagedEnvironmentCount \
      #   --scope /subscriptions/abc-123/providers/Microsoft.App/locations/eastus \
      #   --limit-object value=100 \
      #   --resource-type Microsoft.App/managedEnvironments
      ```
      
      ---
      
      > **Remember**: Checking limits and quotas early prevents deployment failures and ensures smooth infrastructure provisioning.
      
    • scan.md 3.1 KB
      # Codebase Scan
      
      Analyze workspace to identify components, technologies, and dependencies.
      
      ## Detection Patterns
      
      ### Languages & Frameworks
      
      | File | Indicates |
      |------|-----------|
      | `package.json` | Node.js |
      | `requirements.txt`, `pyproject.toml` | Python |
      | `*.csproj`, `*.sln` | .NET |
      | `pom.xml`, `build.gradle` | Java |
      | `go.mod` | Go |
      
      ### Component Types
      
      | Pattern | Component Type |
      |---------|----------------|
      | React/Vue/Angular in package.json | SPA Frontend |
      | Only .html/.css/.js files, no package.json | Pure Static Site |
      | Express/Fastify/Koa | API Service |
      | Flask/FastAPI/Django | API Service |
      | Next.js/Nuxt | SSR Web App |
      | Celery/Bull/Agenda | Background Worker |
      | azure-functions SDK | Azure Function |
      | .AppHost.csproj or Aspire.Hosting package | .NET Aspire App |
      
      **Pure Static Site Detection:**
      - No package.json, requirements.txt, or build configuration
      - Contains only HTML, CSS, JavaScript, and asset files
      - No framework dependencies (React, Vue, Angular, etc.)
      - ⚠️ For pure static sites, do NOT add `language` field to azure.yaml to avoid triggering build steps
      
      ### Existing Tooling
      
      | Found | Tooling |
      |-------|---------|
      | `azure.yaml` | AZD configured |
      | `infra/*.bicep` | Bicep IaC |
      | `infra/*.tf` | Terraform IaC |
      | `Dockerfile` | Containerized |
      | `.github/workflows/` | GitHub Actions CI/CD |
      | `azure-pipelines.yml` | Azure DevOps CI/CD |
      
      ### .NET Aspire Detection
      
      **.NET Aspire projects** are identified by:
      - A project ending with `.AppHost.csproj` (e.g., `OrleansVoting.AppHost.csproj`)
      - Reference to `Aspire.Hosting` or `Aspire.Hosting.AppHost` package in .csproj files
      - Multiple .NET projects in a solution, typically including an AppHost orchestrator
      
      **When Aspire is detected:**
      - Use `azd init --from-code -e <environment-name>` instead of manual azure.yaml creation
      - The `--from-code` flag automatically detects the AppHost and generates appropriate configuration
      - The `-e` flag is **required** for non-interactive environments (agents, CI/CD)
      - ⚠️ **CRITICAL:** If the AppHost contains `AddAzureFunctionsProject`, you **MUST** add `.WithEnvironment("AzureWebJobsSecretStorageType", "Files")` to the Functions builder chain BEFORE deployment. Without this, Functions will fail at startup with `Secret initialization from Blob storage failed`. See [aspire.md](aspire.md) Step 4b for the complete detection and fix procedure.
      - See [aspire.md](aspire.md) for detailed Aspire-specific guidance
      
      ## Output
      
      Document findings:
      
      ```markdown
      ## Components
      
      | Component | Type | Technology | Path |
      |-----------|------|------------|------|
      | api | API Service | Node.js/Express | src/api |
      | web | SPA | React | src/web |
      | worker | Background | Python | src/worker |
      
      ## Dependencies
      
      | Component | Depends On | Type |
      |-----------|-----------|------|
      | api | PostgreSQL | Database |
      | web | api | HTTP |
      | worker | Service Bus | Queue |
      
      ## Existing Infrastructure
      
      | Item | Status |
      |------|--------|
      | azure.yaml | Not found |
      | infra/ | Not found |
      | Dockerfiles | Found: src/api/Dockerfile |
      ```
      
    • security.md 8.5 KB
      # Security Hardening
      
      Secure Azure resources following Zero Trust principles.
      
      ## Security Principles
      
      1. **Zero Trust** — Never trust, always verify
      2. **Least Privilege** — Minimum required permissions
      3. **Defense in Depth** — Multiple security layers
      4. **Encryption Everywhere** — At rest and in transit
      
      ---
      
      ## Security Services
      
      | Service | Use When | MCP Tools | CLI |
      |---------|----------|-----------|-----|
      | Key Vault | Secrets, keys, certificates | `azure__keyvault` | `az keyvault` |
      | Managed Identity | Credential-free authentication | — | `az identity` |
      | RBAC | Role-based access control | `azure__role` | `az role` |
      | Entra ID | Identity and access management | — | `az ad` |
      | Defender | Threat protection, security posture | — | `az security` |
      
      ### MCP Tools (Preferred)
      
      When Azure MCP is enabled:
      
      **Key Vault:**
      - `azure__keyvault` with command `keyvault_list` — List Key Vaults
      - `azure__keyvault` with command `keyvault_secret_list` — List secrets
      - `azure__keyvault` with command `keyvault_secret_get` — Get secret value
      - `azure__keyvault` with command `keyvault_key_list` — List keys
      - `azure__keyvault` with command `keyvault_certificate_list` — List certificates
      
      **RBAC:**
      - `azure__role` with command `role_assignment_list` — List role assignments
      - `azure__role` with command `role_definition_list` — List role definitions
      
      ### CLI Quick Reference
      
      ```bash
      # Key Vault
      az keyvault list --output table
      az keyvault secret list --vault-name VAULT --output table
      
      # RBAC
      az role assignment list --output table
      
      # Managed Identity
      az identity list --output table
      ```
      
      ---
      
      ## Identity and Access
      
      ### Checklist
      
      - [ ] Use managed identities (no credentials in code)
      - [ ] Enable MFA for all users
      - [ ] Apply least privilege RBAC
      - [ ] Use Microsoft Entra ID for authentication
      - [ ] SQL Server: Entra-only auth — NEVER generate `administratorLogin` or `administratorLoginPassword` anywhere in Bicep, including inside conditional branches (see [sql-database/auth.md](services/sql-database/auth.md))
      - [ ] Review access regularly
      
      ### Managed Identity
      
      ```bash
      # App Service
      az webapp identity assign --name APP -g RG
      
      # Container Apps
      az containerapp identity assign --name APP -g RG --system-assigned
      
      # Function App
      az functionapp identity assign --name APP -g RG
      ```
      
      ### Grant Access
      
      ```bash
      # Grant Key Vault access
      az role assignment create \
        --role "Key Vault Secrets User" \
        --assignee IDENTITY_PRINCIPAL_ID \
        --scope /subscriptions/SUB/resourceGroups/RG/providers/Microsoft.KeyVault/vaults/VAULT
      ```
      
      ### Permissions Required to Grant Roles
      
      > ⚠️ **Important**: To assign RBAC roles to identities, you need a role with the `Microsoft.Authorization/roleAssignments/write` permission.
      
      | Your Role | Permissions | Recommended For |
      |-----------|-------------|-----------------|
      | **User Access Administrator** | Assign roles (no data access) | ✅ Least privilege for role assignment |
      | **Owner** | Full access + assign roles | ❌ More permissions than needed |
      | **Custom Role** | Specific permissions including roleAssignments/write | ✅ Fine-grained control |
      
      **Common Scenario**: Granting Storage Blob Data Owner to a Web App's managed identity
      
      ```bash
      # You need User Access Administrator (or Owner) on the Storage Account to run this:
      az role assignment create \
        --role "Storage Blob Data Owner" \
        --assignee WEBAPP_PRINCIPAL_ID \
        --scope /subscriptions/SUB/resourceGroups/RG/providers/Microsoft.Storage/storageAccounts/ACCOUNT
      ```
      
      If you encounter `AuthorizationFailed` errors when assigning roles, you likely need the User Access Administrator role at the target scope.
      
      ### RBAC Best Practices
      
      | Role | Use When |
      |------|----------|
      | Owner | Full access + assign roles |
      | Contributor | Full access except IAM |
      | Reader | View-only access |
      | Key Vault Secrets User | Read secrets only |
      | Storage Blob Data Reader | Read blobs only |
      
      ```bash
      # Grant minimal role at resource scope
      az role assignment create \
        --role "Storage Blob Data Reader" \
        --assignee PRINCIPAL_ID \
        --scope /subscriptions/SUB/resourceGroups/RG/providers/Microsoft.Storage/storageAccounts/ACCOUNT
      ```
      
      ---
      
      ## Network Security
      
      ### Checklist
      
      - [ ] Use private endpoints for PaaS services
      - [ ] Configure NSGs on all subnets
      - [ ] Disable public endpoints where possible
      - [ ] Enable DDoS protection
      - [ ] Use Azure Firewall or NVA
      
      ### Private Endpoints
      
      ```bash
      # Create private endpoint for storage
      az network private-endpoint create \
        --name myEndpoint -g RG \
        --vnet-name VNET --subnet SUBNET \
        --private-connection-resource-id STORAGE_ID \
        --group-id blob \
        --connection-name myConnection
      ```
      
      ### NSG Rules
      
      ```bash
      # Deny all inbound by default, allow only required traffic
      az network nsg rule create \
        --nsg-name NSG -g RG \
        --name AllowHTTPS \
        --priority 100 \
        --destination-port-ranges 443 \
        --access Allow
      ```
      
      ### Best Practices
      
      1. **Default deny** — Block all traffic by default, allow only required
      2. **Segment networks** — Use subnets and NSGs to isolate workloads
      3. **Private endpoints** — Use for all PaaS services in production
      4. **Service endpoints** — Alternative to private endpoints for simpler scenarios
      5. **Azure Firewall** — Centralize egress traffic control
      
      ---
      
      ## Data Protection
      
      ### Checklist
      
      - [ ] Enable encryption at rest (default for most Azure services)
      - [ ] Use TLS 1.2+ for transit
      - [ ] Store secrets in Key Vault
      - [ ] Enable soft delete for Key Vault
      - [ ] Use customer-managed keys (CMK) for sensitive data
      
      ### Key Vault Security
      
      ```bash
      # Enable soft delete and purge protection
      az keyvault update \
        --name VAULT -g RG \
        --enable-soft-delete true \
        --enable-purge-protection true
      
      # Enable RBAC permission model
      az keyvault update \
        --name VAULT -g RG \
        --enable-rbac-authorization true
      ```
      
      ### Best Practices
      
      1. **Never store secrets in code** — Use Key Vault or managed identity
      2. **Rotate secrets regularly** — Set expiration dates and automate rotation
      3. **Enable soft delete** — Protect against accidental deletion
      4. **Enable purge protection** — Prevent permanent deletion during retention
      5. **Use RBAC for Key Vault** — Prefer over access policies
      6. **Customer-managed keys** — For sensitive data requiring key control
      
      ---
      
      ## Monitoring and Defender
      
      ### Checklist
      
      - [ ] Enable Microsoft Defender for Cloud
      - [ ] Configure diagnostic logging
      - [ ] Set up security alerts
      - [ ] Enable audit logging
      
      ### Microsoft Defender for Cloud
      
      ```bash
      # Enable Defender plans
      az security pricing create \
        --name VirtualMachines \
        --tier Standard
      ```
      
      ### Security Assessment
      
      Use Microsoft Defender for Cloud for:
      - Security score
      - Recommendations
      - Compliance assessment
      - Threat detection
      
      ### Best Practices
      
      1. **Enable Defender** — For all production workloads
      2. **Review security score** — Address high-priority recommendations
      3. **Configure alerts** — Set up notifications for security events
      4. **Diagnostic logs** — Enable for all resources, send to Log Analytics
      5. **Audit logging** — Track administrative actions and access
      
      ---
      
      ## Azure Identity SDK
      
      All Azure SDKs use their language's Identity library for credential-free authentication. Use `DefaultAzureCredential` for **local development only**; in production, use `ManagedIdentityCredential` or another deterministic credential — see [auth-best-practices.md](auth-best-practices.md). Rust uses `DeveloperToolsCredential` as it doesn't have a `DefaultAzureCredential` equivalent.
      
      | Language | Package | Install |
      |----------|---------|---------|
      | .NET | `Azure.Identity` | `dotnet add package Azure.Identity` |
      | Java | `azure-identity` | Maven: `com.azure:azure-identity` |
      | JavaScript | `@azure/identity` | `npm install @azure/identity` |
      | Python | `azure-identity` | `pip install azure-identity` |
      | Go | `azidentity` | `go get github.com/Azure/azure-sdk-for-go/sdk/azidentity` |
      | Rust | `azure_identity` | `cargo add azure_identity` |
      
      For Key Vault SDK examples, see: [Key Vault Reference](services/key-vault/README.md)
      
      For Storage SDK examples, see: [Storage Reference](services/storage/README.md)
      
      ---
      
      ## Further Reading
      
      - [Key Vault documentation](https://learn.microsoft.com/azure/key-vault/general/overview)
      - [Managed identities documentation](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview)
      - [Azure RBAC documentation](https://learn.microsoft.com/azure/role-based-access-control/overview)
      - [Microsoft Defender for Cloud](https://learn.microsoft.com/azure/defender-for-cloud/defender-for-cloud-introduction)
      
    • specialized-routing.md 4.3 KB
      # Specialized Technology Routing
      
      **MANDATORY**: Before starting any planning, check the user's prompt for specialized technology keywords. If matched, invoke the corresponding skill FIRST — it has tested templates and optimized workflows for that technology.
      
      ## Prompt-Based Routing Table
      
      > **⚠️ PRIORITY RULE**: Check rows **top to bottom**. The first match wins. **Python + App Service (code-only) is the highest priority** — route to `python-appservice-deploy` ONLY when the prompt is a code-deploy request **without** IaC or infra keywords (see Priority 1 qualifier below). If the prompt mentions **AWS Lambda migration or AWS Lambda**, invoke **azure-cloud-migrate** even if Azure Functions are also mentioned.
      
      | Priority | User prompt mentions | Invoke skill FIRST | Then resume azure-prepare at |
      |----------|---------------------|--------------------|-----------------------------|
      | **1 (highest)** | Python + Azure App Service **AND NOT** any of: `Terraform`, `Bicep`, `IaC`, `VNet`, `private endpoint`, `Key Vault`, `Cosmos`, `Postgres`, `MySQL`, `SQL`, `Front Door`, `multi-environment`, `Lambda`, `migrate from AWS`, `migrate from GCP`, `Fargate`, `Cloud Run`, `ECS`, `EKS`, `GKE` (e.g., "deploy Python to App Service", "Flask on App Service", "Python web app on App Service") | **python-appservice-deploy** | This is a code-only deploy skill. Do not resume `azure-prepare`. If the prompt contains any IaC, infra, or cross-cloud migration keyword above, **skip this row** and continue to row 2+ (i.e., let `azure-cloud-migrate` handle Lambda/Fargate/Cloud Run migrations, or stay in `azure-prepare` for the full infrastructure path). |
      | 2 | Lambda, AWS Lambda, migrate AWS, migrate GCP, Lambda to Functions, migrate from AWS, migrate from GCP | **azure-cloud-migrate** | Phase 1 Step 4 (Select Recipe) — azure-cloud-migrate does assessment + code conversion, then azure-prepare takes over for infrastructure, local testing, or deployment |
      | 3 | Azure Functions, function app, serverless function, timer trigger, HTTP trigger, queue trigger, func new, func start | Stay in **azure-prepare** | Phase 1 Step 4 (Select Recipe) — prefer Azure Functions templates |
      | 4 (lowest) | workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable, order processing | Stay in **azure-prepare** | Phase 1 Step 4 — select **durable** recipe. **MUST** load [durable.md](services/functions/durable.md), [DTS reference](services/durable-task-scheduler/README.md), and [DTS Bicep patterns](services/durable-task-scheduler/bicep.md). |
      
      > ⚠️ This checks the user's **prompt text**, not just existing code. Essential for greenfield projects where there is no codebase to scan.
      
      ## Why This Step Exists
      
      azure-prepare is the default entry point for all Azure app work. Some technologies have dedicated skills with:
      - Pre-tested `azd` templates that avoid manual scaffolding errors
      - Specialized configuration (BYOM model config)
      - Optimized infrastructure patterns
      
      Without this check, azure-prepare generates generic infrastructure that misses these optimizations.
      
      > ⚠️ **Re-entry guard**: When azure-prepare is invoked as a **resume** from a specialized skill (e.g., `python-appservice-deploy` handing back for full-infra needs like VNet / Key Vault / DB provisioning), **skip this routing check** and proceed directly to Step 4. The specialized skill has already completed its work.
      
      ## Flow
      
      ```
      User prompt → azure-prepare activated
        │
        ├─ Prompt mentions specialized tech?
        │   ├─ YES → Invoke specialized skill → Skill scaffolds + configures
        │   │         → Resume azure-prepare at Step 4 (recipe/infra/validate/deploy)
        │   └─ NO  → Continue normal azure-prepare workflow from Step 1
        │
        └─ Phase 1 Step 3 (Scan Codebase) also detects tech indicators in existing files
            → See [scan.md](scan.md) for file-based detection
      ```
      
      ## Complementary Checks
      
      This prompt-based check complements — does not replace — existing file-based detection:
      - **[scan.md](scan.md)** — Detects tech in dependency files (package.json, requirements.txt)
      - **[analyze.md](analyze.md)** — Delegation table triggered by user mentions during planning
      - **[research.md](research.md)** — Skill invocation during research phase
      
      The prompt check catches **greenfield** scenarios where no code exists yet.
      
  • SKILL.md 12.6 KB
    ---
    name: azure-prepare
    description: "Prepare azd-based Azure projects for deployment: generates azure.yaml, infrastructure (Bicep/Terraform), and Dockerfiles for the Azure Developer CLI (azd) workflow. USE ONLY when the user explicitly wants to use azd as the deployment tool, or the project already has an azure.yaml file. DO NOT USE FOR: non-azd deployments, Python App Service code-only deploys (use python-appservice-deploy), or cross-cloud migration (use azure-cloud-migrate). WHEN: prepare app for azd, create azure.yaml, set up azd infrastructure, modernize app for Azure with azd, deploy with azd, function app, timer trigger, service bus trigger, event-driven function, managed identity, generate Bicep, generate Terraform, create and deploy to Azure."
    license: MIT
    metadata:
      author: Microsoft
      version: "1.3.4"
    ---
    
    # Azure Prepare
    
    > **AUTHORITATIVE GUIDANCE — MANDATORY COMPLIANCE**
    >
    > This document is the **official, canonical source** for preparing applications for Azure deployment. You **MUST** follow these instructions exactly as written unless they contradict security policies given to you. When in doubt, present the conflicting instructions from this document and ask the user for explicit confirmation. Do not improvise, infer, or substitute steps.
    
    ---
    
    ## Triggers
    
    Activate this skill when user wants to:
    - Create a new application
    - Add services or components to an existing app
    - Make updates or changes to existing application
    - Modernize or migrate an application
    - Set up Azure infrastructure
    - Deploy to Azure or host on Azure
    - Create and deploy to Azure (including Terraform-based deployment requests)
    
    ## Rules
    
    1. **Plan first — MANDATORY** — You MUST physically write an initial `.azure/deployment-plan.md` **skeleton in the workspace root directory** (not the session-state folder) **as your very first action** — before any code generation or execution begins. Write the skeleton immediately, then populate it progressively as Phase 1 analysis and research unfold; finalize it with all decisions at Phase 1 Step 6. This file must exist on disk throughout. azure-validate and azure-deploy depend on it and will fail without it. Do not skip or defer this step.
    2. **Get approval** — Present plan to user before execution
    3. **Research before generating** — Load references and invoke related skills
    4. **Update plan progressively** — Mark steps complete as you go
    5. **Validate before deploy** — Invoke azure-validate before azure-deploy
    6. **Confirm Azure context** — Use `ask_user` for subscription and location per [Azure Context](references/azure-context.md)
    7. ❌ **Destructive actions require `ask_user`** — [Global Rules](references/global-rules.md)
    8. ⛔ **NEVER delete user project or workspace directories** — When adding features to an existing project, MODIFY existing files. `azd init -t <template>` is for NEW projects only; do NOT run `azd init -t` in an existing workspace. Plain `azd init` (without a template argument) may be used in existing workspaces when appropriate. File deletions within a project (e.g., removing build artifacts or temp files) are permitted when appropriate, but NEVER delete the user's project or workspace directory itself. See [Global Rules](references/global-rules.md).
    9. **Scope: preparation only** — This skill generates infrastructure code and configuration files. Deployment execution (`azd up`, `azd deploy`, `terraform apply`) is handled by the **azure-deploy** skill, which provides built-in error recovery and deployment verification.
    10. ⛔ **SQL Server Bicep: NEVER generate `administratorLogin` or `administratorLoginPassword`** — not in direct properties, not in conditional/ternary branches, not anywhere in the file. Always use Entra-only authentication (`azureADOnlyAuthentication: true`) unconditionally. See [references/services/sql-database/bicep.md](references/services/sql-database/bicep.md).
    11. **Remove stale template IaC after conversion** — If you converted Bicep templates from the selected `azd` template into Terraform templates, remove the Bicep templates that were introduced by that `azd` template and are now fully replaced by Terraform equivalents. Do not remove user-authored Bicep files. Only remove those template-provided Bicep files after the Terraform IaC is complete and Terraform has been selected as the deployment path. Before handing off to azure-validate skill, keep only the IaC templates required by the chosen deployment path.
    
    ---
    
    ## ❌ PLAN-FIRST WORKFLOW — MANDATORY
    
    > **YOU MUST CREATE A PLAN BEFORE DOING ANY WORK**
    >
    > 1. **STOP** — Do not generate any code, infrastructure, or configuration yet
    > 2. **CREATE SKELETON** - Write an initial `.azure/deployment-plan.md` skeleton to disk **immediately** (before any code generation or execution begins), then populate it progressively as Phase 1 steps 1-5 reveal details; finalize it at Step 6
    > 3. **CONFIRM** — Present the completed plan to the user and get approval
    > 4. **EXECUTE** — Only after approval, execute the plan step by step
    >
    > The `.azure/deployment-plan.md` file is the **source of truth** for this workflow and for azure-validate and azure-deploy skills. Without it, those skills will fail.
    >
    > ⚠️ **CRITICAL: `.azure/deployment-plan.md` must be WRITTEN TO DISK inside the workspace root** (e.g., `<workspace-root>/.azure/deployment-plan.md`), not in the session-state folder. Use a file-write tool to create this file. This is the deployment plan artifact read by azure-validate and azure-deploy. **You MUST create this file — do not proceed without it.** 
    > ⚠️ **CRITICAL: You must create the file with the name `.azure/deployment-plan.md` as is**. You must not use other names such as `.azure/plan.md`.
    >
    > ⛔ **Critical:** Skipping the plan file creation will cause azure-validate and azure-deploy to fail. This requirement has no exceptions.
    
    ---
    
    ## ❌ STEP 0: Specialized Technology Check — MANDATORY FIRST ACTION
    
    **BEFORE starting Phase 1**, check if the user's prompt OR workspace codebase matches a specialized technology that has a dedicated skill with tested templates. If matched, **invoke that skill FIRST** — then resume azure-prepare for validation and deployment.
    
    ### Check 1: Prompt keywords
    
    | Prompt keywords | Invoke FIRST |
    |----------------|-------------|
    | Python + App Service (e.g., "deploy Python to App Service", "Flask on Azure App Service", "publish Python web app to App Service") | **python-appservice-deploy** |
    | Lambda, AWS Lambda, migrate AWS, migrate GCP, Lambda to Functions, migrate from AWS, migrate from GCP | **azure-cloud-migrate** |
    | Azure Functions, function app, serverless function, timer trigger, HTTP trigger, func new | Stay in **azure-prepare** — prefer Azure Functions templates in Step 4; for plan choice/cold starts see [hosting-plans.md](references/services/functions/hosting-plans.md) and [cold-start.md](references/services/functions/cold-start.md) |
    | APIM, API Management, API gateway, deploy APIM | Stay in **azure-prepare** — see [APIM Deployment Guide](references/apim.md) |
    | AI gateway, AI gateway policy, AI gateway backend, AI gateway configuration | **azure-aigateway** |
    | workflow, orchestration, multi-step, pipeline, fan-out/fan-in, saga, long-running process, durable, order processing | Stay in **azure-prepare** — select **durable** recipe in Step 4. **MUST** load [durable.md](references/services/functions/durable.md), [DTS reference](references/services/durable-task-scheduler/README.md), and [DTS Bicep patterns](references/services/durable-task-scheduler/bicep.md). |
    
    > ⚠️ Check the user's **prompt text** — not just existing code. Critical for greenfield projects with no codebase to scan. See [full routing table](references/specialized-routing.md).
    
    After the specialized skill completes, **resume azure-prepare** at Phase 1 Step 4 (Select Recipe) for remaining infrastructure, validation, and deployment.
    
    ---
    
    ## Phase 1: Planning (BLOCKING — Complete Before Any Execution)
    
    Create `.azure/deployment-plan.md` by completing these steps. Do NOT generate any artifacts until the plan is approved.
    
    | # | Action | Reference |
    |---|--------|-----------|
    | 0 | If the prompt matches a specialized technology with a dedicated skill, invoke that skill first | [specialized-routing.md](references/specialized-routing.md) |
    | 1 | **Analyze Workspace** — Determine mode: NEW, MODIFY, or MODERNIZE | [analyze.md](references/analyze.md) |
    | 2 | **Gather Requirements** — Classification, scale, budget | [requirements.md](references/requirements.md) |
    | 3 | **Scan Codebase** — Identify components, technologies, dependencies | [scan.md](references/scan.md) |
    | 4 | **Select Recipe** — Choose AZD (default), AZCLI, Bicep, or Terraform | [recipe-selection.md](references/recipe-selection.md) |
    | 5 | **Plan Architecture** — Select stack + map components to Azure services | [architecture.md](references/architecture.md) |
    | 6 | **Finalize Plan (MANDATORY)** - Use a file-write tool to finalize `.azure/deployment-plan.md` with all decisions from steps 1-5. Update the skeleton written at the start of Phase 1 with the complete content. The file must be fully populated before you present the plan to the user. | [plan-template.md](references/plan-template.md) |
    | 7 | **Present Plan** — Show plan to user and ask for approval | `.azure/deployment-plan.md` |
    | 8 | **Destructive actions require `ask_user`** | [Global Rules](references/global-rules.md) |
    
    ---
    
    > **❌ STOP HERE** — Do NOT proceed to Phase 2 until the user approves the plan.
    
    ---
    
    ## Phase 2: Execution (Only After Plan Approval)
    
    Execute the approved plan. Update `.azure/deployment-plan.md` status after each step.
    
    | # | Action | Reference |
    |---|--------|-----------|
    | 1 | **Research Components** — Load service references + invoke related skills | [research.md](references/research.md) |
    | 2 | **Confirm Azure Context** — Detect and confirm subscription + location and check the resource provisioning limit | [Azure Context](references/azure-context.md) |
    | 3 | **Generate Artifacts** — Create infrastructure and configuration files | [generate.md](references/generate.md) |
    | 4 | **Harden Security** — Apply security best practices | [security.md](references/security.md) |
    | 5 | **Functional Verification** — Verify the app works (UI + backend), locally if possible | [functional-verification.md](references/functional-verification.md) |
    | 6 | **⛔ Update Plan (MANDATORY before hand-off)** — Use the `edit` tool to change the Status in `.azure/deployment-plan.md` to `Ready for Validation`. You **MUST** complete this edit **BEFORE** invoking azure-validate. Do NOT skip this step. | `.azure/deployment-plan.md` |
    | 7 | **⛔ MANDATORY Hand Off** — Invoke **azure-validate** skill. Your preparation work is done. Do NOT run `azd up`, `azd deploy`, or any deployment command directly — all deployment execution is handled by azure-deploy after azure-validate completes. **PREREQUISITE:** Step 6 must be completed first — `.azure/deployment-plan.md` status must say `Ready for Validation`. | — |
    
    ---
    
    ## Outputs
    
    | Artifact | Location |
    |----------|----------|
    | **Plan** | `.azure/deployment-plan.md` |
    | Infrastructure | `./infra/` |
    | AZD Config | `azure.yaml` (AZD only) |
    | Dockerfiles | `src/<component>/Dockerfile` |
    
    ---
    
    ## SDK Quick References
    
    - **Azure Developer CLI**: [azd](references/sdk/azd-deployment.md)
    - **Azure Identity**: [Python](references/sdk/azure-identity-py.md) | [.NET](references/sdk/azure-identity-dotnet.md) | [TypeScript](references/sdk/azure-identity-ts.md) | [Java](references/sdk/azure-identity-java.md)
    - **App Configuration**: [Python](references/sdk/azure-appconfiguration-py.md) | [TypeScript](references/sdk/azure-appconfiguration-ts.md) | [Java](references/sdk/azure-appconfiguration-java.md)
    
    ---
    
    ## Next
    
    > **⛔ MANDATORY NEXT STEP — DO NOT SKIP**
    >
    > After completing preparation, you **MUST** invoke **azure-validate** before any deployment attempt. Do NOT skip validation. Do NOT go directly to azure-deploy. Do NOT run `azd up` or any deployment command directly. The workflow is:
    >
    > `azure-prepare` → `azure-validate` → `azure-deploy`
    >
    > **⛔ BEFORE invoking azure-validate**, you MUST use the `edit` tool to update `.azure/deployment-plan.md` status to `Ready for Validation`. If the plan status has not been updated, the validation will fail.
    >
    > This applies to ALL deployment scenarios including containerized apps, Container Apps, App Service, Azure Functions, static sites, and any other Azure target. No exceptions.
    >
    > Skipping validation leads to deployment failures. Be patient and follow the complete workflow for the highest success outcome.
    
    **→ Update plan status to `Ready for Validation`, then invoke azure-validate**
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related