TL;DR: Este artigo apresenta um padrão de arquitetura para disponibilizar Claude Code a engenheiros sem expor chaves de API da Anthropic. Usando Azure API Management como gateway LLM com autenticação Entra ID, rate limiting por desenvolvedor e métricas de uso, os modelos Claude rodam no Microsoft Foundry com faturamento via Azure. A conclusão: em uma tarde de setup é possível eliminar key sprawl, ter visibilidade de custos por usuário e aplicar quotas sem que o desenvolvedor precise gerenciar tokens de longa duração.
O problema real: por que não basta apontar o Claude Code diretamente para a Anthropic?
Claude Code é um agente de codificação nativo de terminal e IDE que se comunica com o Claude via Anthropic Messages API. Conectá-lo diretamente à Anthropic — ou mesmo diretamente ao Foundry — cria três dores de cabeça para qualquer organização com mais de alguns usuários:
- Key sprawl e faturamento opaco. Chaves de API diretas significam ou uma chave compartilhada (sem atribuição por usuário, pesadelo de rotação) ou múltiplas chaves (overhead de procurement e offboarding).
- Ausência de throttle. Claude Code consome muitos tokens — lê arquivos, planeja e edita em loops longos. Uma sessão runaway ou um time muito entusiasmado pode gerar uma conta surpreendente sem nada entre o desenvolvedor e o modelo.
- Nenhuma visibilidade. Financeiro quer saber custo por time. Segurança quer saber quem está chamando o quê. Uma chave crua não oferece nenhum dos dois.
A correção é um gateway pelo qual toda requisição passa — que sabe quem é o desenvolvedor (Entra ID), aplica limites (APIM GenAI policies) e registra o uso (Azure Monitor). Claude Code suporta exatamente isso através de sua configuração de gateway.
Arquitetura: a independência entre auth do desenvolvedor e auth do backend
O fluxo de requisição é:
Developer laptop (Claude Code CLI / VS Code)
| Authorization: Bearer <Entra access token for the APIM app>
v
Azure API Management (the LLM gateway) [Subscription A]
| 1. validate-jwt confirma identidade Entra, audience, app role
| 2. extract oid chave de contador por usuário
| 3. llm-token-limit tokens/min + quota mensal por usuário
| 4. rate-limit-by-key requisições/min por usuário
| 5. remove Authorization; insere api-key do named value secreto
| 6. llm-emit-token-metric uso por usuário para App Insights
v (forward no formato Anthropic Messages; headers anthropic-* preservados)
Microsoft Foundry https://{resource}.services.ai.azure.com/anthropic/v1/messages
v [Subscription B]
Claude deployments (Sonnet 4.6 / Haiku 4.5 / Opus 4.6)
A ideia central: a autenticação voltada ao desenvolvedor e a autenticação do backend são independentes. Desenvolvedores sempre se autenticam como eles mesmos com Entra ID no gateway. Como o gateway se autentica no Foundry é uma decisão separada — e há duas boas opções.
Como o gateway se autentica no Foundry?
| Aspecto | Opção A — Chave de API do Foundry | Opção B — Managed Identity |
|---|---|---|
| Como o APIM autentica | Header api-key de um named value secreto |
Token Entra da managed identity do APIM, no header Authorization |
| Setup | Ler a chave uma vez, armazenar no APIM | Habilitar identity do APIM, atribuir papel Cognitive Services User no Foundry |
| Mesma subscription | Funciona | Funciona |
| Cross-subscription | Funciona — sem RBAC cruzando a fronteira | Funciona — atribuição de papel atravessa subscriptions no mesmo tenant |
| Cross-tenant | Funciona | Não suportado — usar chave |
| Segredo compartilhado para rotacionar | Sim | Nenhum |
| Melhor para | Start mais rápido; cross-tenant; ambientes só com chave | Produção; elimina o segredo compartilhado |
Pré-requisitos
- Duas subscriptions Azure, ambas pay-as-you-go. Subscription A para APIM; Subscription B para Foundry (Claude no Foundry não roda em subscriptions free, trial, sponsored ou CSP).
- Um recurso Microsoft Foundry na Subscription B, em região com Claude disponível (East US 2 ou Sweden Central), com deployments de Claude criados e ao menos uma chave de API em Keys and Endpoint.
- Uma instância de API Management na Subscription A. SKU Developer é suficiente para um piloto; Standard v2 ou Premium para produção e integração com VNet.
- Permissão para ler a chave do Foundry na Subscription B, contribuidor na instância APIM e capacidade de registrar apps Entra.
- Desenvolvedores em Windows 10/11 com PowerShell (5.1 nativo ou 7), Azure CLI e Claude Code CLI instalados.
Passo a passo da implementação
Passo 1 — Implantar Claude no Foundry (Subscription B)
No portal do Foundry, abra o Model catalog, procure por Claude e implante os modelos que o Claude Code utiliza. Nomeie cada deployment para corresponder ao model ID, assim o gateway pode passar o campo model adiante sem modificação:
| Papel | Nome do deployment (recomendado) |
|---|---|
| Principal (codificação geral) | claude-sonnet-4-6 |
| Rápido (leitura de arquivos, edições pequenas, tarefas de fundo) | claude-haiku-4-5 |
| Pensamento estendido (opcional) | claude-opus-4-6 |
Importante: Fixe as versões — selecione uma versão específica, não auto-update to latest. Sem fixação, uma nova versão do modelo pode quebrar todos os desenvolvedores de uma vez.
Copie o endpoint e uma das chaves de API. A base do endpoint Anthropic é: https://{resource}.services.ai.azure.com/anthropic
Crítico: O endpoint do Claude no Foundry é a superfície Anthropic (/anthropic/v1/messages), não a superfície OpenAI (/openai/deployments/.../chat/completions?api-version=...). Ao construir a API no APIM, não aplique o template de política OpenAI, não adicione parâmetro api-version e não reescreva para o caminho /openai/....
Passo 2 — Registro de app Entra ID (voltado ao desenvolvedor)
Este registro vive no tenant da Subscription A. Define a audience para a qual os tokens dos desenvolvedores são emitidos e o que o APIM valida.
- App registrations → New registration → nomeie como
Claude Code Gateway. - Expose an API → defina o Application ID URI, ex.:
api://claude-code-gateway. Adicione um scopeaccess_as_user. - (Opcional, para tiering) App roles → adicione papéis como
Claude.StandardeClaude.Premium. Atribua desenvolvedores ou grupos em Enterprise applications → este app → Users and groups. - Anote o Application (client) ID, o Application ID URI e o Tenant ID.
Passo 3 — Provisionar a API do APIM e o backend do Foundry (Subscription A)
Opção A — Armazenar a chave de API do Foundry no APIM
Leia a chave do Foundry na Subscription B e armazene como um named value secreto no APIM:
# Ler chave do Foundry da Subscription B
$FOUNDRY_KEY = az cognitiveservices account keys list `
--name <foundry-resource> `
--resource-group <foundry-rg> `
--subscription <SUBSCRIPTION_B_ID> `
--query key1 -o tsv
# Criar named value secreto no APIM (Subscription A)
az apim nv create -g <apim-rg> --service-name <apim-name> `
--named-value-id foundry-api-key `
--display-name foundry-api-key `
--value "$FOUNDRY_KEY" `
--secret true
Para maior segurança, coloque a chave no Key Vault e crie um named value backed by Key Vault, centralizando a rotação.
Opção B — Usar managed identity no APIM
Habilite uma identity gerenciada pelo sistema no APIM e atribua o papel Cognitive Services User no recurdo Foundry:
az apim update -g <apim-rg> --name <apim-name> --set identity.type=SystemAssigned
$APIM_MI = az apim show -g <apim-rg> --name <apim-name> --query identity.principalId -o tsv
$FOUNDRY_ID = az cognitiveservices account show --name <foundry-resource> `
--resource-group <foundry-rg> --subscription <SUBSCRIPTION_B_ID> --query id -o tsv
az role assignment create --assignee-object-id $APIM_MI `
--assignee-principal-type ServicePrincipal --role "Cognitive Services User" --scope $FOUNDRY_ID
Criar o backend e a API
az apim backend create -g <apim-rg> --service-name <apim-name> `
--backend-id foundry-claude `
--url "https://<foundry-resource>.services.ai.azure.com/anthropic" --protocol http
az apim api create -g <apim-rg> --service-name <apim-name> `
--api-id claude-anthropic --display-name "Claude (Foundry)" `
--path="" --protocols https `
--service-url "https://<foundry-resource>.services.ai.azure.com/anthropic"
Adicione as operações que o Claude Code chama: POST /v1/messages, POST /v1/messages/count_tokens e GET /v1/models (opcional, para descoberta de modelos).
Passo 4 — A política do APIM (auth + rate limiting + métricas)
Aplique a política no nível da API. Substitua o tenant ID e a audience. A versão abaixo usa chave de API (Opção A). Para managed identity (Opção B), substitua o passo 6 conforme mostrado adiante.
<policies>
<inbound>
<base />
<!-- 1. Validar token Entra do desenvolvedor -->
<validate-jwt header-name="Authorization"
failed-validation-httpcode="401"
failed-validation-error-message="Unauthorized: invalid or missing Entra token.">
<openid-config url="https://login.microsoftonline.com/{{tenant-id}}/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>{{gateway-audience}}</audience>
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/{{tenant-id}}/v2.0</issuer>
<issuer>https://sts.windows.net/{{tenant-id}}/</issuer>
</issuers>
<required-claims>
<claim name="roles" match="any">
<value>Claude.Standard</value>
<value>Claude.Premium</value>
</claim>
</required-claims>
</validate-jwt>
<!-- 2. Extrair oid estável do token -->
<set-variable name="callerId" value="@{
var jwt = context.Request.Headers
.GetValueOrDefault("Authorization","").Split(' ').Last().AsJwt();
return jwt.Claims.GetValueOrDefault("oid", "unknown");
}" />
<!-- 3. Extrair tier do app role -->
<set-variable name="tier" value="@{
var jwt = context.Request.Headers
.GetValueOrDefault("Authorization","").Split(' ').Last().AsJwt();
return jwt.Claims.GetValueOrDefault("roles","").Contains("Claude.Premium") ? "premium" : "standard";
}" />
<!-- 4. Rate limiting token-aware por desenvolvedor -->
<choose>
<when condition="@(((string)context.Variables["tier"]) == "premium")">
<llm-token-limit counter-key="@((string)context.Variables["callerId"])"
tokens-per-minute="200000" estimate-prompt-tokens="true"
remaining-tokens-header-name="x-tokens-remaining"
token-quota="20000000" token-quota-period="Monthly" />
<rate-limit-by-key calls="300" renewal-period="60"
counter-key="@((string)context.Variables["callerId"])" />
</when>
<otherwise>
<llm-token-limit counter-key="@((string)context.Variables["callerId"])"
tokens-per-minute="50000" estimate-prompt-tokens="true"
remaining-tokens-header-name="x-tokens-remaining"
token-quota="5000000" token-quota-period="Monthly" />
<rate-limit-by-key calls="100" renewal-period="60"
counter-key="@((string)context.Variables["callerId"])" />
</otherwise>
</choose>
<!-- 5. Emitir métricas de uso por desenvolvedor -->
<llm-emit-token-metric namespace="claudecode">
<dimension name="UserId" value="@((string)context.Variables["callerId"])" />
<dimension name="Tier" value="@((string)context.Variables["tier"])" />
<dimension name="Model" value="@(context.Request.Body?.As<JObject>(true)?["model"]?.ToString() ?? "unknown")" />
</llm-emit-token-metric>
<!-- 6. Autenticar no Foundry com chave de API (named value secreto) -->
<set-header name="Authorization" exists-action="delete" />
<set-header name="x-api-key" exists-action="override">
<value>{{foundry-api-key}}</value>
</set-header>
<set-backend-service backend-id="foundry-claude" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Opção B (managed identity) — substitua o passo 6:
<!-- 6 (Option B). Autenticar no Foundry com managed identity do APIM -->
<authentication-managed-identity
resource="https://cognitiveservices.azure.com"
output-token-variable-name="msi-token" />
<set-header name="Authorization" exists-action="override">
<value>@("Bearer " + (string)context.Variables["msi-token"])</value>
</set-header>
<set-backend-service backend-id="foundry-claude" />
Passo 5 — Configurar o Claude Code nas máquinas dos desenvolvedores
Crie o script helper %USERPROFILE%\.claude\get-claude-gateway-token.ps1:
az account get-access-token `
--resource "api://claude-code-gateway" `
--query accessToken -o tsv
E o arquivo de configuração %USERPROFILE%\.claude\settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "https://<apim-name>.azure-api.net",
"ANTHROPIC_MODEL": "claude-opus-4-8",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-8",
"CLAUDE_CODE_API_KEY_HELPER_TTL_MS": "600000"
},
"apiKeyHelper": "powershell -NoProfile -ExecutionPolicy Bypass -File C:\\Users\\<you>\\.claude\\get-claude-gateway-token.ps1"
}
Passo 6 — Design de rate limiting e rastreamento de uso
Tudo é chaveado no claim oid do Entra — estável e único por usuário, diferente de email ou upn que podem mudar. Para contas de serviço ou CI, use appid.
Duas camadas de enforcement:
- llm-token-limit: tokens/minuto + quota mensal. O verdadeiro controle de custo.
- rate-limit-by-key: requisições/minuto. Protege contra loops runaway.
Tiering é dirigido por app roles do Entra (Claude.Standard / Claude.Premium) lidos do JWT — sem necessidade de gerenciamento separado de subscription no APIM.
O rastreamento de uso flui de llm-emit-token-metric para o Application Insights com dimensões de UserId, Tier e Model. Exemplo de query no Log Analytics:
customMetrics
| where name == "Total Tokens" and customDimensions.namespace == "claudecode"
| extend UserId = tostring(customDimensions.UserId), Model = tostring(customDimensions.Model)
| summarize Tokens = sum(valueSum) by UserId, Model, bin(timestamp, 1d)
| order by Tokens desc
O Foundry não retorna os headers padrão de rate limit da Anthropic, então gerencie e observe limites através do APIM e do Azure Monitor.
Passo 7 — Testar e validar
# 1. Obter token como desenvolvedor
$TOKEN = az account get-access-token --resource "api://claude-code-gateway" --query accessToken -o tsv
# 2. Chamar o gateway diretamente no formato Anthropic Messages
$body = @{
model = "claude-sonnet-4-6"
max_tokens = 64
messages = @(@{ role = "user"; content = "Say hello in one word." })
} | ConvertTo-Json
Invoke-RestMethod -Method Post `
-Uri "https://<apim-name>.azure-api.net/v1/messages" `
-Headers @{
"Authorization" = "Bearer $TOKEN"
"anthropic-version" = "2023-06-01"
"content-type" = "application/json"
} -Body $body
Checklist de validação:
- Sem token / token expirado → 401 do validate-jwt
- Token válido → 200 com resposta do Claude; response traz
x-tokens-remaining/x-ratelimit-remaining - 401 do Foundry com token válido → named value
api-keyincorreto ou não injetado - Excedeu limite → 429 com
retry-after - App Insights →
customMetricsmostra contagem de tokens porUserId
Passo 8 — Operações e hardening
- Rotação de chave (Opção A): Foundry fornece duas chaves. Rotacione atualizando o named value
foundry-api-keyparakey2e depois regerandokey1— zero downtime. - Prefira managed identity em produção (Opção B): Remove o segredo compartilhado completamente. Porque a atribuição do papel Cognitive Services User funciona entre subscriptions no mesmo tenant, a topologia cross-subscription não bloqueia essa atualização.
- Rede privada: Coloque o APIM em modo VNet interno e alcance o Foundry via Private Endpoint; desabilite o acesso público do Foundry.
- Resiliência: Implante Claude em duas regiões e use o backend pool load-balanced do APIM com retry em 429 e 5xx.
- Guardrails de custo: Combine quotas de
llm-token-limitpor usuário com um Azure Budget e alerta no recurso Foundry na Subscription B.
Troubleshooting
| Sintoma | Causa / correção |
|---|---|
| 404 resource not found do Foundry | URL ou caminho do backend errado, ou rewrite estilo OpenAI aplicado. Backend deve terminar em /anthropic; chamadores acessam /v1/messages. Remova qualquer rewrite /openai/... e parâmetro api-version. |
| 401 do Foundry com token de desenvolvedor válido (Opção A) | Header api-key ausente/errado, ou named value foundry-api-key não foi salvo como esperado. Confirme o named value e que a política deleta o header Authorization do desenvolvedor e define api-key. |
| 401/403 do Foundry (Opção B, managed identity) | Atribuição de papel ausente ou não propagada, ou audience do token errada. Confirme que a identity do APIM tem Cognitive Services User no recurso Foundry, aguarde alguns minutos, e garanta que a política use resource="https://cognitiveservices.azure.com". |
| Managed identity funciona na mesma sub mas não cross-sub | Os dois recursos estão em tenants Entra diferentes. Managed identity cross-tenant não é suportado — use a chave de API (Opção A). |
| 401 no gateway mesmo com token | Mismatch de aud ou issuer. Confirme que aud do token = api://claude-code-gateway e que usou o OIDC config e issuer v2.0. |
| Funcionalidade reduzida do Claude Code | Gateway removeu headers anthropic-beta / anthropic-version. Garanta que ambos passem. |
| Modelo não disponível | O model ID do Claude Code não corresponde ao nome do deployment no Foundry. Alinhe os nomes ou reescreva o campo model do body na política. |
| Falha de autenticação ChainedTokenCredential (lado cliente) | Desenvolvedor não está logado. Execute az login para que o helper tenha uma credencial Azure utilizável. |
Considerações finais
Com cerca de uma tarde de setup, você obtém um gateway pelo qual toda requisição do Claude Code passa: Entra ID prova quem é o desenvolvedor, APIM GenAI policies limitam quanto cada pessoa pode gastar e Application Insights mostra exatamente para onde os tokens foram. Para o salto APIM → Foundry, escolha o que se encaixa: uma chave de API do Foundry mantida apenas dentro do APIM (start mais rápido, funciona cross-tenant) ou uma managed identity sem segredo compartilhado (postura de produção).
O caminho de upgrade é limpo: se começou com chave, mova-a para o Key Vault e depois migre para managed identity para eliminar o segredo completamente. Nenhum desses passos afeta os desenvolvedores — o contrato deles (autenticar-se no gateway como eles mesmos) nunca muda.
Perguntas Frequentes
-
Preciso ter um contrato direto com a Anthropic para usar o Claude Code?
Não. Os modelos Claude rodam dentro do Microsoft Foundry e são faturados através da sua assinatura Azure. Não é necessário contrato ou chaves diretas com a Anthropic, eliminando a complexidade de procurement e gestão de fornecedores. -
Como fica a cobrança se eu usar Claude através do Foundry em vez de diretamente pela Anthropic?
Todo o consumo é faturado via Azure, consolidado na sua conta corporativa. O gateway emite métricas por usuário no Application Insights, permitindo chargeback por time ou desenvolvedor — algo inviável com chaves de API compartilhadas. -
É possível usar managed identity em vez de chave de API para autenticar entre APIM e Foundry?
Sim, essa é a abordagem recomendada para produção. Em vez de armazenar uma chave secreta, o APIM obtém um token Entra para sua própria managed identity e o utiliza para autenticar no Foundry. Isso elimina a necessidade de rotação de chaves e reduz a superfície de ataque. -
O que acontece se um desenvolvedor exceder o limite de tokens configurado no gateway?
O APIM retorna HTTP 429 (Too Many Requests) com o cabeçalho retry-after. O desenvolvedor precisa aguardar o período de renovação. As quotas podem ser mensais (token-quota) ou por minuto (tokens-per-minute), com camadas distintas para usuários Standard e Premium com base em app roles do Entra ID. -
Como garantir que a chave do Foundry não fique exposta nos laptops dos desenvolvedores?
A chave do Foundry nunca sai do APIM. Os desenvolvedores autenticam-se exclusivamente com tokens Entra de curta duração (60-90 minutos) obtidos via um script helper que roda localmente. O APIM faz a troca: remove o token do desenvolvedor e injeta a chave do Foundry antes de encaminhar a requisição.
Artigo originalmente publicado por Murali Kumanduri em Azure Updates - Latest from Azure Charts.