Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions scripts/preflight-claude.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,19 @@
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).
6 Insufficient quota (Terraform variant only; on Bicep this is a
warning because azd's own ARM preflight already surfaces it).

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.
deploy time, just less ergonomically). The marketplace-offer check
remains a hard fail in both variants when it can run. The quota check
is a hard fail only on the Terraform variant because `azapi_resource`
swallows quota into the opaque `715-123420`; on Bicep it's a warning
because azd's own provisionParametersResolver prints `InsufficientQuota`
next and prompts to continue.
#>

[CmdletBinding()]
Expand All @@ -59,6 +63,23 @@ function Warn([string]$message) {
Write-Host "Preflight: $message" -ForegroundColor Yellow
}

# Detect which IaC variant the preprovision hook is running under. The hook
# always fires from inside the variant folder (`infra-bicep/` or
# `infra-terraform/`), so the local `azure.yaml` tells us which provider azd
# is about to drive. This decides whether the quota check is a hard fail
# (Terraform: `azapi_resource` bypasses ARM preflight and the RP returns the
# opaque `400 715-123420`, so we MUST catch it here) or a warning (Bicep:
# azd's own `provisionParametersResolver` already runs ARM preflight and
# prints a clean `InsufficientQuota` message + prompts to continue, so a
# hard fail here just blocks that better UX).
$variant = 'unknown'
$azureYaml = Join-Path (Get-Location) 'azure.yaml'
if (Test-Path $azureYaml) {
$yaml = Get-Content $azureYaml -Raw
if ($yaml -match '(?m)^\s*provider:\s*bicep') { $variant = 'bicep' }
elseif ($yaml -match '(?m)^\s*provider:\s*terraform') { $variant = 'terraform' }
}

# --- 1. Required env vars ---------------------------------------------------
if (-not $env:CLAUDE_ORGANIZATION_NAME) {
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'"
Expand Down Expand Up @@ -164,7 +185,7 @@ $msg
$available = $limit - $current
if ($available -lt $capacity) {
$upperFamily = $family.ToUpper()
Fail 6 @"
$quotaMsg = @"
Insufficient quota for '$modelName' (family=$family) in '$location'.

Requested capacity: $capacity TPM (thousands)
Expand All @@ -185,6 +206,17 @@ opaque '400 715-123420' error because azapi bypasses ARM preflight
validation. Bicep and 'az deployment group create' show the real
'InsufficientQuota' message because they go through ARM preflight.
"@
if ($variant -eq 'bicep') {
# Bicep variant: azd's own ARM preflight will print the same
# InsufficientQuota next and prompt to continue. Warn so the
# diagnostic is up front, then let azd take over.
Write-Host ""
Write-Host "Preflight WARNING: $quotaMsg" -ForegroundColor Yellow
Write-Host "(Continuing — azd's Bicep ARM preflight will repeat this and prompt to continue.)" -ForegroundColor Yellow
Write-Host ""
continue
}
Fail 6 $quotaMsg
}
Write-Host "Preflight: '$modelName' quota OK ($capacity requested, $available available of $limit in $location)." -ForegroundColor Green
} else {
Expand Down
40 changes: 35 additions & 5 deletions scripts/preflight-claude.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@
# Exit codes:
# 0 Preflight passed (or skipped — see warnings).
# 4 Marketplace offer not found.
# 6 Insufficient quota.
# 6 Insufficient quota (Terraform variant only; on Bicep this is a
# warning because azd's own ARM preflight already surfaces it).
#
# 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.
# ergonomically). The marketplace-offer check remains a hard fail in both
# variants when it can run. The quota check is a hard fail only on the
# Terraform variant because azapi_resource swallows quota into the opaque
# 715-123420; on Bicep it's a warning because azd's own
# provisionParametersResolver prints InsufficientQuota next and prompts to
# continue.

set -euo pipefail

Expand All @@ -43,6 +47,24 @@ warn() {
printf 'Preflight: %s\n' "$*" >&2
}

# Detect which IaC variant the preprovision hook is running under. The hook
# always fires from inside the variant folder (infra-bicep/ or
# infra-terraform/), so the local azure.yaml tells us which provider azd is
# about to drive. This decides whether the quota check is a hard fail
# (Terraform: azapi_resource bypasses ARM preflight and the RP returns the
# opaque 400 715-123420, so we MUST catch it here) or a warning (Bicep:
# azd's own provisionParametersResolver already runs ARM preflight and prints
# a clean InsufficientQuota message + prompts to continue, so a hard fail
# here just blocks that better UX).
VARIANT="unknown"
if [ -f "./azure.yaml" ]; then
if grep -qE '^[[:space:]]*provider:[[:space:]]*bicep' ./azure.yaml; then
VARIANT="bicep"
elif grep -qE '^[[:space:]]*provider:[[:space:]]*terraform' ./azure.yaml; then
VARIANT="terraform"
fi
fi

# --- 1. Required env vars ---------------------------------------------------
if [ -z "${CLAUDE_ORGANIZATION_NAME:-}" ]; then
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'"
Expand Down Expand Up @@ -153,7 +175,7 @@ $MP_RAW"
AVAILABLE=$(( LIMIT_INT - CURRENT_INT ))
if [ "$AVAILABLE" -lt "$CAPACITY" ]; then
FAMILY_UPPER="$(echo "$FAMILY" | tr '[:lower:]' '[:upper:]')"
fail 6 "Insufficient quota for '$MODEL_NAME' (family=$FAMILY) in '$LOCATION'.
QUOTA_MSG="Insufficient quota for '$MODEL_NAME' (family=$FAMILY) in '$LOCATION'.

Requested capacity: $CAPACITY TPM (thousands)
Available: $AVAILABLE TPM (limit $LIMIT_INT, currently used $CURRENT_INT)
Expand All @@ -172,6 +194,14 @@ Note: without this preflight, Terraform (azapi_resource) would fail with an
opaque '400 715-123420' error because azapi bypasses ARM preflight
validation. Bicep / 'az deployment group create' show the real
'InsufficientQuota' message because they go through ARM preflight."
if [ "$VARIANT" = "bicep" ]; then
# Bicep variant: azd's own ARM preflight will print the same
# InsufficientQuota next and prompt to continue. Warn so the
# diagnostic is up front, then let azd take over.
printf '\nPreflight WARNING: %s\n(Continuing — azd'"'"'s Bicep ARM preflight will repeat this and prompt to continue.)\n\n' "$QUOTA_MSG" >&2
continue
fi
fail 6 "$QUOTA_MSG"
fi
echo "Preflight: '$MODEL_NAME' quota OK ($CAPACITY requested, $AVAILABLE available of $LIMIT_INT in $LOCATION)."
else
Expand Down
2 changes: 1 addition & 1 deletion skills/claude-on-foundry/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ azd env set CLAUDE_CODE_AUTO_INSTALL true
azd up
```

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 `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 missing offer in the Anthropic catalog (exit 4) when that check can run — that's the case that turns into an opaque deploy failure otherwise. The quota check is a **hard fail on Terraform** (because `azapi_resource` swallows quota into the opaque `715-123420`) and a **warning on Bicep** (because azd's own ARM preflight already prints `InsufficientQuota` and prompts to continue). Never suggest bypassing the preflight.

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
Expand Down
Loading