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
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This is the **Claude on Foundry Starter Kit** ([`Azure-Samples/claude`](https://
| `CLAUDE_MODEL_NAME` | no | `claude-sonnet-4-6` | **Legacy** single-deployment fallback (only used when all three `CLAUDE_*_MODEL` are empty) |
| `ASSIGN_RBAC` | no | `false` | `true` grants Foundry User + Foundry Project Manager to `AZURE_PRINCIPAL_ID` |
| `CLAUDE_CODE_AUTO_INSTALL` | no | `false` | `true` installs the Claude Code CLI in the postprovision hook |
| `CLAUDE_SKIP_VSCODE_SETTINGS` | no | `false` | `true` skips the `.vscode/settings.json` write in the postprovision hook (use when you don't have the Claude Code VS Code extension) |
| `CLAUDE_WRITE_VSCODE_SETTINGS` | no | `false` | `true` opts in to having the postprovision hook write `.vscode/settings.json` for the [Anthropic Claude Code VS Code extension](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code). Default skips it — the CLI / SDK don't need workspace settings. |

Always set vars with `azd env set <VAR> <VALUE>` from inside the chosen variant folder.

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ azd up

> **Want just one family?** Set only that one (e.g. just `CLAUDE_OPUS_MODEL`) and leave the others unset. Want to override capacity per family? Set `CLAUDE_HAIKU_CAPACITY` / `CLAUDE_SONNET_CAPACITY` / `CLAUDE_OPUS_CAPACITY` (TPM ÷ 1000, default 25 each). See [Choosing which models to deploy](#choosing-which-models-to-deploy).

`azd up` provisions Foundry + the Claude deployment, then a **postprovision** hook ([`scripts/configure-claude-code.ps1`](./scripts/configure-claude-code.ps1)) writes a `claude-code.env.ps1` / `claude-code.env.sh` activator at the repo root and a `.vscode/settings.json` for the Claude Code VS Code extension. See [Claude Code post-deploy setup](#claude-code-post-deploy-setup) for details.
`azd up` provisions Foundry + the Claude deployment, then a **postprovision** hook ([`scripts/configure-claude-code.ps1`](./scripts/configure-claude-code.ps1)) writes a `claude-code.env.ps1` / `claude-code.env.sh` activator at the repo root and pins a workspace default via `.claude/settings.json`. See [Claude Code post-deploy setup](#claude-code-post-deploy-setup) for details.

### Use Claude Code

Expand Down Expand Up @@ -236,7 +236,7 @@ After `azd up` succeeds, the **postprovision** hook ([`scripts/configure-claude-
- `ANTHROPIC_FOUNDRY_RESOURCE=<your-foundry-account-name>`
- One `ANTHROPIC_DEFAULT_<FAMILY>_MODEL=<deployment-name>` per deployed family (`HAIKU` / `SONNET` / `OPUS`). Only the families you actually deployed get a line.
- **`AZURE_CONFIG_DIR=<repo>/.azure-cli`** &mdash; scopes `az login` (and `azd`) to this workspace only. See [Workspace-scoped `az login`](#workspace-scoped-az-login) below.
2. Writes (or merges into) `.vscode/settings.json` with `claudeCode.environmentVariables` (the array-of-`{name,value}` schema the extension actually reads &mdash; the display name in the Settings UI is *"Claude Code: Environment Variables"*) and `claudeCode.disableLoginPrompt: true` so the [Claude Code VS Code extension](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code) skips the Anthropic-account login and uses your Foundry deployment via Entra ID. It also sets `terminal.integrated.env.{windows,linux,osx}.AZURE_CONFIG_DIR` so every terminal VS Code spawns in this workspace inherits the scoped Azure config automatically &mdash; you don't even have to source the activator first. **Not using the Claude Code extension?** Opt out before `azd up` with `azd env set CLAUDE_SKIP_VSCODE_SETTINGS 1` (or pass `-SkipVsCodeSettings` / `--skip-vscode-settings` when running the script standalone) and the hook leaves `.vscode/settings.json` alone. The activator at step 1 still works for sourced shells.
2. **(Opt-in)** Writes (or merges into) `.vscode/settings.json` with `claudeCode.environmentVariables` (the array-of-`{name,value}` schema the extension actually reads &mdash; the display name in the Settings UI is *"Claude Code: Environment Variables"*) and `claudeCode.disableLoginPrompt: true` so the [Claude Code VS Code extension](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code) skips the Anthropic-account login and uses your Foundry deployment via Entra ID. It also sets `terminal.integrated.env.{windows,linux,osx}.AZURE_CONFIG_DIR` so every terminal VS Code spawns in this workspace inherits the scoped Azure config automatically &mdash; you don't even have to source the activator first. **This step only runs if you ask for it** &mdash; the hook leaves `.vscode/settings.json` alone by default since most users run Claude from the SDK, the Claude Code CLI (which only needs the activator at step 1), or another OpenAI-compatible client. **Using the Claude Code extension?** Opt in before `azd up` with `azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1` (or pass `-WriteVsCodeSettings` / `--write-vscode-settings` when running the script standalone).
3. Writes (or merges into) `.claude/settings.json` at the repo root with `{ "model": "<family>" }` pinned to a deployed family (sonnet &gt; opus &gt; haiku priority). This is the **workspace-level** Claude Code config and overrides whatever is in your user-global `~/.claude/settings.json` &mdash; so bare `claude` / `claude -p` resolves to a family you actually deployed, even if your global default points elsewhere.
4. Checks whether `claude` is on PATH. If not, prints the platform-appropriate one-liner install command. Set `CLAUDE_CODE_AUTO_INSTALL=true` *before* `azd up` to run [the official installer](https://claude.ai/install.ps1) automatically.

Expand Down
42 changes: 29 additions & 13 deletions scripts/configure-claude-code.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,25 @@
[CmdletBinding()]
param(
[string] $RepoRoot,
[switch] $WriteVsCodeSettings,
# Deprecated no-op: skipping is now the default. Kept so existing CI / docs
# that still pass -SkipVsCodeSettings don't break.
[switch] $SkipVsCodeSettings
)

$ErrorActionPreference = 'Stop'

# Env-var opt-out so the postprovision hook can honor it without having to
# pass -SkipVsCodeSettings on the command line:
# azd env set CLAUDE_SKIP_VSCODE_SETTINGS 1
if (-not $SkipVsCodeSettings) {
$skipEnv = $env:CLAUDE_SKIP_VSCODE_SETTINGS
if ($skipEnv -and $skipEnv -match '^(1|true|yes|on)$') {
$SkipVsCodeSettings = $true
# .vscode/settings.json is opt-in. Most users of this starter call Claude from
# the Anthropic SDK, the Claude Code CLI (uses the activator at the repo root,
# no workspace settings needed), or another OpenAI-compatible client. Only
# users of the Anthropic Claude Code VS Code extension benefit from having
# claudeCode.* keys written into their workspace settings, so make them ask:
# azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1
# (or pass -WriteVsCodeSettings when running the script standalone).
if (-not $WriteVsCodeSettings) {
$writeEnv = $env:CLAUDE_WRITE_VSCODE_SETTINGS
if ($writeEnv -and $writeEnv -match '^(1|true|yes|on)$') {
$WriteVsCodeSettings = $true
}
}

Expand Down Expand Up @@ -185,10 +192,12 @@ Write-Host "Wrote activator: $ps1Path"
Write-Host "Wrote activator: $shPath"

# ---------------------------------------------------------------------------
# 2. Write / merge `.vscode/settings.json` for the Claude Code VS Code extension.
# 2. Optionally write / merge `.vscode/settings.json` for the Claude Code
# VS Code extension. Opt-in via CLAUDE_WRITE_VSCODE_SETTINGS=1 (or
# -WriteVsCodeSettings) since most users don't run that extension.
# ---------------------------------------------------------------------------
if ($SkipVsCodeSettings) {
Write-Host "Skipping .vscode/settings.json (CLAUDE_SKIP_VSCODE_SETTINGS / -SkipVsCodeSettings set). The activator above still wires up sourced shells."
if (-not $WriteVsCodeSettings) {
Write-Host "Skipping .vscode/settings.json (opt-in). Set 'azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1' before 'azd up' to wire up the Anthropic Claude Code VS Code extension automatically. The activator above already works for sourced shells."
} else {
$vscodeDir = Join-Path $RepoRoot '.vscode'
$settingsPath = Join-Path $vscodeDir 'settings.json'
Expand All @@ -197,6 +206,7 @@ if ($SkipVsCodeSettings) {
}

$existing = [ordered]@{}
$parseFailed = $false
if (Test-Path $settingsPath) {
try {
$raw = Get-Content -Raw -Path $settingsPath
Expand All @@ -208,11 +218,11 @@ if ($SkipVsCodeSettings) {
}
} catch {
Write-Host "WARNING: Could not parse existing $settingsPath ($($_.Exception.Message)). Leaving it untouched." -ForegroundColor Yellow
$SkipVsCodeSettings = $true
$parseFailed = $true
}
}

if (-not $SkipVsCodeSettings) {
if (-not $parseFailed) {
# Use [ordered] hashtables per entry so name appears before value in
# the rendered JSON (PSCustomObject hashtable iteration is unordered).
$claudeEnv = @(
Expand Down Expand Up @@ -372,6 +382,12 @@ Write-Host " claude"
Write-Host ""
Write-Host "Or in VS Code: install the 'Claude Code' extension"
Write-Host "(https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code)"
Write-Host "— the .vscode/settings.json in this workspace already wires it up."
if ($WriteVsCodeSettings) {
Write-Host "— the .vscode/settings.json in this workspace already wires it up."
} else {
Write-Host "— then re-run 'azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1; azd provision'"
Write-Host " (or '. .\scripts\configure-claude-code.ps1 -WriteVsCodeSettings')"
Write-Host " to auto-wire the extension to this Foundry deployment."
}
Write-Host ""
exit 0
41 changes: 27 additions & 14 deletions scripts/configure-claude-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,27 @@ fail() {
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="${REPO_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}"

# Skip flag from env. Prefer the CLAUDE_-namespaced name (matches the rest of
# the env-var contract); keep the un-prefixed name as a deprecated alias.
# Set via: azd env set CLAUDE_SKIP_VSCODE_SETTINGS 1
SKIP_VSCODE_SETTINGS="${CLAUDE_SKIP_VSCODE_SETTINGS:-${SKIP_VSCODE_SETTINGS:-0}}"
case "$SKIP_VSCODE_SETTINGS" in
1|true|TRUE|yes|YES|on|ON) SKIP_VSCODE_SETTINGS=1 ;;
*) SKIP_VSCODE_SETTINGS=0 ;;
# .vscode/settings.json is opt-in. Most users of this starter call Claude from
# the Anthropic SDK, the Claude Code CLI (uses the activator at the repo root,
# no workspace settings needed), or another OpenAI-compatible client. Only
# users of the Anthropic Claude Code VS Code extension benefit from having
# claudeCode.* keys written into their workspace settings, so make them ask:
# azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1
# (or pass --write-vscode-settings when running the script standalone).
WRITE_VSCODE_SETTINGS="${CLAUDE_WRITE_VSCODE_SETTINGS:-0}"
case "$WRITE_VSCODE_SETTINGS" in
1|true|TRUE|yes|YES|on|ON) WRITE_VSCODE_SETTINGS=1 ;;
*) WRITE_VSCODE_SETTINGS=0 ;;
esac

while [[ $# -gt 0 ]]; do
case "$1" in
--repo-root) REPO_ROOT="$2"; shift 2 ;;
--repo-root=*) REPO_ROOT="${1#*=}"; shift ;;
--skip-vscode-settings) SKIP_VSCODE_SETTINGS=1; shift ;;
--repo-root) REPO_ROOT="$2"; shift 2 ;;
--repo-root=*) REPO_ROOT="${1#*=}"; shift ;;
--write-vscode-settings) WRITE_VSCODE_SETTINGS=1; shift ;;
# Deprecated no-op: skipping is now the default. Kept so existing CI
# / docs that still pass --skip-vscode-settings don't break.
--skip-vscode-settings) shift ;;
*) fail 2 "Unknown argument: $1" ;;
esac
done
Expand Down Expand Up @@ -161,8 +168,8 @@ for cand in python python3; do
if command -v "$cand" >/dev/null 2>&1; then PYTHON_BIN="$cand"; break; fi
done

if [ "${SKIP_VSCODE_SETTINGS:-}" = "1" ]; then
echo "Skipping .vscode/settings.json (CLAUDE_SKIP_VSCODE_SETTINGS / --skip-vscode-settings set). The activator above still wires up sourced shells."
if [ "${WRITE_VSCODE_SETTINGS:-}" != "1" ]; then
echo "Skipping .vscode/settings.json (opt-in). Set 'azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1' before 'azd up' to wire up the Anthropic Claude Code VS Code extension automatically. The activator above already works for sourced shells."
elif [ -n "$PYTHON_BIN" ]; then
VSCODE_DIR="$REPO_ROOT/.vscode"
mkdir -p "$VSCODE_DIR"
Expand Down Expand Up @@ -219,7 +226,7 @@ with open(path, 'w', encoding='utf-8') as f:
f.write('\n')
print(f"Wrote VS Code settings: {path}")
PYEOF
elif [ "${SKIP_VSCODE_SETTINGS:-}" != "1" ]; then
elif [ "${WRITE_VSCODE_SETTINGS:-}" = "1" ]; then
echo "WARNING: python not found on PATH; skipping .vscode/settings.json."
fi

Expand Down Expand Up @@ -334,6 +341,12 @@ echo " claude"
echo ""
echo "Or in VS Code: install the 'Claude Code' extension"
echo "(https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code)"
echo "- the .vscode/settings.json in this workspace already wires it up."
if [ "${WRITE_VSCODE_SETTINGS:-}" = "1" ]; then
echo "- the .vscode/settings.json in this workspace already wires it up."
else
echo "- then re-run 'azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1; azd provision'"
echo " (or 'bash scripts/configure-claude-code.sh --write-vscode-settings')"
echo " to auto-wire the extension to this Foundry deployment."
fi
echo ""
exit 0
2 changes: 1 addition & 1 deletion skills/claude-on-foundry/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ claude # interactive REPL; try /status and /mode
| **Switch region** | `azd env new <name-region>` in the same variant folder, then redo the [DEPLOY](#deploy--running-azd-up) flow. **Don't** try to mutate `AZURE_LOCATION` on an existing env — the account is region-stamped. |
| **Switch variants (Bicep ↔ Terraform)** | They produce equivalent infra but with different `azd` env state. Create a new env in the other folder: `cd infra-terraform && azd env new <name> && ...`. |
| **Refresh Claude Code wiring** | `pwsh -File scripts/configure-claude-code.ps1` (or the `.sh` variant). Idempotent — runs without re-deploying. |
| **Skip the `.vscode/settings.json` write** | `azd env set CLAUDE_SKIP_VSCODE_SETTINGS 1` (then re-run `azd provision` or the configure script). For customers who don't use the Claude Code VS Code extension and don't want workspace settings touched. The activator at the repo root still wires up sourced shells. |
| **Wire up the Claude Code VS Code extension** | `azd env set CLAUDE_WRITE_VSCODE_SETTINGS 1` (then re-run `azd provision` or the configure script). Opt-in because the activator + `.claude/settings.json` are enough for the CLI and SDK; only the [Anthropic Claude Code VS Code extension](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code) needs `claudeCode.*` keys in workspace settings. |
| **Convert to long-running auth** | Replace `Anthropic(auth_token=...)` with `AnthropicIdentity(azure_ad_token_provider=...)` from [`src/hello_claude_token_refresh.py`](../../src/hello_claude_token_refresh.py). |

---
Expand Down
Loading