4 de junho de 202615 min de leitura

Como disponibilizar Claude Code para sua equipe com Azure API Management e Microsoft Foundry

Murali Kumanduri

Azure

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:

  1. 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).
  2. 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.
  3. 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.

  1. App registrations → New registration → nomeie como Claude Code Gateway.
  2. Expose an API → defina o Application ID URI, ex.: api://claude-code-gateway. Adicione um scope access_as_user.
  3. (Opcional, para tiering) App roles → adicione papéis como Claude.Standard e Claude.Premium. Atribua desenvolvedores ou grupos em Enterprise applications → este app → Users and groups.
  4. 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-key incorreto ou não injetado
  • Excedeu limite → 429 com retry-after
  • App Insights → customMetrics mostra contagem de tokens por UserId

Passo 8 — Operações e hardening

  • Rotação de chave (Opção A): Foundry fornece duas chaves. Rotacione atualizando o named value foundry-api-key para key2 e depois regerando key1 — 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-limit por 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.

Gostou? Compartilhe:
Precisa de ajuda?Fale com nossos especialistas 👋
Avatar Walcew - Headset