Back to skills
SkillHub ClubRun DevOpsFull StackDevOps

azure-deploy

Execute Azure deployments for ALREADY-PREPARED applications that have existing .azure/plan.md and infrastructure files. DO NOT use this skill when the user asks to CREATE a new application — use azure-prepare instead. This skill runs azd up, azd deploy, terraform apply, and az deployment commands with built-in error recovery. Requires .azure/plan.md from azure-prepare and validated status from azure-validate. WHEN: "run azd up", "run azd deploy", "execute deployment", "push to production", "push to cloud", "go live", "ship it", "bicep deploy", "terraform apply", "publish to Azure", "launch on Azure". DO NOT USE WHEN: "create and deploy", "build and deploy", "create a new app", "set up infrastructure", "create and deploy to Azure using Terraform" — use azure-prepare for these.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
426
Hot score
99
Updated
March 20, 2026
Overall rating
C3.5
Composite score
3.5
Best-practice grade
B75.6

Install command

npx @skill-hub/cli install microsoft-azure-skills-azure-deploy

Repository

microsoft/azure-skills

Skill path: .github/plugins/azure-skills/skills/azure-deploy

Execute Azure deployments for ALREADY-PREPARED applications that have existing .azure/plan.md and infrastructure files. DO NOT use this skill when the user asks to CREATE a new application — use azure-prepare instead. This skill runs azd up, azd deploy, terraform apply, and az deployment commands with built-in error recovery. Requires .azure/plan.md from azure-prepare and validated status from azure-validate. WHEN: "run azd up", "run azd deploy", "execute deployment", "push to production", "push to cloud", "go live", "ship it", "bicep deploy", "terraform apply", "publish to Azure", "launch on Azure". DO NOT USE WHEN: "create and deploy", "build and deploy", "create a new app", "set up infrastructure", "create and deploy to Azure using Terraform" — use azure-prepare for these.

Open repository

Best for

Primary workflow: Run DevOps.

Technical facets: Full Stack, DevOps.

Target audience: everyone.

License: MIT.

Original source

Catalog source: SkillHub Club.

Repository owner: microsoft.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install azure-deploy into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/microsoft/azure-skills before adding azure-deploy to shared team environments
  • Use azure-deploy for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: azure-deploy
description: "Execute Azure deployments for ALREADY-PREPARED applications that have existing .azure/plan.md and infrastructure files. DO NOT use this skill when the user asks to CREATE a new application — use azure-prepare instead. This skill runs azd up, azd deploy, terraform apply, and az deployment commands with built-in error recovery. Requires .azure/plan.md from azure-prepare and validated status from azure-validate. WHEN: \"run azd up\", \"run azd deploy\", \"execute deployment\", \"push to production\", \"push to cloud\", \"go live\", \"ship it\", \"bicep deploy\", \"terraform apply\", \"publish to Azure\", \"launch on Azure\". DO NOT USE WHEN: \"create and deploy\", \"build and deploy\", \"create a new app\", \"set up infrastructure\", \"create and deploy to Azure using Terraform\" — use azure-prepare for these."
license: MIT
metadata:
  author: Microsoft
  version: "1.0.5"
---

# Azure Deploy

> **AUTHORITATIVE GUIDANCE — MANDATORY COMPLIANCE**
>
> **PREREQUISITE**: The **azure-validate** skill **MUST** be invoked and completed with status `Validated` BEFORE executing this skill.

> **⛔ STOP — PREREQUISITE CHECK REQUIRED**
> Before proceeding, verify BOTH prerequisites are met:
>
> 1. **azure-prepare** was invoked and completed → `.azure/plan.md` exists
> 2. **azure-validate** was invoked and passed → plan status = `Validated`
>
> If EITHER is missing, **STOP IMMEDIATELY**:
> - No plan? → Invoke **azure-prepare** skill first
> - Status not `Validated`? → Invoke **azure-validate** skill first
>
> **⛔ DO NOT MANUALLY UPDATE THE PLAN STATUS**
>
> You are **FORBIDDEN** from changing the plan status to `Validated` yourself. Only the **azure-validate** skill is authorized to set this status after running actual validation checks. If you update the status without running validation, deployments will fail.
>
> **DO NOT ASSUME** the app is ready. **DO NOT SKIP** validation to save time. Skipping steps causes deployment failures. The complete workflow ensures success:
>
> `azure-prepare` → `azure-validate` → `azure-deploy`

## Triggers

Activate this skill when user wants to:
- Execute deployment of an already-prepared application (azure.yaml and infra/ exist)
- Push updates to an existing Azure deployment
- Run `azd up`, `azd deploy`, or `az deployment` on a prepared project
- Ship already-built code to production
- Deploy an application that already includes API Management (APIM) gateway infrastructure

> **Scope**: This skill executes deployments. It does not create applications, generate infrastructure code, or scaffold projects. For those tasks, use **azure-prepare**.

> **APIM / AI Gateway**: Use this skill to deploy applications whose APIM/AI gateway infrastructure was already created during **azure-prepare**. For creating or changing APIM resources, see [APIM deployment guide](https://learn.microsoft.com/azure/api-management/get-started-create-service-instance). For AI governance policies, invoke **azure-aigateway** skill.

## Rules

1. Run after azure-prepare and azure-validate
2. `.azure/plan.md` must exist with status `Validated`
3. **Pre-deploy checklist required** — [Pre-Deploy Checklist](references/pre-deploy-checklist.md)
4. ⛔ **Destructive actions require `ask_user`** — [global-rules](references/global-rules.md)
5. **Scope: deployment execution only** — This skill owns execution of `azd up`, `azd deploy`, `terraform apply`, and `az deployment` commands. These commands are run through this skill's error recovery and verification pipeline.

---

## Steps

| # | Action | Reference |
|---|--------|-----------|
| 1 | **Check Plan** — Read `.azure/plan.md`, verify status = `Validated` AND **Validation Proof** section is populated | `.azure/plan.md` |
| 2 | **Pre-Deploy Checklist** — MUST complete ALL steps | [Pre-Deploy Checklist](references/pre-deploy-checklist.md) |
| 3 | **Load Recipe** — Based on `recipe.type` in `.azure/plan.md` | [recipes/README.md](references/recipes/README.md) |
| 4 | **Execute Deploy** — Follow recipe steps | Recipe README |
| 5 | **Post-Deploy** — Configure SQL managed identity and apply EF migrations if applicable | [Post-Deployment](references/recipes/azd/post-deployment.md) |
| 6 | **Handle Errors** — See recipe's `errors.md` | — |
| 7 | **Verify Success** — Confirm deployment completed and endpoints are accessible | [Verification](references/recipes/azd/verify.md) |

> **⛔ VALIDATION PROOF CHECK**
>
> When checking the plan, verify the **Validation Proof** section (Section 7) contains actual validation results with commands run and timestamps. If this section is empty, validation was bypassed — invoke **azure-validate** skill first.

## 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)

## MCP Tools

| Tool | Purpose |
|------|---------|
| `mcp_azure_mcp_subscription_list` | List available subscriptions |
| `mcp_azure_mcp_group_list` | List resource groups in subscription |
| `mcp_azure_mcp_azd` | Execute AZD commands |

## References

- [Troubleshooting](references/troubleshooting.md) - Common issues and solutions
- [Post-Deployment Steps](references/recipes/azd/post-deployment.md) - SQL + EF Core setup


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/pre-deploy-checklist.md

```markdown
# Pre-Deployment Checklist

> **CRITICAL**: Before running ANY provisioning commands, you MUST complete this checklist IN ORDER.
>
> ⛔ **DO NOT** run `azd up` until ALL steps are complete. Trial-and-error wastes time and creates orphan resources.

## Step 1: Check Current Subscription

Use the Azure MCP tool to get current subscription:

```
mcp_azure_mcp_subscription_list
```

**CLI fallback:**
```bash
az account show --query "{name:name, id:id}" -o json
```

## Step 2: Prompt User for Subscription

**You MUST use `ask_user`** to confirm the subscription. Find the default subscription (marked `isDefault: true`) from Step 1 results and present it as the recommended choice.

✅ **Correct — show actual name and ID as a choice:**
```
ask_user(
  question: "Which Azure subscription would you like to deploy to?",
  choices: [
    "Use current: <subscription-name> (<subscription-id>) (Recommended)",
    "Let me specify a different subscription"
  ]
)
```

❌ **Wrong — never use freeform input for subscription:**
```
ask_user(
  question: "Which Azure subscription should I deploy to? I'll need the subscription name or ID."
)
```

## Step 3: Create AZD Environment FIRST

> ⚠️ **MANDATORY** — Create the environment BEFORE setting any variables or running `azd up`.
>
> ⛔ **DO NOT** manually create `.azure/` folder with `mkdir` or `New-Item`. Let `azd` create it.

**For new projects (no azure.yaml):**
```bash
azd init -e <environment-name>
```

**For existing projects (azure.yaml exists):**
```bash
azd env new <environment-name>
```

Both commands create:
- `.azure/<env-name>/` folder with config files
- Set the environment as default

The environment name becomes part of the resource group name (`rg-<env-name>`).

## Step 4: Check if Resource Group Already Exists

> ⛔ **CRITICAL** — Skip this and you'll hit "Invalid resource group location" errors.

Use the Azure MCP tool to list resource groups:

```
mcp_azure_mcp_group_list
  subscription: <subscription-id>
```

Then check if `rg-<environment-name>` exists in the results.

**CLI fallback:**
```bash
az group show --name rg-<environment-name> --query "{location:location}" -o json 2>&1
```

**If RG exists:**
- Use `ask_user` to offer choices:
  1. Use existing RG location (show the location)
  2. Choose a different environment name
  3. Delete the existing RG and start fresh

**If RG doesn't exist:** Proceed to location selection.

## Step 5: Check for Tag Conflicts (AZD only)

> ⚠️ AZD uses `azd-service-name` tags to find deployment targets **within the target resource group**. Multiple resources with the same tag in the same RG cause failures. Tags in other RGs are fine.

```bash
az resource list --resource-group rg-<env-name> --tag azd-service-name=<service-name> --query "[].name" -o table
```

Check for each service in `azure.yaml`. If duplicates exist **in the target RG**:

1. **Preferred — Fresh environment**: Run `azd env new <new-name>` and restart from Step 4. Non-destructive, no user confirmation needed, avoids orphan risks.
2. **Alternative — Delete conflicts**: Use `ask_user` to confirm deletion of old resources (required by global rules).

## Step 6: Prompt User for Location

**You MUST use `ask_user`** with regions that support ALL services in the architecture.

See [Region Availability](region-availability.md) for service-specific limitations.

## Step 7: Set Environment Variables

> ⚠️ **Set ALL variables BEFORE running `azd up`** — not during error recovery.

Environment should already be configured during **azure-validate**. Run `azd env get-values` to confirm.

Verify settings:
```bash
azd env get-values
```

## Step 8: Only NOW Run Deployment

```bash
azd up --no-prompt
```

---

## Quick Reference: Correct AZD Sequence

```bash
# 1. Create environment FIRST
azd env new myapp-dev

# 2. Set subscription
azd env set AZURE_SUBSCRIPTION_ID 25fd0362-...

# 3. Set location (after checking RG doesn't conflict)
azd env set AZURE_LOCATION westus2

# 4. Verify
azd env get-values

# 5. Deploy
azd up --no-prompt
```

## Common Mistakes to Avoid

| ❌ Wrong | ✅ Correct |
|----------|-----------|
| `azd up --location eastus2` | `azd env set AZURE_LOCATION eastus2` then `azd up` |
| Running `azd up` without environment | `azd env new <name>` first |
| Assuming location without checking RG | Check `az group show` before choosing |
| Ignoring tag conflicts in target RG | Check `az resource list --resource-group rg-<env>` before deploy |

---

## Service-Specific Checks

### Durable Functions — Verify DTS Backend

> **⛔ MANDATORY**: If the plan includes Durable Functions, verify infrastructure uses **Durable Task Scheduler** (DTS), NOT Azure Storage.

Check that `infra/` Bicep files contain:
- `Microsoft.DurableTask/schedulers` resource
- `Microsoft.DurableTask/schedulers/taskHubs` child resource
- `Durable Task Data Contributor` RBAC role assignment
- `DURABLE_TASK_SCHEDULER_CONNECTION_STRING` app setting

If any are missing, **STOP** and invoke **azure-prepare** to regenerate with the durable recipe.

---

## Non-AZD Deployments

**For Azure CLI / Bicep:**
```bash
az account set --subscription <subscription-id-or-name>
# Pass location as parameter: --location <location>
```

**For Terraform:**
```bash
az account set --subscription <subscription-id-or-name>
# Set in terraform.tfvars or -var="location=<location>"
```

```

### references/global-rules.md

```markdown
# 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

---

## Rule 2: Never Assume Subscription or Location

⛔ **ALWAYS use `ask_user`** to confirm:
- Azure subscription (show actual name and ID)
- Azure region/location

See [Pre-Deploy Checklist](pre-deploy-checklist.md).

```

### references/recipes/README.md

```markdown
# Recipes

Deployment recipes for different infrastructure approaches.

| Recipe | When to Use |
|--------|-------------|
| [AZD](azd/README.md) | Projects using Azure Developer CLI |
| [AZCLI](azcli/README.md) | Projects using Azure CLI scripts |
| [Bicep](bicep/README.md) | Projects using Bicep templates |
| [Terraform](terraform/README.md) | Projects using Terraform |
| [CI/CD](cicd/README.md) | Pipeline-based deployments |

```

### references/recipes/azd/post-deployment.md

```markdown
# Post-Deployment Steps

Execute critical post-deployment configuration after infrastructure provisioning completes.

> ⚠️ **Run AFTER `azd up` or `azd provision` completes successfully**

## When to Apply

Post-deployment steps are required when your deployment includes:

| Scenario | Required Actions |
|----------|-----------------|
| **ASP.NET Core + Azure SQL + Managed Identity** | Grant managed identity SQL access, apply EF migrations |
| **App Service + Azure SQL + Entra auth** | Grant App Service identity database permissions |
| **Container Apps + SQL Database** | Configure managed identity access, run migrations |

## ASP.NET Core + EF Core + Azure SQL

Complete workflow for apps using Entity Framework with Azure SQL Database.

### Prerequisites

- `azd up` or `azd provision` completed successfully
- App Service or Container App has system-assigned managed identity enabled
- Azure SQL Server configured with Entra ID admin
- EF Core project with migrations

### Step 1: Grant Managed Identity SQL Access

Grant the App Service or Container App's managed identity permissions on the SQL database.

See [SQL Managed Identity Access](sql-managed-identity.md) for detailed SQL scripts and examples.

**Quick Template:**

```bash
# Get the app identity name from azd
eval $(azd env get-values)
APP_NAME=$SERVICE_API_NAME  # or SERVICE_WEB_NAME

# Connect as Entra admin and grant permissions
# See sql-managed-identity.md for connection patterns
```

### Step 2: Apply EF Core Migrations

Apply Entity Framework migrations to create database schema.

See [EF Core Migrations](ef-migrations.md) for deployment patterns and troubleshooting.

**Quick Options:**

| Method | Command | Use When |
|--------|---------|----------|
| **azd hook** | Add `postprovision` hook in `azure.yaml` | Automated deployments |
| **Manual** | `dotnet ef database update` | One-time or troubleshooting |
| **SQL Script** | `dotnet ef migrations script --idempotent` | Pre-generated scripts |

### Step 3: Verify Deployment

```bash
# Get app endpoint
ENDPOINT=$(azd env get-values | grep SERVICE_.*_URI | cut -d'=' -f2)

# Health check
curl -f "$ENDPOINT/health" || echo "Health check failed"

# Test database connectivity
curl -f "$ENDPOINT/api/test-db" || echo "Database connection failed"
```

**Expected Result:**
- HTTP 200 from health endpoint
- No SQL authentication errors in logs
- Application starts successfully

## Common Issues

| Error | Cause | Solution |
|-------|-------|----------|
| `Login failed for user '<token-identified principal>'` | Managed identity not granted SQL access | Follow [sql-managed-identity.md](sql-managed-identity.md) |
| `Cannot open database` | Firewall rules block access | Check SQL firewall, ensure "Allow Azure services" enabled |
| `Invalid object name` | Migrations not applied | Run EF migrations per [ef-migrations.md](ef-migrations.md) |
| `No such table` | Schema missing | Apply migrations or check connection string database name |

## Best Practices

1. **Automate with azd hooks** — Add `postprovision` hook to `azure.yaml` for repeatable deployments
2. **Use idempotent scripts** — Generate SQL with `dotnet ef migrations script --idempotent`
3. **Verify incrementally** — Test SQL access, then migrations, then endpoint
4. **Log everything** — Enable verbose logging during initial setup for troubleshooting

## References

- [SQL Managed Identity Access](sql-managed-identity.md)
- [EF Core Migrations](ef-migrations.md)
- [Verification Steps](verify.md)

```

### references/recipes/azd/verify.md

```markdown
# AZD Verification

Verify deployment success and application health.

## Step 1: Verify Resources

```bash
azd show
```

Expected output:
```
Showing deployed resources:
  Resource Group: rg-myapp-dev
  Services:
    api - Endpoint: https://api-xxxx.azurecontainerapps.io
```

## Step 2: Health Check

```bash
# Get endpoint
ENDPOINT=$(azd env get-values | grep -E "SERVICE_.*_URI|.*_ENDPOINT" | head -1 | cut -d'=' -f2)

# Test endpoint
curl -f "$ENDPOINT/health" || curl -f "$ENDPOINT"
```

Expected: HTTP 200 response.

## Step 3: Post-Deployment Verification (if applicable)

For deployments with Azure SQL Database and managed identity:

### Verify SQL Access

```bash
# Load environment variables
eval $(azd env get-values)

# Check managed identity user exists in database
az sql db query \
  --server "$SQL_SERVER" \
  --database "$SQL_DATABASE" \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --auth-mode ActiveDirectoryDefault \
  --queries "SELECT name, type_desc FROM sys.database_principals WHERE type = 'E'"
```

**Expected:** Should list the App Service or Container App managed identity.

### Verify Database Schema

For EF Core applications:

```bash
# Check tables exist
az sql db query \
  --server "$SQL_SERVER" \
  --database "$SQL_DATABASE" \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --auth-mode ActiveDirectoryDefault \
  --queries "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"
```

**Expected:** Should list application tables (not just `__EFMigrationsHistory`).

### Check Application Logs

```bash
# For App Service
az webapp log tail --name <app-name> --resource-group <resource-group>

# For Container Apps
az containerapp logs show --name <app-name> --resource-group <resource-group> --follow
```

**Look for:**
- ✅ No SQL authentication errors
- ✅ Successful database connection
- ✅ Application started successfully

## Common Issues

| Symptom | Cause | Fix |
|---------|-------|-----|
| HTTP 500 on startup | SQL authentication failure | See [sql-managed-identity.md](sql-managed-identity.md) |
| "Invalid object name" errors | Migrations not applied | See [ef-migrations.md](ef-migrations.md) |
| Endpoint not accessible | Service still starting | Wait 1-2 minutes, retry |
| Health check fails | Application error | Check logs with `az webapp log tail` |

## References

- [Post-Deployment Steps](post-deployment.md)
- [SQL Managed Identity Access](sql-managed-identity.md)
- [EF Core Migrations](ef-migrations.md)

```

### references/sdk/azd-deployment.md

```markdown
# 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

```

### references/sdk/azure-identity-py.md

```markdown
# 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

```

### references/sdk/azure-identity-dotnet.md

```markdown
# 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

```

### references/sdk/azure-identity-ts.md

```markdown
# 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

```

### references/sdk/azure-identity-java.md

```markdown
# 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

```

### references/troubleshooting.md

```markdown
# Troubleshooting

This reference covers common errors encountered during Azure deployment with `azd` and how to resolve them.

## Language Not Supported

**Symptom:** Error message like `ERROR: error executing step command 'package --all': initializing service 'web', getting framework service: language 'html' is not supported by built-in framework services`

**Cause:** Using unsupported language value in `azure.yaml`. Neither `html` nor `static` are valid language types for azd.

**Solution:**

For pure HTML/CSS static sites, omit the `language` field:

```yaml
services:
  web:
    project: ./src/web   # or . for root
    host: staticwebapp
    dist: .              # relative to project path (only works when project != root)
```

Valid language values: `python`, `js`, `ts`, `java`, `dotnet`, `go` (or omit for staticwebapp without build)

## SWA Project Path Issues

**Symptom:** Deployment fails, gets stuck in "Uploading", or shows default Azure page

**Cause:** Incorrect `project` or `dist` configuration.

**Solution:** Match configuration to your project layout:

| Layout | `project` | `dist` |
|--------|-----------|--------|
| Static files in root | `.` | `public` (put files in public/ folder) |
| Framework in root | `.` | `dist`/`build`/`out` |
| Static in subfolder | `./src/web` | `.` |
| Framework in subfolder | `./src/web` | `dist`/`build`/`out` |

> **SWA CLI Limitation:** When `project: .`, you **cannot** use `dist: .`. Put static files in a `public/` folder instead.

## SWA Dist Not Found

**Symptom:** Error like `dist folder not found` or empty deployment

**Cause:** The `dist` path doesn't exist or build didn't run.

**Solution:**
1. For framework apps: ensure `language: js` is set to trigger build
2. Verify `dist` value matches your framework's output folder
3. For pure static in root: put files in `public/` folder and use `dist: public`
4. For pure static in subfolder: use `dist: .`

## Service Resource Not Found

**Symptom:** Error message like `ERROR: getting target resource: resource not found: unable to find a resource tagged with 'azd-service-name: web'`

**Cause:** The Azure resource is missing the `azd-service-name` tag that azd uses to link services defined in `azure.yaml` to deployed infrastructure.

**Solution:**

Add the tag to your bicep resource definition:

```bicep
resource staticWebApp 'Microsoft.Web/staticSites@2022-09-01' = {
  name: name
  location: location
  tags: union(tags, { 'azd-service-name': 'web' })  // Must match service name in azure.yaml
  // ... rest of config
}
```

After updating, run `azd provision` to apply the tag, then `azd deploy`.

## Location Not Available for Resource Type

**Symptom:** Error message like `LocationNotAvailableForResourceType: The provided location 'westus3' is not available for resource type 'Microsoft.Web/staticSites'`

**Cause:** Azure Static Web Apps is not available in all regions.

**Solution:**

Change to a supported region:

```bash
azd env set AZURE_LOCATION westus2
```

Available regions for Static Web Apps: `westus2`, `centralus`, `eastus2`, `westeurope`, `eastasia`

## Missing Infrastructure Parameters

**Symptom:** Error message like `ERROR: prompting for value: no default response for prompt 'Enter a value for the '<param>' infrastructure parameter:'`

**Cause:** A Bicep parameter exists in your template but no corresponding environment variable is set.

**Example:** The `infra/main.bicep` has a parameter like:
```bicep
@description('SKU for the storage account.')
param storageAccountSku string
```

**Solution:**

1. Check `infra/main.parameters.json` for an existing mapping to this parameter.

2. **If a mapping exists** (e.g., `"value": "${STORAGE_SKU}"`), ask the user for the desired value and set the environment variable:
```bash
azd env set STORAGE_SKU <user-provided-value>
```

3. **If no mapping exists**, add one to `infra/main.parameters.json`:
```json
{
  "parameters": {
    "storageAccountSku": {
      "value": "${STORAGE_SKU}"
    }
  }
}
```

Then ask the user for the desired value and set the environment variable:
```bash
azd env set STORAGE_SKU <user-provided-value>
```

During `azd provision`, azd will substitute `${STORAGE_SKU}` with the value from the environment and will pass it to Bicep.

**Reference:** [Use environment variables in infrastructure files](https://learn.microsoft.com/azure/developer/azure-developer-cli/manage-environment-variables?tabs=bash#use-environment-variables-in-infrastructure-files)

## .NET Aspire Limited Mode - Missing Environment Variables

**Symptom:** When deploying .NET Aspire projects with azd, `azd provision` succeeds but `azd deploy` fails with errors about missing container registry or managed identity environment variables.

**Cause:** .NET Aspire projects can use azd in "limited mode" where infrastructure is generated in-memory without creating an explicit `infra/` folder on disk. In this mode, `azd provision` creates Azure resources (Container Registry, Managed Identity, Container Apps Environment, etc.) but doesn't automatically populate certain environment variables that `azd deploy` needs.

**Common missing variables:**
- `AZURE_CONTAINER_REGISTRY_ENDPOINT` — ACR login server URL
- `AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID` — Managed identity resource ID
- `MANAGED_IDENTITY_CLIENT_ID` — Managed identity client ID

**Solution:**

After `azd provision` completes, manually populate the missing environment variables:

```bash
# Get your resource group name first
azd env get-values

# Set the container registry endpoint
azd env set AZURE_CONTAINER_REGISTRY_ENDPOINT $(az acr list --resource-group <resource-group-name> --query "[0].loginServer" -o tsv)

# Set the managed identity resource ID
azd env set AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID $(az identity list --resource-group <resource-group-name> --query "[0].id" -o tsv)

# Set the managed identity client ID
azd env set MANAGED_IDENTITY_CLIENT_ID $(az identity list --resource-group <resource-group-name> --query "[0].clientId" -o tsv)
```

Then retry deployment:
```bash
azd deploy --no-prompt
```

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### references/auth-best-practices.md

```markdown
# 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)

```

### references/recipes/azcli/README.md

```markdown
# Azure CLI Deploy Recipe

Deploy to Azure using Azure CLI.

## Prerequisites

- `az` CLI installed → Run `mcp_azure_mcp_extension_cli_install` with `cli-type: az` if needed
- `.azure/plan.md` exists with status `Validated`
- Bicep/ARM templates exist in `infra/`
- **Subscription and location confirmed** → See [Pre-Deploy Checklist](../../pre-deploy-checklist.md)

## Workflow

| Step | Task | Command |
|------|------|---------|
| 1 | **[Pre-deploy checklist](../../pre-deploy-checklist.md)** | Confirm subscription/location with user |
| 2 | Deploy infrastructure | `az deployment sub create` |
| 3 | Deploy application | Service-specific commands |
| 4 | Verify | `az resource list` |

## Infrastructure Deployment

### Subscription-Level (Recommended)

```bash
az deployment sub create \
  --location eastus2 \
  --template-file ./infra/main.bicep \
  --parameters environmentName=dev
```

### Resource Group Level

```bash
az group create --name rg-myapp-dev --location eastus2

az deployment group create \
  --resource-group rg-myapp-dev \
  --template-file ./infra/main.bicep \
  --parameters environmentName=dev
```

## Application Deployment

### Container Apps

```bash
az containerapp update \
  --name <app-name> \
  --resource-group <rg-name> \
  --image <acr-name>.azurecr.io/myapp:latest
```

### App Service

```bash
az webapp deploy \
  --name <app-name> \
  --resource-group <rg-name> \
  --src-path ./publish.zip
```

### Azure Functions

```bash
func azure functionapp publish <function-app-name>
```

## References

- [Verification steps](./verify.md)
- [Error handling](./errors.md)

```

### references/recipes/azcli/errors.md

```markdown
# Azure CLI Errors

| Error | Resolution |
|-------|------------|
| Not authenticated | `az login` |
| Subscription not found | `az account list` |
| Deployment failed | `az deployment sub show --name <name>` |
| Template error | `az deployment sub validate` |
| Permission denied | Verify RBAC roles |
| Quota exceeded | Request increase or change region |

## Cleanup (DESTRUCTIVE)

```bash
az group delete --name <rg-name> --yes
```

⚠️ Permanently deletes ALL resources in the group.

```

### references/recipes/azcli/verify.md

```markdown
# Azure CLI Verification

```bash
az resource list --resource-group <rg-name> --output table
```

## Health Check

```bash
curl -s https://<endpoint>/health | jq .
```

## Container Apps

```bash
az containerapp revision list \
  --name <app-name> \
  --resource-group <rg-name> \
  --query "[].{name:name, active:properties.active}" \
  --output table
```

## App Service

```bash
az webapp show \
  --name <app-name> \
  --resource-group <rg-name> \
  --query "{state:state, hostNames:hostNames}"
```

```

### references/recipes/azd/README.md

```markdown
# AZD Deploy Recipe

Deploy to Azure using Azure Developer CLI (azd).

> 💡 **Note:** azd supports both Bicep and Terraform as IaC providers. The deployment workflow is identical regardless of which you use.

## Prerequisites

- `azd` CLI installed → Run `mcp_azure_mcp_extension_cli_install` with `cli-type: azd` if needed
- `.azure/plan.md` exists with status `Validated`
- `azure.yaml` exists and validated
- Infrastructure files exist (Bicep: `infra/main.bicep`, Terraform: `infra/*.tf`)
- **AZD environment configured** → Done in azure-validate
- **Subscription and location confirmed** → See [Pre-deploy Checklist](../../pre-deploy-checklist.md)

## Workflow

| Step | Task | Command |
|------|------|---------|
| 1 | **Verify environment** | `azd env get-values` — Confirm AZURE_SUBSCRIPTION_ID and AZURE_LOCATION set |
| 2 | **Deploy** | `azd up --no-prompt` |
| 3 | **Post-Deploy** | [Post-Deployment Steps](post-deployment.md) — If using SQL + managed identity |
| 4 | **Verify** | See [Verification](verify.md) |

> ⚠️ **Important:** For .NET Aspire projects or projects using azd "limited mode" (no explicit `infra/` folder), verify that `azd provision` populated all required environment variables. If `azd deploy` fails with errors about missing `AZURE_CONTAINER_REGISTRY_ENDPOINT`, `AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID`, or `MANAGED_IDENTITY_CLIENT_ID`, see [Error Handling](errors.md#missing-container-registry-variables) for the resolution.

## Common Mistakes

| ❌ Wrong | Why It Fails |
|----------|-------------|
| `azd up --location eastus2` | `--location` is not a valid flag for `azd up` |
| `azd up` without `azd env new` | Prompts for input, fails with `--no-prompt` |
| `mkdir .azure` then `azd env new` | Creates env folder structure incorrectly |
| Setting AZURE_LOCATION without checking RG | "Invalid resource group location" if RG exists elsewhere |
| Ignoring `azd-service-name` tag conflicts in same RG | "found '2' resources tagged with..." error |
| `language: html` or `language: static` | Not valid - use `language: js` with `dist: .` for static sites |

## Deployment Commands

> ⚠️ `azd up` takes 5-15 min. Run with output **streamed visibly to the user** — do NOT run silently or suppress output. The user must see provisioning progress in real time.

### Full Deployment

Provisions infrastructure AND deploys application:

```bash
azd up --no-prompt
```

### Infrastructure Only

```bash
azd provision --no-prompt
```

### Application Only

Deploy code to existing infrastructure:

```bash
azd deploy --no-prompt
```

### Single Service

```bash
azd deploy api --no-prompt
```

## References

- [Pre-deploy Checklist](../../pre-deploy-checklist.md) — **REQUIRED**
- [Post-Deployment Steps](post-deployment.md) — SQL + managed identity setup
- [Azure Functions Deployment](functions-deploy.md)
- [Verification](verify.md)
- [Error Handling](errors.md)

```

### references/recipes/azd/ef-migrations.md

```markdown
# EF Core Migrations Deployment

Apply Entity Framework Core migrations to Azure SQL Database after deployment.

## Detection

EF Core projects contain `Migrations/` folder or `Microsoft.EntityFrameworkCore` package reference in `.csproj`.

```bash
find . -type d -name "Migrations" 2>/dev/null
find . -name "*.csproj" -exec grep -l "Microsoft.EntityFrameworkCore" {} \;
```

## Deployment Methods

### Method 1: azd Hook (Recommended)

Automate via `postprovision` hook in `azure.yaml`:

```yaml
hooks:
  postprovision:
    shell: sh
    run: ./scripts/apply-migrations.sh
```

**scripts/apply-migrations.sh:**

```bash
#!/bin/bash
set -e
eval $(azd env get-values)
CONNECTION_STRING="Server=tcp:${SQL_SERVER}.database.windows.net,1433;Database=${SQL_DATABASE};Authentication=Active Directory Default;Encrypt=True;"
cd src/api  # Adjust path
dotnet ef database update --connection "$CONNECTION_STRING"
```

> 💡 Make executable: `chmod +x scripts/*.sh`. For PowerShell: Use `azd env get-values | ForEach-Object` pattern.

### Method 2: SQL Script (Production)

Generate idempotent script for review before applying:

```bash
dotnet ef migrations script --idempotent --output migrations.sql
az sql db query --server "$SQL_SERVER" --database "$SQL_DATABASE" \
  --auth-mode ActiveDirectoryDefault --queries "$(cat migrations.sql)"
```

### Method 3: Application Startup (Dev Only)

⚠️ **Development only** — production should use explicit migration steps.

```csharp
// Program.cs
if (app.Environment.IsDevelopment()) {
    using var scope = app.Services.CreateScope();
    scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
}
```

## Combined Hook: SQL Access + Migrations

Combine both steps — see [sql-managed-identity.md](sql-managed-identity.md) for SQL grant commands.

```bash
#!/bin/bash
set -e
eval $(azd env get-values)

# Grant SQL access
az sql db query --server "$SQL_SERVER" --database "$SQL_DATABASE" \
  --auth-mode ActiveDirectoryDefault --queries "
    IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = '$SERVICE_API_NAME')
      CREATE USER [$SERVICE_API_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 = '$SERVICE_API_NAME'
    )
      ALTER ROLE db_datareader ADD MEMBER [$SERVICE_API_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 = '$SERVICE_API_NAME'
    )
      ALTER ROLE db_datawriter ADD MEMBER [$SERVICE_API_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_ddladmin' AND m.name = '$SERVICE_API_NAME'
    )
      ALTER ROLE db_ddladmin ADD MEMBER [$SERVICE_API_NAME];
  "

# Apply migrations
cd src/api
CONNECTION_STRING="Server=tcp:${SQL_SERVER}.database.windows.net,1433;Database=${SQL_DATABASE};Authentication=Active Directory Default;Encrypt=True;"
dotnet ef database update --connection "$CONNECTION_STRING"
```

## Prerequisites

Install EF Core tools:

```bash
dotnet tool install --global dotnet-ef
dotnet ef --version  # Verify installation
```

## Connection String

```
Server=tcp:{server}.database.windows.net,1433;Database={database};Authentication=Active Directory Default;Encrypt=True;
```

## Troubleshooting

| Error | Solution |
|-------|----------|
| Cannot open database | Check firewall rules: `az sql server firewall-rule list` |
| Login failed | Grant SQL access per [sql-managed-identity.md](sql-managed-identity.md) |
| Unable to create DbContext | Add `IDesignTimeDbContextFactory` implementation |
| Hook fails but deployment continues | Remove `|| true` to make migrations block deployment |

**DbContext Factory Example:**

```csharp
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext> {
    public ApplicationDbContext CreateDbContext(string[] args) {
        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
        var connectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") 
            ?? args.FirstOrDefault() ?? "Server=(localdb)\\mssqllocaldb;Database=MyDb;Trusted_Connection=True;";
        optionsBuilder.UseSqlServer(connectionString);
        return new ApplicationDbContext(optionsBuilder.Options);
    }
}
```

## Best Practices

- Use `--idempotent` flag for production scripts
- Version control Migrations/ folder
- Test locally before deploying
- Backup production databases before applying
- Keep migrations small and focused

## References

- [SQL Managed Identity Access](sql-managed-identity.md)
- [Post-Deployment Guide](post-deployment.md)
- [EF Core Migrations](https://learn.microsoft.com/ef/core/managing-schemas/migrations/)

```

### references/recipes/azd/errors.md

```markdown
# AZD Errors

## Deployment Runtime Errors

These errors occur **during** `azd up` execution:

| Error | Cause | Resolution |
|-------|-------|------------|
| `unknown flag: --location` | `azd up` doesn't accept `--location` | Use `azd env set AZURE_LOCATION <region>` before `azd up` |
| Provision failed | Bicep template errors | Check detailed error in output |
| Deploy failed | Build or Docker errors | Check build logs |
| Package failed | Missing Dockerfile or deps | Verify Dockerfile exists and dependencies |
| Quota exceeded | Subscription limits | Request increase or change region |
| `could not determine container registry endpoint` | Missing `AZURE_CONTAINER_REGISTRY_ENDPOINT` | See [Missing Container Registry Variables](#missing-container-registry-variables) |
| `map has no entry for key "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID"` | Missing managed identity env vars | See [Missing Container Registry Variables](#missing-container-registry-variables) |
| `map has no entry for key "MANAGED_IDENTITY_CLIENT_ID"` | Missing managed identity client ID | See [Missing Container Registry Variables](#missing-container-registry-variables) |
| `found '2' resources tagged with 'azd-service-name: <name>'` | Previous deployment left duplicate-tagged resources in same RG | **Preferred**: Create fresh env with `azd env new <new-name>`, set subscription/location, redeploy. **Alternative**: Delete conflicting resources (requires `ask_user`). |

> ℹ️ **Pre-flight validation**: Run `azure-validate` before deployment to catch configuration errors early. See [Pre-Deploy Checklist](../../pre-deploy-checklist.md).

## Missing Container Registry Variables

**Symptom:** Errors during `azd deploy` about missing container registry or managed identity environment variables:

```
ERROR: could not determine container registry endpoint, ensure 'registry' has been set in the docker options or 'AZURE_CONTAINER_REGISTRY_ENDPOINT' environment variable has been set
```

Or:

```
ERROR: failed executing template file: template: manifest template:6:14: executing "manifest template" at <.Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID>: map has no entry for key "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID"
```

Or:

```
ERROR: failed executing template file: template: manifest template:39:26: executing "manifest template" at <.Env.MANAGED_IDENTITY_CLIENT_ID>: map has no entry for key "MANAGED_IDENTITY_CLIENT_ID"
```

**Cause:** This typically occurs with .NET Aspire projects using azd "limited mode" (in-memory infrastructure generation without explicit `infra/` folder). The `azd provision` command creates the Azure Container Registry and Managed Identity resources but doesn't automatically populate the environment variables that `azd deploy` needs to reference them.

> ⚠️ **Prevention is Better:** For .NET Aspire projects, this issue should be addressed PROACTIVELY before deployment by setting up environment variables after `azd init` but before `azd up`. This avoids deployment failures entirely.

**Solution:**

After `azd provision` succeeds, manually set the missing environment variables by querying the provisioned resources:

```bash
# Get the resource group name (typically rg-{environment-name})
azd env get-values

# Set container registry endpoint
azd env set AZURE_CONTAINER_REGISTRY_ENDPOINT $(az acr list --resource-group <resource-group-name> --query "[0].loginServer" -o tsv)

# Set managed identity resource ID
azd env set AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID $(az identity list --resource-group <resource-group-name> --query "[0].id" -o tsv)

# Set managed identity client ID
azd env set MANAGED_IDENTITY_CLIENT_ID $(az identity list --resource-group <resource-group-name> --query "[0].clientId" -o tsv)
```

**PowerShell:**
```powershell
# Set container registry endpoint
azd env set AZURE_CONTAINER_REGISTRY_ENDPOINT (az acr list --resource-group <resource-group-name> --query "[0].loginServer" -o tsv)

# Set managed identity resource ID
azd env set AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID (az identity list --resource-group <resource-group-name> --query "[0].id" -o tsv)

# Set managed identity client ID
azd env set MANAGED_IDENTITY_CLIENT_ID (az identity list --resource-group <resource-group-name> --query "[0].clientId" -o tsv)
```

After setting these variables, retry the deployment:
```bash
azd deploy --no-prompt
```

> 💡 **Tip:** This issue is specific to Aspire limited mode. Manually setting these environment variables after `azd provision` is the recommended workaround.

## Retry

After fixing the issue:
```bash
azd up --no-prompt
```

## Cleanup (DESTRUCTIVE)

```bash
azd down --force --purge
```

⚠️ Permanently deletes ALL resources including databases and Key Vaults.

```

### references/recipes/azd/functions-deploy.md

```markdown
# Azure Functions Deployment

Deployment workflows for Azure Functions using AZD.

## Prerequisites

- Azure Functions project prepared with azd template
- `azure.yaml` exists and validated
- `.azure/plan.md` status = `Validated`
- Azure Functions Core Tools (optional, for local debugging or when using `func` commands outside azd workflows)

## AZD Deployment

### Full Deployment (Infrastructure + Code)

```bash
# Deploy everything
azd up --no-prompt
```

### Infrastructure Only

```bash
# Provision infrastructure without deploying code
azd provision --no-prompt
```

### Application Only

```bash
# Deploy code to existing infrastructure
azd deploy --no-prompt
```

### Preview Changes

```bash
# Preview changes before deployment
azd provision --preview
```

## Environment Configuration

### Set AZD Environment Variables

These are for azd provisioning, not application runtime:

```bash
azd env set AZURE_LOCATION eastus2
azd env set VNET_ENABLED false
```

> ⚠️ **Important**: `azd env set` sets variables for the azd provisioning process, NOT application environment variables.

## Verify Deployment

### Check Function App Status

```bash
# Show deployment details
azd show
```

## Testing HTTP Endpoints

> ⚠️ **Never use `curl -I` (HEAD) to test Azure Functions endpoints.**
>
> Azure Functions `[HttpTrigger]` with `"get"` does **not** automatically handle HEAD requests. HEAD returns 404 from the routing layer even when GET works correctly, causing false-negative results and misdirected debugging.

### ✅ DO — Use GET with output suppression

```bash
# Check status code only (GET, don't follow redirects)
curl -s -o /dev/null -w "%{http_code}" "https://<func-name>.azurewebsites.net/api/<route>"

# Get status code and redirect URL
curl -s -o /dev/null -w "Status: %{http_code}\nRedirect: %{redirect_url}" "https://<func-name>.azurewebsites.net/api/<route>"

# Verbose output showing response headers (GET)
curl -sS -D - -o /dev/null "https://<func-name>.azurewebsites.net/api/<route>"
```

### ❌ DON'T — Use HEAD requests

```bash
# DO NOT use this — returns 404 even when the function works
curl -I "https://<func-name>.azurewebsites.net/api/<route>"
```

## Monitoring

Monitor your Functions deployment through Azure Portal or use azd to view deployment status.

### Common Issues

1. **Deployment timeout**: Use `azd up` with appropriate timeout settings
2. **Missing dependencies**: Ensure package.json/requirements.txt is correct and committed
3. **Function not appearing**: Check azure.yaml service configuration
4. **Cold start issues**: Consider Premium plan configuration in Bicep templates

## CI/CD Integration

For automated deployments with azd, see [cicd/README.md](../cicd/README.md) for GitHub Actions and Azure DevOps integration.

## Data Loss Warning

> ⚠️ **CRITICAL: `azd down` Data Loss Warning**
>
> `azd down` **permanently deletes ALL resources** in the environment, including:
> - **Function Apps** with all configuration and deployment slots
> - **Storage accounts** with all blobs and files
> - **Key Vault** with all secrets (use `--purge` to bypass soft-delete)
> - **Databases** with all data (Cosmos DB, SQL, etc.)
>
> **Best practices:**
> - Always use `azd provision --preview` before `azd up`
> - Use separate environments for dev/staging/production
> - Back up important data before running `azd down`

## Next Steps

After deployment:
1. Verify functions are running
2. Test endpoints
3. Monitor Application Insights
4. Set up alerts and monitoring

```

### references/recipes/azd/sql-entra-auth.md

```markdown
# SQL Database Entra Authentication

Quick reference for Azure SQL Database Entra authentication in post-deployment scenarios.

## Prerequisites

Azure SQL Server must be configured with Entra-only authentication during provisioning. The signed-in user must be set as Entra admin:

```bicep
properties: {
  administrators: {
    administratorType: 'ActiveDirectory'
    principalType: 'User'
    login: principalName
    sid: principalId
    tenantId: subscription().tenantId
    azureADOnlyAuthentication: true
  }
}
```

## Connection Patterns

### Azure CLI (Recommended for Scripts)

```bash
az sql db query \
  --server "$SQL_SERVER" \
  --database "$SQL_DATABASE" \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --auth-mode ActiveDirectoryDefault \
  --queries "SELECT 1"
```

### Connection Strings

**For .NET applications with managed identity:**

```
Server=tcp:{server}.database.windows.net,1433;Database={database};Authentication=Active Directory Default;Encrypt=True;
```

**Required packages:**
- `Microsoft.Data.SqlClient` (v5.1.0+)
- `Azure.Identity` (for local development)

## Database Roles

| Role | Permissions | Use For |
|------|------------|---------|
| `db_datareader` | SELECT | Read operations |
| `db_datawriter` | INSERT, UPDATE, DELETE | Write operations |
| `db_ddladmin` | CREATE, ALTER, DROP schema | EF migrations |
| `db_owner` | Full control | Admin (use sparingly) |

## Grant Managed Identity Access

```sql
-- Create user from managed identity
CREATE USER [app-name] FROM EXTERNAL PROVIDER;

-- Grant standard application permissions
ALTER ROLE db_datareader ADD MEMBER [app-name];
ALTER ROLE db_datawriter ADD MEMBER [app-name];
ALTER ROLE db_ddladmin ADD MEMBER [app-name];
```

> 💡 **Tip:** The managed identity name matches the App Service or Container App name.

## Verify Current Admin

```bash
az sql server ad-admin list \
  --server "$SQL_SERVER" \
  --resource-group "$AZURE_RESOURCE_GROUP"
```

## References

- [SQL Managed Identity Access](sql-managed-identity.md)
- [EF Core Migrations](ef-migrations.md)
- [Post-Deployment Guide](post-deployment.md)

```

### references/recipes/azd/sql-managed-identity.md

```markdown
# SQL Managed Identity Access

Grant Azure managed identities database permissions on Azure SQL with Entra authentication.

## Prerequisites

- Azure SQL Server with Entra ID admin configured
- App Service/Container App with system-assigned managed identity
- Your account is Entra ID admin on SQL Server
- Azure CLI: `az login`

## Quick Grant

```bash
eval $(azd env get-values)
APP_NAME=$(echo "$SERVICE_API_NAME")  # or SERVICE_WEB_NAME

az sql db query \
  --server "$SQL_SERVER" \
  --database "$SQL_DATABASE" \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --auth-mode ActiveDirectoryDefault \
  --queries "
    CREATE USER [$APP_NAME] FROM EXTERNAL PROVIDER;
    ALTER ROLE db_datareader ADD MEMBER [$APP_NAME];
    ALTER ROLE db_datawriter ADD MEMBER [$APP_NAME];
    ALTER ROLE db_ddladmin ADD MEMBER [$APP_NAME];
  "
```

## Database Roles

| Role | Permissions | Use For |
|------|------------|---------|
| `db_datareader` | SELECT | Read-only queries |
| `db_datawriter` | INSERT, UPDATE, DELETE | CRUD operations |
| `db_ddladmin` | CREATE, ALTER, DROP schema | EF migrations |
| `db_owner` | Full control | Admin (use sparingly) |

**Standard app (read/write/migrations):** All three roles above.  
**Read-only app:** Only `db_datareader`.

## Automate with azd Hook

Add `postprovision` hook to `azure.yaml`:

```yaml
hooks:
  postprovision:
    shell: sh
    run: ./scripts/grant-sql-access.sh
```

**scripts/grant-sql-access.sh:**

```bash
#!/bin/bash
set -e
eval $(azd env get-values)

az sql db query \
  --server "$SQL_SERVER" \
  --database "$SQL_DATABASE" \
  --resource-group "$AZURE_RESOURCE_GROUP" \
  --auth-mode ActiveDirectoryDefault \
  --queries "
    IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = '$SERVICE_API_NAME')
      CREATE USER [$SERVICE_API_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 = '$SERVICE_API_NAME'
    )
      ALTER ROLE db_datareader ADD MEMBER [$SERVICE_API_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 = '$SERVICE_API_NAME'
    )
      ALTER ROLE db_datawriter ADD MEMBER [$SERVICE_API_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_ddladmin' AND m.name = '$SERVICE_API_NAME'
    )
      ALTER ROLE db_ddladmin ADD MEMBER [$SERVICE_API_NAME];
  "
```

> 💡 Make executable: `chmod +x scripts/*.sh`. For PowerShell: Use `azd env get-values | ForEach-Object` pattern.

## Verification

```bash
eval $(azd env get-values)
APP_NAME=$SERVICE_API_NAME  # or SERVICE_WEB_NAME

az sql db query --server "$SQL_SERVER" --database "$SQL_DATABASE" \
  --auth-mode ActiveDirectoryDefault --queries "
    SELECT dp.name AS UserName, dr.name AS RoleName
    FROM sys.database_principals dp
    JOIN sys.database_role_members drm ON dp.principal_id = drm.member_principal_id
    JOIN sys.database_principals dr ON drm.role_principal_id = dr.principal_id
    WHERE dp.name = '$APP_NAME'
  "
```

Expected: UserName matches `$APP_NAME`, RoleName includes `db_datareader`, `db_datawriter`, `db_ddladmin`.

## Troubleshooting

| Error | Solution |
|-------|----------|
| "Cannot find the user" | Verify identity exists: `az webapp identity show` or `az containerapp identity show` |
| "Principal does not have permission" | Check you're Entra admin: `az sql server ad-admin list` |
| "Login failed for user" | Run CREATE USER commands from this guide |

**Idempotent Script Pattern:**

```sql
-- Check if user exists before creating
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = 'my-app')
  CREATE USER [my-app] FROM EXTERNAL PROVIDER;

-- Check role membership before adding
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 = 'my-app'
)
  ALTER ROLE db_datareader ADD MEMBER [my-app];
```

## References

- [SQL Entra Authentication](sql-entra-auth.md)
- [EF Core Migrations](ef-migrations.md)
- [Post-Deployment Guide](post-deployment.md)

```

### references/recipes/bicep/README.md

```markdown
# Bicep Deploy Recipe

Deploy to Azure using Bicep templates directly.

## Prerequisites

- `az` CLI installed with Bicep extension
- `.azure/plan.md` exists with status `Validated`
- Bicep templates exist in `infra/`
- **Subscription and location confirmed** → See [Pre-Deploy Checklist](../../pre-deploy-checklist.md)

## Workflow

| Step | Task | Command |
|------|------|---------|
| 1 | **[Pre-deploy checklist](../../pre-deploy-checklist.md)** | Confirm subscription/location with user |
| 2 | Build (optional) | `az bicep build --file main.bicep` |
| 3 | Deploy | `az deployment sub create` |
| 4 | Verify | `az resource list` |

## Deployment Commands

### Subscription-Level Deployment

```bash
az deployment sub create \
  --location eastus2 \
  --template-file ./infra/main.bicep \
  --parameters ./infra/main.parameters.json
```

### Resource Group Deployment

```bash
az deployment group create \
  --resource-group rg-myapp-dev \
  --template-file ./infra/main.bicep \
  --parameters ./infra/main.parameters.json
```

### With Inline Parameters

```bash
az deployment sub create \
  --location eastus2 \
  --template-file ./infra/main.bicep \
  --parameters environmentName=dev location=eastus2
```

### What-If (Preview Changes)

```bash
az deployment sub what-if \
  --location eastus2 \
  --template-file ./infra/main.bicep \
  --parameters environmentName=dev
```

## Get Deployment Outputs

```bash
az deployment sub show \
  --name main \
  --query properties.outputs
```

## References

- [Verification steps](./verify.md)
- [Error handling](./errors.md)

## MCP Tools

| Tool | Purpose |
|------|---------|
| `mcp_bicep_get_bicep_best_practices` | Best practices |
| `mcp_bicep_get_az_resource_type_schema` | Resource schemas |
| `mcp_bicep_list_avm_metadata` | Azure Verified Modules |

## AVM Verification Before Deploy

Before running deployment commands, verify generated templates followed AVM-first module selection:

1. AVM Bicep Pattern Modules (prefer AVM+AZD patterns)
2. AVM Bicep Resource Modules
3. AVM Bicep Utility Modules

If no AVM+AZD pattern module is available, fallback must remain within AVM modules (resource -> utility).

## Cleanup (DESTRUCTIVE)

```bash
az group delete --name <rg-name> --yes
```

⚠️ Permanently deletes ALL resources in the group.

```

### references/recipes/bicep/errors.md

```markdown
# Bicep Errors

| Error | Resolution |
|-------|------------|
| Syntax error | `az bicep build` to check |
| Missing parameter | Add to parameters file |
| Invalid property | Check `mcp_bicep_get_az_resource_type_schema` |
| Resource conflict | Check existing resources |
| Deployment failed | `az deployment sub show --name <name>` |
| Permission denied | Verify RBAC roles |

## Cleanup (DESTRUCTIVE)

```bash
az group delete --name <rg-name> --yes
```

⚠️ Permanently deletes ALL resources in the group.

```

### references/recipes/bicep/verify.md

```markdown
# Bicep Verification

```bash
az resource list --resource-group <rg-name> --output table
```

## Get Deployment Outputs

```bash
az deployment sub show \
  --name main \
  --query properties.outputs
```

## Health Check

```bash
curl -s https://<endpoint>/health | jq .
```

```

### references/recipes/cicd/README.md

```markdown
# CI/CD Deploy Recipe

Deploy to Azure using automated pipelines.

## Prerequisites

- `.azure/plan.md` exists with status `Validated`
- Azure Service Principal or federated credentials configured
- Pipeline file exists (`.github/workflows/` or `azure-pipelines.yml`)

## GitHub Actions

| Example | Description |
|---------|-------------|
| [github-azd.yml](examples/github-azd.yml) | AZD deployment workflow |
| [github-bicep.yml](examples/github-bicep.yml) | Bicep infrastructure deployment |

## Azure DevOps

| Example | Description |
|---------|-------------|
| [azdo-azd.yml](examples/azdo-azd.yml) | Basic AZD pipeline |
| [azdo-multistage.yml](examples/azdo-multistage.yml) | Multi-stage with approvals |

## Setup Requirements

### GitHub Actions

1. Create Azure Service Principal with federated credentials
2. Add secrets: `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID`
3. Add variables: `AZURE_ENV_NAME`, `AZURE_LOCATION`
4. Create environments with protection rules

### Azure DevOps

1. Create Service Connection to Azure
2. Create Variable Groups per environment
3. Create Environments with approval gates

## References

- [Verification steps](./verify.md)
- [Error handling](./errors.md)

```

### references/recipes/cicd/errors.md

```markdown
# CI/CD Errors

| Error | Resolution |
|-------|------------|
| Authentication failed | Check service principal/federated credentials |
| Missing secrets | Add required secrets to repository |
| Missing variables | Add required variables |
| Pipeline timeout | Increase timeout or optimize deployment |
| Approval pending | Request approval in environment settings |

## GitHub Actions Debugging

Check workflow logs in Actions tab for detailed error messages.

## Azure DevOps Debugging

Check pipeline run logs for detailed error messages.

```

### references/recipes/cicd/verify.md

```markdown
# CI/CD Verification

Check pipeline run status:
- **GitHub**: Actions tab → workflow run
- **Azure DevOps**: Pipelines → pipeline run

## Verify Deployed Resources

```bash
az resource list --resource-group <rg-name> --output table
```

## Health Check

```bash
curl -s https://<endpoint>/health | jq .
```

```

### references/recipes/terraform/README.md

```markdown
# Terraform Deploy Recipe

Deploy to Azure using Terraform.

## Prerequisites

- Terraform CLI installed
- `.azure/plan.md` exists with status `Validated`
- Terraform initialized (`terraform init`)
- Plan validated (`terraform plan`)
- **Subscription and location confirmed** → See [Pre-Deploy Checklist](../../pre-deploy-checklist.md)

## Workflow

| Step | Task | Command |
|------|------|---------|
| 1 | **[Pre-deploy checklist](../../pre-deploy-checklist.md)** | Confirm subscription/location with user |
| 2 | Select workspace | `terraform workspace select <env>` |
| 3 | Apply | `terraform apply tfplan` |
| 4 | Get outputs | `terraform output` |
| 5 | Deploy app | Service-specific commands |

## Deployment Commands

### Apply with Plan File (Recommended)

```bash
terraform plan -out=tfplan
terraform apply tfplan
```

### Auto-Approve (CI/CD)

```bash
terraform apply -auto-approve
```

### With Variables

```bash
terraform apply \
  -var="environment=prod" \
  -var="location=westus2" \
  -auto-approve
```

### Target Specific Resource

```bash
terraform apply -target=azurerm_container_app.api
```

## Get Outputs

```bash
terraform output
terraform output -json
terraform output api_url
```

## Application Deployment

After infrastructure is deployed:

```bash
ACR_NAME=$(terraform output -raw acr_name)
APP_NAME=$(terraform output -raw container_app_name)
RG_NAME=$(terraform output -raw resource_group_name)

az acr build --registry $ACR_NAME --image myapp:latest ./src/api

az containerapp update \
  --name $APP_NAME \
  --resource-group $RG_NAME \
  --image $ACR_NAME.azurecr.io/myapp:latest
```

## References

- [Verification steps](./verify.md)
- [Error handling](./errors.md)

```

### references/recipes/terraform/errors.md

```markdown
# Terraform Errors

| Error | Resolution |
|-------|------------|
| State lock error | Wait or `terraform force-unlock <lock-id>` |
| Resource exists | `terraform import <resource>` |
| Backend denied | Check storage permissions |
| Provider error | `terraform init -upgrade` |

## Cleanup (DESTRUCTIVE)

```bash
terraform destroy -auto-approve
```

Selective:
```bash
terraform destroy -target=azurerm_container_app.api
```

⚠️ Permanently deletes resources.

```

### references/recipes/terraform/verify.md

```markdown
# Terraform Verification

```bash
terraform output
terraform output -json
```

## Health Check

```bash
curl -s https://$(terraform output -raw api_url)/health | jq .
```

## Resource Check

```bash
az resource list --resource-group $(terraform output -raw resource_group_name) --output table
```

```

### references/region-availability.md

```markdown
# Azure Region Availability Reference

> **AUTHORITATIVE SOURCE** — Consult this file 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 → refer to the table or use the MCP tool for that service to list supported regions with sufficient quota, 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

### Azure Static Web Apps (SWA)

⚠️ **NOT available in many common regions**

| ✅ Available | ❌ NOT Available (will FAIL) |
|-------------|------------------------------|
| `westus2` | `eastus` |
| `centralus` | `northeurope` |
| `eastus2` | `southeastasia` |
| `westeurope` | `uksouth` |
| `eastasia` | `canadacentral` |
| | `australiaeast` |
| | `westus3` |

---

### Azure Kubernetes Service (AKS)

It has limited quota in some regions, to get available regions with enough quota, use `mcp_azure_mcp_quota` tool.

---

### Azure Database for PostgreSQL

It has limited quota in some regions, to get available regions with enough quota, use `mcp_azure_mcp_quota` tool.

---

### Azure OpenAI

⚠️ **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.

---

## 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**

---

## Common Architecture Patterns

| Pattern | Recommended Regions |
|---------|---------------------|
| SWA only | `westus2`, `centralus`, `eastus2`, `westeurope`, `eastasia` |
| SWA + backend services | `westus2`, `centralus`, `eastus2`, `westeurope`, `eastasia` |
| Container Apps (no SWA) | `eastus`, `eastus2`, `westus2`, `centralus`, `westeurope` |
| With Azure OpenAI (GPT-4o/4/3.5 + embeddings) | `eastus`, `eastus2`, `swedencentral` |
| SWA + Azure OpenAI (GPT-4o/4/3.5 + embeddings) | `eastus2` (only region with full SWA + model overlap) |

---

**Last updated:** 2026-03-03


```

azure-deploy | SkillHub