From 26d60c4a092ccec84b6cd458a54372732649a8e4 Mon Sep 17 00:00:00 2001 From: Arun Sekhar Date: Fri, 29 May 2026 13:02:35 -0700 Subject: [PATCH] Preflight: warn instead of hard-fail when missing env vars / az login (#21) Reported by @pamelafox: `azd up` on a clean machine hit `ERROR: CLAUDE_ORGANIZATION_NAME is required` before azd / Bicep could prompt for the parameter, and hit `ERROR: Azure CLI not found` because `az login` wasn't called out as a prerequisite. The four soft preconditions (CLAUDE_ORGANIZATION_NAME, AZURE_LOCATION, az on PATH, az signed in) now warn and exit 0 so azd can prompt / the RP can take over. Marketplace-offer-not-found (exit 4) and insufficient quota (exit 6) remain hard fails -- those are the cases Terraform azapi swallows into the opaque 715-123420. --- README.md | 1 + scripts/preflight-claude.ps1 | 30 +++++++++++++++++++++++------- scripts/preflight-claude.sh | 30 +++++++++++++++++++++++------- skills/claude-on-foundry/SKILL.md | 2 +- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 8eafe5a..eddfda3 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ The assistant follows the playbook in [`skills/claude-on-foundry/SKILL.md`](./sk - An Azure subscription [eligible to deploy Claude in Foundry](https://learn.microsoft.com/azure/ai-foundry/foundry-models/how-to/use-foundry-models-claude#prerequisites), with `Contributor` on the target subscription/resource group (see [Required permissions](#required-permissions) for the full breakdown, including the data-plane role you need to call the model). - Region: `eastus2` or `swedencentral` host all three Claude families (haiku / sonnet / opus). `westus2` is sonnet + opus only. - Tools: [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli), [azd](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd), Python ≥ 3.10, and [Terraform](https://developer.hashicorp.com/terraform/install) ≥ 1.6 (Terraform variant only). +- Run `az login` once (in addition to `azd auth login` below). The `preprovision` hook uses `az` to validate that each requested Claude SKU exists in the Anthropic-on-Foundry catalog and that you have enough TPM quota in the chosen region. If `az` isn't installed or signed in, the hook warns and skips those checks so `azd up` still works — you just lose the proactive error messages. ## Quickstart diff --git a/scripts/preflight-claude.ps1 b/scripts/preflight-claude.ps1 index 2fc475b..11e9071 100644 --- a/scripts/preflight-claude.ps1 +++ b/scripts/preflight-claude.ps1 @@ -29,12 +29,18 @@ .NOTES Exit codes: - 0 Preflight passed. - 1 A required env var is missing. - 2 Azure CLI / subscription not available. + 0 Preflight passed (or skipped — see warnings). 4 Marketplace offer not found (typo in a model name, or model not in the Anthropic-on-Foundry catalog yet). 6 Insufficient quota (used + requested > limit). + + The preflight is best-effort. If `CLAUDE_ORGANIZATION_NAME` / + `AZURE_LOCATION` aren't set, or `az` isn't installed / logged in, it + warns and exits 0 so `azd up` can continue (azd / Bicep will prompt + for any missing parameter; the RP surfaces catalog / quota errors at + deploy time, just less ergonomically). The marketplace-offer and + quota checks remain hard fails when they CAN run, because they + catch the most common cause of opaque deploy failures. #> [CmdletBinding()] @@ -49,12 +55,17 @@ function Fail([int]$code, [string]$message) { exit $code } +function Warn([string]$message) { + Write-Host "Preflight: $message" -ForegroundColor Yellow +} + # --- 1. Required env vars --------------------------------------------------- if (-not $env:CLAUDE_ORGANIZATION_NAME) { - Fail 1 "CLAUDE_ORGANIZATION_NAME is required. Run: azd env set CLAUDE_ORGANIZATION_NAME 'Your Org'" + Warn "CLAUDE_ORGANIZATION_NAME is not set. azd will prompt for the 'claudeOrganizationName' Bicep parameter at provision time. To skip the prompt: azd env set CLAUDE_ORGANIZATION_NAME 'Your Org'" } if (-not $env:AZURE_LOCATION) { - Fail 1 "AZURE_LOCATION is required. Run: azd env set AZURE_LOCATION swedencentral" + Warn "AZURE_LOCATION is not set. azd will prompt at provision time. Skipping marketplace + quota validation (they need a region). To skip the prompt: azd env set AZURE_LOCATION swedencentral" + exit 0 } $location = $env:AZURE_LOCATION @@ -73,14 +84,19 @@ if ($requested.Count -eq 0) { } # --- 2. Azure CLI / active subscription ------------------------------------ +# These checks are best-effort: if `az` is missing or the user hasn't run +# `az login`, we skip the marketplace + quota checks and let `azd up` +# continue. The RP will surface any errors at deploy time. $az = Get-Command az -ErrorAction SilentlyContinue if (-not $az) { - Fail 2 "Azure CLI (az) not found on PATH. Install: https://learn.microsoft.com/cli/azure/install-azure-cli" + Warn "Azure CLI (az) not found on PATH. Skipping marketplace + quota validation. Install az and run 'az login' for proactive checks: https://learn.microsoft.com/cli/azure/install-azure-cli" + exit 0 } $subId = (az account show --query id -o tsv 2>$null) if (-not $subId) { - Fail 2 "No active Azure subscription. Run: az login (and 'az account set --subscription ' if needed)" + Warn "Not signed in to Azure CLI. Skipping marketplace + quota validation. Run 'az login' (and 'az account set --subscription ' if needed) for proactive checks." + exit 0 } $summary = ($requested | ForEach-Object { "$($_.Family)=$($_.Model)@$($_.Capacity)" }) -join ', ' diff --git a/scripts/preflight-claude.sh b/scripts/preflight-claude.sh index 0c761b4..d2b14a4 100644 --- a/scripts/preflight-claude.sh +++ b/scripts/preflight-claude.sh @@ -19,11 +19,17 @@ # `az deployment group create` show the real `InsufficientQuota`). # # Exit codes: -# 0 Preflight passed. -# 1 A required env var is missing. -# 2 Azure CLI / subscription not available. +# 0 Preflight passed (or skipped — see warnings). # 4 Marketplace offer not found. # 6 Insufficient quota. +# +# The preflight is best-effort. If CLAUDE_ORGANIZATION_NAME / AZURE_LOCATION +# aren't set, or `az` isn't installed / logged in, it warns and exits 0 so +# `azd up` can continue (azd / Bicep will prompt for any missing parameter; +# the RP surfaces catalog / quota errors at deploy time, just less +# ergonomically). The marketplace-offer and quota checks remain hard fails +# when they CAN run, because they catch the most common cause of opaque +# deploy failures. set -euo pipefail @@ -33,12 +39,17 @@ fail() { exit "$code" } +warn() { + printf 'Preflight: %s\n' "$*" >&2 +} + # --- 1. Required env vars --------------------------------------------------- if [ -z "${CLAUDE_ORGANIZATION_NAME:-}" ]; then - fail 1 "CLAUDE_ORGANIZATION_NAME is required. Run: azd env set CLAUDE_ORGANIZATION_NAME 'Your Org'" + warn "CLAUDE_ORGANIZATION_NAME is not set. azd will prompt for the 'claudeOrganizationName' Bicep parameter at provision time. To skip the prompt: azd env set CLAUDE_ORGANIZATION_NAME 'Your Org'" fi if [ -z "${AZURE_LOCATION:-}" ]; then - fail 1 "AZURE_LOCATION is required. Run: azd env set AZURE_LOCATION swedencentral" + warn "AZURE_LOCATION is not set. azd will prompt at provision time. Skipping marketplace + quota validation (they need a region). To skip the prompt: azd env set AZURE_LOCATION swedencentral" + exit 0 fi LOCATION="$AZURE_LOCATION" @@ -65,13 +76,18 @@ if [ "${#FAMILIES[@]}" -eq 0 ]; then fi # --- 2. Azure CLI / active subscription ------------------------------------ +# These checks are best-effort: if `az` is missing or the user hasn't run +# `az login`, we skip the marketplace + quota checks and let `azd up` +# continue. The RP will surface any errors at deploy time. if ! command -v az >/dev/null 2>&1; then - fail 2 "Azure CLI (az) not found on PATH. Install: https://learn.microsoft.com/cli/azure/install-azure-cli" + warn "Azure CLI (az) not found on PATH. Skipping marketplace + quota validation. Install az and run 'az login' for proactive checks: https://learn.microsoft.com/cli/azure/install-azure-cli" + exit 0 fi SUB_ID="$(az account show --query id -o tsv 2>/dev/null || true)" if [ -z "$SUB_ID" ]; then - fail 2 "No active Azure subscription. Run: az login (and 'az account set --subscription ' if needed)" + warn "Not signed in to Azure CLI. Skipping marketplace + quota validation. Run 'az login' (and 'az account set --subscription ' if needed) for proactive checks." + exit 0 fi SUMMARY="" diff --git a/skills/claude-on-foundry/SKILL.md b/skills/claude-on-foundry/SKILL.md index 8a66412..e47b7d6 100644 --- a/skills/claude-on-foundry/SKILL.md +++ b/skills/claude-on-foundry/SKILL.md @@ -92,7 +92,7 @@ azd env set CLAUDE_CODE_AUTO_INSTALL true azd up ``` -The `preprovision` hook runs `scripts/preflight-claude.ps1` automatically and **hard-fails** on (a) missing offer in the Anthropic catalog or (b) insufficient TPM quota. Never suggest bypassing it. +The `preprovision` hook runs `scripts/preflight-claude.ps1` automatically. It is **best-effort**: it warns and continues when `CLAUDE_ORGANIZATION_NAME` / `AZURE_LOCATION` aren't set (azd / Bicep will prompt) or when `az` isn't installed / logged in (the RP will surface any errors at deploy time). It still **hard-fails** on (a) a missing offer in the Anthropic catalog or (b) insufficient TPM quota when those checks can actually run — those are the cases that turn into opaque deploy failures otherwise. Never suggest bypassing it. The `postprovision` hook runs `scripts/configure-claude-code.ps1` to wire up Claude Code. The customer can re-run it any time without re-deploying: ```powershell