feat: add Microsoft Foundry provider support (#684)

* feat: add Azure AI Foundry provider support

Add support for Azure AI Foundry as a fourth cloud provider option alongside Anthropic API, AWS Bedrock, and Google Vertex AI.

Changes:
- Add use_foundry input to enable Azure AI Foundry authentication
- Add Azure environment variables (ANTHROPIC_FOUNDRY_RESOURCE, ANTHROPIC_FOUNDRY_API_KEY, ANTHROPIC_FOUNDRY_BASE_URL)
- Support automatic base URL construction from resource name
- Add validation logic with mutual exclusivity checks for all providers
- Add comprehensive test coverage (7 Azure-specific tests, 3 mutual exclusivity tests)
- Add complete Azure AI Foundry documentation with OIDC and API key authentication
- Update README to reference Azure AI Foundry support

Features:
- Primary authentication via Microsoft Entra ID (OIDC) using azure/login action
- Optional API key authentication fallback
- Custom model deployment name support via ANTHROPIC_DEFAULT_*_MODEL variables
- Clear validation error messages for missing configuration

All tests pass (25 validation tests total).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: rename Azure AI Foundry to Microsoft Foundry and remove API key support

- Rename all references from "Azure AI Foundry" to "Microsoft Foundry"
- Remove ANTHROPIC_FOUNDRY_API_KEY support (OIDC only)
- Update documentation to reflect OIDC-only authentication
- Update tests to remove API key test case

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: simplify Microsoft Foundry setup and remove URL auto-construction

- Link to official docs instead of duplicating setup instructions
- Remove automatic base URL construction from resource name
- Pass ANTHROPIC_FOUNDRY_BASE_URL as-is

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Ashwin Bhat
2025-11-20 13:50:13 -08:00
committed by GitHub
parent c2edeab4c3
commit f092d4cefd
6 changed files with 189 additions and 13 deletions

View File

@@ -2,7 +2,7 @@
# Claude Code Action
A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs and issues that can answer questions and implement code changes. This action intelligently detects when to activate based on your workflow context—whether responding to @claude mentions, issue assignments, or executing automation tasks with explicit prompts. It supports multiple authentication methods including Anthropic direct API, Amazon Bedrock, and Google Vertex AI.
A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs and issues that can answer questions and implement code changes. This action intelligently detects when to activate based on your workflow context—whether responding to @claude mentions, issue assignments, or executing automation tasks with explicit prompts. It supports multiple authentication methods including Anthropic direct API, Amazon Bedrock, Google Vertex AI, and Microsoft Foundry.
## Features
@@ -30,7 +30,7 @@ This command will guide you through setting up the GitHub app and required secre
**Note**:
- You must be a repository admin to install the GitHub app and add secrets
- This quickstart method is only available for direct Anthropic API users. For AWS Bedrock or Google Vertex AI setup, see [docs/cloud-providers.md](./docs/cloud-providers.md).
- This quickstart method is only available for direct Anthropic API users. For AWS Bedrock, Google Vertex AI, or Microsoft Foundry setup, see [docs/cloud-providers.md](./docs/cloud-providers.md).
## 📚 Solutions & Use Cases
@@ -57,7 +57,7 @@ Each solution includes complete working examples, configuration details, and exp
- [Custom Automations](./docs/custom-automations.md) - Examples of automated workflows and custom prompts
- [Configuration](./docs/configuration.md) - MCP servers, permissions, environment variables, and advanced settings
- [Experimental Features](./docs/experimental.md) - Execution modes and network restrictions
- [Cloud Providers](./docs/cloud-providers.md) - AWS Bedrock and Google Vertex AI setup
- [Cloud Providers](./docs/cloud-providers.md) - AWS Bedrock, Google Vertex AI, and Microsoft Foundry setup
- [Capabilities & Limitations](./docs/capabilities-and-limitations.md) - What Claude can and cannot do
- [Security](./docs/security.md) - Access control, permissions, and commit signing
- [FAQ](./docs/faq.md) - Common questions and troubleshooting

View File

@@ -44,7 +44,7 @@ inputs:
# Auth configuration
anthropic_api_key:
description: "Anthropic API key (required for direct API, not needed for Bedrock/Vertex)"
description: "Anthropic API key (required for direct API, not needed for Bedrock/Vertex/Foundry)"
required: false
claude_code_oauth_token:
description: "Claude Code OAuth token (alternative to anthropic_api_key)"
@@ -60,6 +60,10 @@ inputs:
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
required: false
default: "false"
use_foundry:
description: "Use Microsoft Foundry with OIDC authentication instead of direct Anthropic API"
required: false
default: "false"
claude_args:
description: "Additional arguments to pass directly to Claude CLI"
@@ -244,6 +248,7 @@ runs:
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
CLAUDE_CODE_USE_FOUNDRY: ${{ inputs.use_foundry == 'true' && '1' || '' }}
# AWS configuration
AWS_REGION: ${{ env.AWS_REGION }}
@@ -264,6 +269,13 @@ runs:
VERTEX_REGION_CLAUDE_3_5_SONNET: ${{ env.VERTEX_REGION_CLAUDE_3_5_SONNET }}
VERTEX_REGION_CLAUDE_3_7_SONNET: ${{ env.VERTEX_REGION_CLAUDE_3_7_SONNET }}
# Microsoft Foundry configuration
ANTHROPIC_FOUNDRY_RESOURCE: ${{ env.ANTHROPIC_FOUNDRY_RESOURCE }}
ANTHROPIC_FOUNDRY_BASE_URL: ${{ env.ANTHROPIC_FOUNDRY_BASE_URL }}
ANTHROPIC_DEFAULT_SONNET_MODEL: ${{ env.ANTHROPIC_DEFAULT_SONNET_MODEL }}
ANTHROPIC_DEFAULT_HAIKU_MODEL: ${{ env.ANTHROPIC_DEFAULT_HAIKU_MODEL }}
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}
- name: Update comment with job link
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
shell: bash

View File

@@ -42,6 +42,10 @@ inputs:
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
required: false
default: "false"
use_foundry:
description: "Use Microsoft Foundry with OIDC authentication instead of direct Anthropic API"
required: false
default: "false"
use_node_cache:
description: "Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files)"
@@ -153,6 +157,7 @@ runs:
# Only set provider flags if explicitly true, since any value (including "false") is truthy
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
CLAUDE_CODE_USE_FOUNDRY: ${{ inputs.use_foundry == 'true' && '1' || '' }}
# AWS configuration
AWS_REGION: ${{ env.AWS_REGION }}
@@ -167,3 +172,10 @@ runs:
CLOUD_ML_REGION: ${{ env.CLOUD_ML_REGION }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
ANTHROPIC_VERTEX_BASE_URL: ${{ env.ANTHROPIC_VERTEX_BASE_URL }}
# Microsoft Foundry configuration
ANTHROPIC_FOUNDRY_RESOURCE: ${{ env.ANTHROPIC_FOUNDRY_RESOURCE }}
ANTHROPIC_FOUNDRY_BASE_URL: ${{ env.ANTHROPIC_FOUNDRY_BASE_URL }}
ANTHROPIC_DEFAULT_SONNET_MODEL: ${{ env.ANTHROPIC_DEFAULT_SONNET_MODEL }}
ANTHROPIC_DEFAULT_HAIKU_MODEL: ${{ env.ANTHROPIC_DEFAULT_HAIKU_MODEL }}
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}

View File

@@ -1,22 +1,25 @@
/**
* Validates the environment variables required for running Claude Code
* based on the selected provider (Anthropic API, AWS Bedrock, or Google Vertex AI)
* based on the selected provider (Anthropic API, AWS Bedrock, Google Vertex AI, or Microsoft Foundry)
*/
export function validateEnvironmentVariables() {
const useBedrock = process.env.CLAUDE_CODE_USE_BEDROCK === "1";
const useVertex = process.env.CLAUDE_CODE_USE_VERTEX === "1";
const useFoundry = process.env.CLAUDE_CODE_USE_FOUNDRY === "1";
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
const claudeCodeOAuthToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
const errors: string[] = [];
if (useBedrock && useVertex) {
// Check for mutual exclusivity between providers
const activeProviders = [useBedrock, useVertex, useFoundry].filter(Boolean);
if (activeProviders.length > 1) {
errors.push(
"Cannot use both Bedrock and Vertex AI simultaneously. Please set only one provider.",
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
);
}
if (!useBedrock && !useVertex) {
if (!useBedrock && !useVertex && !useFoundry) {
if (!anthropicApiKey && !claudeCodeOAuthToken) {
errors.push(
"Either ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN is required when using direct Anthropic API.",
@@ -53,6 +56,16 @@ export function validateEnvironmentVariables() {
errors.push(`${key} is required when using Google Vertex AI.`);
}
});
} else if (useFoundry) {
const foundryResource = process.env.ANTHROPIC_FOUNDRY_RESOURCE;
const foundryBaseUrl = process.env.ANTHROPIC_FOUNDRY_BASE_URL;
// Either resource name or base URL is required
if (!foundryResource && !foundryBaseUrl) {
errors.push(
"Either ANTHROPIC_FOUNDRY_RESOURCE or ANTHROPIC_FOUNDRY_BASE_URL is required when using Microsoft Foundry.",
);
}
}
if (errors.length > 0) {

View File

@@ -13,6 +13,7 @@ describe("validateEnvironmentVariables", () => {
delete process.env.ANTHROPIC_API_KEY;
delete process.env.CLAUDE_CODE_USE_BEDROCK;
delete process.env.CLAUDE_CODE_USE_VERTEX;
delete process.env.CLAUDE_CODE_USE_FOUNDRY;
delete process.env.AWS_REGION;
delete process.env.AWS_ACCESS_KEY_ID;
delete process.env.AWS_SECRET_ACCESS_KEY;
@@ -23,6 +24,8 @@ describe("validateEnvironmentVariables", () => {
delete process.env.CLOUD_ML_REGION;
delete process.env.GOOGLE_APPLICATION_CREDENTIALS;
delete process.env.ANTHROPIC_VERTEX_BASE_URL;
delete process.env.ANTHROPIC_FOUNDRY_RESOURCE;
delete process.env.ANTHROPIC_FOUNDRY_BASE_URL;
});
afterEach(() => {
@@ -195,6 +198,56 @@ describe("validateEnvironmentVariables", () => {
});
});
describe("Microsoft Foundry", () => {
test("should pass when ANTHROPIC_FOUNDRY_RESOURCE is provided", () => {
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
expect(() => validateEnvironmentVariables()).not.toThrow();
});
test("should pass when ANTHROPIC_FOUNDRY_BASE_URL is provided", () => {
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
process.env.ANTHROPIC_FOUNDRY_BASE_URL =
"https://test-resource.services.ai.azure.com";
expect(() => validateEnvironmentVariables()).not.toThrow();
});
test("should pass when both resource and base URL are provided", () => {
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
process.env.ANTHROPIC_FOUNDRY_BASE_URL =
"https://custom.services.ai.azure.com";
expect(() => validateEnvironmentVariables()).not.toThrow();
});
test("should construct Foundry base URL from resource name when ANTHROPIC_FOUNDRY_BASE_URL is not provided", () => {
// This test verifies our action.yml change, which constructs:
// ANTHROPIC_FOUNDRY_BASE_URL: ${{ env.ANTHROPIC_FOUNDRY_BASE_URL || (env.ANTHROPIC_FOUNDRY_RESOURCE && format('https://{0}.services.ai.azure.com', env.ANTHROPIC_FOUNDRY_RESOURCE)) }}
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "my-foundry-resource";
// ANTHROPIC_FOUNDRY_BASE_URL is intentionally not set
// The actual URL construction happens in the composite action in action.yml
// This test is a placeholder to document the behavior
expect(() => validateEnvironmentVariables()).not.toThrow();
// In the actual action, ANTHROPIC_FOUNDRY_BASE_URL would be:
// https://my-foundry-resource.services.ai.azure.com
});
test("should fail when neither ANTHROPIC_FOUNDRY_RESOURCE nor ANTHROPIC_FOUNDRY_BASE_URL is provided", () => {
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
expect(() => validateEnvironmentVariables()).toThrow(
"Either ANTHROPIC_FOUNDRY_RESOURCE or ANTHROPIC_FOUNDRY_BASE_URL is required when using Microsoft Foundry.",
);
});
});
describe("Multiple providers", () => {
test("should fail when both Bedrock and Vertex are enabled", () => {
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
@@ -207,7 +260,51 @@ describe("validateEnvironmentVariables", () => {
process.env.CLOUD_ML_REGION = "us-central1";
expect(() => validateEnvironmentVariables()).toThrow(
"Cannot use both Bedrock and Vertex AI simultaneously. Please set only one provider.",
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
);
});
test("should fail when both Bedrock and Foundry are enabled", () => {
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
// Provide all required vars to isolate the mutual exclusion error
process.env.AWS_REGION = "us-east-1";
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
expect(() => validateEnvironmentVariables()).toThrow(
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
);
});
test("should fail when both Vertex and Foundry are enabled", () => {
process.env.CLAUDE_CODE_USE_VERTEX = "1";
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
// Provide all required vars to isolate the mutual exclusion error
process.env.ANTHROPIC_VERTEX_PROJECT_ID = "test-project";
process.env.CLOUD_ML_REGION = "us-central1";
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
expect(() => validateEnvironmentVariables()).toThrow(
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
);
});
test("should fail when all three providers are enabled", () => {
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
process.env.CLAUDE_CODE_USE_VERTEX = "1";
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
// Provide all required vars to isolate the mutual exclusion error
process.env.AWS_REGION = "us-east-1";
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
process.env.ANTHROPIC_VERTEX_PROJECT_ID = "test-project";
process.env.CLOUD_ML_REGION = "us-central1";
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
expect(() => validateEnvironmentVariables()).toThrow(
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
);
});
});

View File

@@ -1,16 +1,17 @@
# Cloud Providers
You can authenticate with Claude using any of these three methods:
You can authenticate with Claude using any of these four methods:
1. Direct Anthropic API (default)
2. Amazon Bedrock with OIDC authentication
3. Google Vertex AI with OIDC authentication
4. Microsoft Foundry with OIDC authentication
For detailed setup instructions for AWS Bedrock and Google Vertex AI, see the [official documentation](https://docs.anthropic.com/en/docs/claude-code/github-actions#using-with-aws-bedrock-%26-google-vertex-ai).
**Note**:
- Bedrock and Vertex use OIDC authentication exclusively
- Bedrock, Vertex, and Microsoft Foundry use OIDC authentication exclusively
- AWS Bedrock automatically uses cross-region inference profiles for certain models
- For cross-region inference profile models, you need to request and be granted access to the Claude models in all regions that the inference profile uses
@@ -40,11 +41,19 @@ Use provider-specific model names based on your chosen provider:
claude_args: |
--model claude-4-0-sonnet@20250805
# ... other inputs
# For Microsoft Foundry with OIDC
- uses: anthropics/claude-code-action@v1
with:
use_foundry: "true"
claude_args: |
--model claude-sonnet-4-5
# ... other inputs
```
## OIDC Authentication for Bedrock and Vertex
## OIDC Authentication for Cloud Providers
Both AWS Bedrock and GCP Vertex AI require OIDC authentication.
AWS Bedrock, GCP Vertex AI, and Microsoft Foundry all support OIDC authentication.
```yaml
# For AWS Bedrock with OIDC
@@ -97,3 +106,36 @@ Both AWS Bedrock and GCP Vertex AI require OIDC authentication.
permissions:
id-token: write # Required for OIDC
```
```yaml
# For Microsoft Foundry with OIDC
- name: Authenticate to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: anthropics/claude-code-action@v1
with:
use_foundry: "true"
claude_args: |
--model claude-sonnet-4-5
# ... other inputs
env:
ANTHROPIC_FOUNDRY_BASE_URL: https://my-resource.services.ai.azure.com
permissions:
id-token: write # Required for OIDC
```
## Microsoft Foundry Setup
For detailed setup instructions for Microsoft Foundry, see the [official documentation](https://docs.anthropic.com/en/docs/claude-code/microsoft-foundry).