-
Notifications
You must be signed in to change notification settings - Fork 98
feat: add Claude Platform on AWS migration skill (.claude/skills/) #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
wirjo
wants to merge
4
commits into
aws-samples:main
Choose a base branch
from
wirjo:feat/claude-platform-on-aws-skill
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
347ac8f
feat: add Claude Platform on AWS migration skill
wirjo 2113c16
fix: address all review comments from @scouturier
wirjo 928b6ee
fix: correct EnvironmentWorker SDK usage and environment key prefix
wirjo 14a6b0d
chore: update model ID to claude-sonnet-5
wirjo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| --- | ||
| name: claude-platform-on-aws | ||
| description: Convert Anthropic 1P (direct API) code samples to Claude Platform on AWS (CPOA). Use when migrating from api.anthropic.com to aws-external-anthropic.{region}.api.aws, converting auth to IAM SigV4 or CPOA API key, adding workspace headers, adapting self-hosted sandbox workers for CPOA, or when someone asks about differences between Anthropic 1P and CPOA authentication/endpoints. Triggers on phrases like "convert to CPOA", "migrate to Claude Platform on AWS", "CPOA auth", "workspace header", "aws-external-anthropic", "SigV4 anthropic". | ||
| --- | ||
|
|
||
| # Claude Platform on AWS — Migration Skill | ||
|
|
||
| Convert Anthropic 1P API code to Claude Platform on AWS (CPOA). | ||
|
|
||
| ## Key Differences: 1P vs CPOA | ||
|
|
||
| | Aspect | Anthropic 1P | CPOA | | ||
| |--------|-------------|------| | ||
| | Base URL | `https://api.anthropic.com` | `https://aws-external-anthropic.{region}.api.aws` | | ||
| | Auth | API key (`sk-ant-api03-...`) in `x-api-key` header | IAM SigV4 OR CPOA API key (generated in AWS Console) | | ||
| | Required headers | `x-api-key`, `anthropic-version` | `anthropic-workspace-id` (handled by platform SDK clients) | | ||
| | SDK client | `Anthropic` | `AnthropicAWS` (Python) / `AnthropicAws` (TypeScript) | | ||
| | Billing | Anthropic billing | AWS Marketplace | | ||
| | Inference | Anthropic infra | Anthropic infra (same — CPOA only changes auth/billing layer) | | ||
| | Env keys (self-hosted workers) | `sk-ant-oat01-...` (from Claude Console) | NOT used — workers authenticate via IAM or CPOA API key | | ||
| | VPC access | Direct HTTPS to api.anthropic.com | PrivateLink available (VPC→AWS hop only; inference still on Anthropic) | | ||
| | Credential rotation | Manual API key management | Automatic via STS (IAM mode) | | ||
|
|
||
| ## Conversion Steps | ||
|
|
||
| ### 1. Identify Auth Mode | ||
|
|
||
| CPOA supports two auth modes: | ||
|
|
||
| - **IAM SigV4** (preferred for production): Uses execution role credentials via standard AWS provider chain. No secrets to manage. | ||
| - **API key** (dev/quick-start): Generated in AWS Console under Claude Platform on AWS → API keys. Short-term tokens generated from AWS credentials default to 12 hours (capped at the lesser of requested duration, AWS credentials' expiry, and 12 hours). | ||
|
|
||
| ### 2. Install the Platform SDK | ||
|
|
||
| The platform-specific SDK client handles SigV4 signing, base URL construction, and the `anthropic-workspace-id` header automatically. | ||
|
|
||
| ```bash | ||
| # Python | ||
| pip install -U "anthropic[aws]" | ||
|
|
||
| # TypeScript | ||
| npm install @anthropic-ai/aws-sdk | ||
| ``` | ||
|
|
||
| ### 3. Convert Client Initialization | ||
|
|
||
| See `references/code-patterns.md` for language-specific conversion patterns (Python, Node.js, curl). | ||
|
|
||
| The platform client reads `AWS_REGION` and `ANTHROPIC_AWS_WORKSPACE_ID` from environment variables. Set them: | ||
|
|
||
| ```bash | ||
| export AWS_REGION='us-west-2' | ||
| export ANTHROPIC_AWS_WORKSPACE_ID='wrkspc_XXXXX' | ||
| ``` | ||
|
|
||
| ### 4. Update Self-Hosted Sandbox Workers | ||
|
|
||
| For self-hosted environments on CPOA: | ||
| - Environment keys from the Claude Console **do not work** on the CPOA endpoint | ||
| - Workers authenticate via IAM (attach `AnthropicSelfHostedEnvironmentAccess` policy) or CPOA API key | ||
| - Use the `EnvironmentWorker` SDK helper (from `anthropic.lib.environments`) or call `GET /v1/environments/{id}/work/poll` directly | ||
|
|
||
| ### 5. IAM Permissions | ||
|
|
||
| Different policies for different use cases: | ||
|
|
||
| - **Inference** (`/v1/messages`): Attach `AnthropicInferenceAccess` managed policy (includes `CreateInference`) | ||
| - **Self-hosted workers**: Attach `AnthropicSelfHostedEnvironmentAccess` managed policy (includes `ProcessEnvironmentWork`, `GetEnvironment`, `GetSession`, `UpdateSession`, `GetSkill`, `CallWithBearerToken` — but NOT `CreateInference`) | ||
|
|
||
| A role with only `AnthropicSelfHostedEnvironmentAccess` will 403 on `/v1/messages`. Combine both policies if the worker also needs to make inference calls. | ||
|
|
||
| ### 6. Security Comparison | ||
|
|
||
| CPOA advantages for sandboxed/multi-tenant environments: | ||
| - No long-lived secrets — IAM creds rotate hourly via STS | ||
| - IAM policy scoping — restrict to specific actions | ||
| - Condition keys — `aws:SourceVpc`, `aws:SourceAccount` | ||
| - CloudTrail audit — native AWS logging | ||
| - SCP enforcement at org level | ||
|
|
||
| Tradeoff: more operational complexity (IAM setup, workspace subscription, outbound web identity federation enablement). | ||
|
|
||
| ## Common Pitfalls | ||
|
|
||
| 1. **Missing workspace header** → 400 error. Use the platform SDK client (`AnthropicAWS`/`AnthropicAws`) which adds it automatically, or set `ANTHROPIC_AWS_WORKSPACE_ID` env var. | ||
| 2. **Outbound web identity federation disabled** → Every request fails with "Outbound web identity federation is disabled for your account". Run `aws iam enable-outbound-web-identity-federation` once per AWS account. | ||
| 3. **Using Claude Console environment keys on CPOA** → Won't work. Workers on CPOA authenticate via IAM or CPOA API key instead. | ||
| 4. **Confusing IAM policies** → `AnthropicSelfHostedEnvironmentAccess` does NOT grant inference. Need `AnthropicInferenceAccess` for `/v1/messages`. | ||
| 5. **Region not set** → Unlike `AnthropicBedrock` (which falls back to `us-east-1`), the platform client raises an error if no region is set. | ||
| 6. **Account not CPOA-subscribed** → SigV4 will fail; ensure the AWS account has completed Claude Platform on AWS sign-up. |
286 changes: 286 additions & 0 deletions
286
.claude/skills/claude-platform-on-aws/references/code-patterns.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,286 @@ | ||
| # Code Patterns: 1P → CPOA Conversion | ||
|
|
||
| ## Python | ||
|
|
||
| ### Before (Anthropic 1P) | ||
|
|
||
| ```python | ||
| from anthropic import Anthropic | ||
|
|
||
| client = Anthropic(api_key="sk-ant-api03-...") | ||
|
|
||
| response = client.messages.create( | ||
| model="claude-sonnet-5", | ||
| max_tokens=1024, | ||
| messages=[{"role": "user", "content": "Hello"}], | ||
| ) | ||
| ``` | ||
|
|
||
| ### After (CPOA — API Key Mode) | ||
|
|
||
| ```python | ||
| from anthropic import AnthropicAWS # pip install -U "anthropic[aws]" | ||
|
|
||
| # API key generated in AWS Console under Claude Platform on AWS → API keys | ||
| # Region + workspace from AWS_REGION / ANTHROPIC_AWS_WORKSPACE_ID env vars | ||
| client = AnthropicAWS(api_key="<your-cpoa-api-key>") | ||
|
|
||
| response = client.messages.create( | ||
| model="claude-sonnet-5", | ||
| max_tokens=1024, | ||
| messages=[{"role": "user", "content": "Hello"}], | ||
| ) | ||
| ``` | ||
|
|
||
| ### After (CPOA — SigV4 IAM Mode) | ||
|
|
||
| ```python | ||
| from anthropic import AnthropicAWS # pip install -U "anthropic[aws]" | ||
|
|
||
| # No API key → SigV4 via default AWS credential chain | ||
| # (env → ~/.aws → IRSA → ECS → IMDS) | ||
| client = AnthropicAWS(aws_region="us-west-2") | ||
|
|
||
| response = client.messages.create( | ||
| model="claude-sonnet-5", | ||
| max_tokens=1024, | ||
| messages=[{"role": "user", "content": "Hello"}], | ||
| ) | ||
| ``` | ||
|
|
||
| ### After (CPOA — Short-Term Token) | ||
|
|
||
| ```python | ||
| from token_generator_for_aws_external_anthropic import TokenGenerator | ||
| from anthropic import AnthropicAWS | ||
|
|
||
| # Token defaults to 12h, capped at min(requested, AWS creds expiry, 12h) | ||
| token = TokenGenerator(region="us-west-2").get_token() | ||
|
|
||
| client = AnthropicAWS(api_key=token, aws_region="us-west-2") | ||
|
|
||
| response = client.messages.create( | ||
| model="claude-sonnet-5", | ||
| max_tokens=1024, | ||
| messages=[{"role": "user", "content": "Hello"}], | ||
| ) | ||
| ``` | ||
|
|
||
| ## Node.js / TypeScript | ||
|
|
||
| ### Before (Anthropic 1P) | ||
|
|
||
| ```typescript | ||
| import Anthropic from "@anthropic-ai/sdk"; | ||
|
|
||
| const client = new Anthropic({ apiKey: "sk-ant-api03-..." }); | ||
|
|
||
| const response = await client.messages.create({ | ||
| model: "claude-sonnet-5", | ||
| max_tokens: 1024, | ||
| messages: [{ role: "user", content: "Hello" }], | ||
| }); | ||
| ``` | ||
|
|
||
| ### After (CPOA — API Key Mode) | ||
|
|
||
| ```typescript | ||
| import AnthropicAws from "@anthropic-ai/aws-sdk"; // npm install @anthropic-ai/aws-sdk | ||
|
|
||
| // API key generated in AWS Console under Claude Platform on AWS → API keys | ||
| const client = new AnthropicAws({ | ||
| apiKey: "<your-cpoa-api-key>", | ||
| awsRegion: "us-west-2", | ||
| }); | ||
|
|
||
| const response = await client.messages.create({ | ||
| model: "claude-sonnet-5", | ||
| max_tokens: 1024, | ||
| messages: [{ role: "user", content: "Hello" }], | ||
| }); | ||
| ``` | ||
|
|
||
| ### After (CPOA — SigV4 IAM Mode) | ||
|
|
||
| ```typescript | ||
| import AnthropicAws from "@anthropic-ai/aws-sdk"; | ||
|
|
||
| // No API key → SigV4 via default AWS credential chain | ||
| const client = new AnthropicAws({ awsRegion: "us-west-2" }); | ||
|
|
||
| const response = await client.messages.create({ | ||
| model: "claude-sonnet-5", | ||
| max_tokens: 1024, | ||
| messages: [{ role: "user", content: "Hello" }], | ||
| }); | ||
| ``` | ||
|
|
||
| ### After (CPOA — Short-Term Token) | ||
|
|
||
| ```typescript | ||
| import { getTokenProvider } from "@aws/token-generator-for-aws-external-anthropic"; | ||
| import AnthropicAws from "@anthropic-ai/aws-sdk"; | ||
|
|
||
| const tokenProvider = getTokenProvider({ region: "us-west-2" }); | ||
| const token = await tokenProvider(); | ||
|
|
||
| const client = new AnthropicAws({ apiKey: token, awsRegion: "us-west-2" }); | ||
|
|
||
| const response = await client.messages.create({ | ||
| model: "claude-sonnet-5", | ||
| max_tokens: 1024, | ||
| messages: [{ role: "user", content: "Hello" }], | ||
| }); | ||
| ``` | ||
|
|
||
| ## curl | ||
|
|
||
| ### Before (Anthropic 1P) | ||
|
|
||
| ```bash | ||
| curl -X POST https://api.anthropic.com/v1/messages \ | ||
| -H "x-api-key: sk-ant-api03-..." \ | ||
| -H "anthropic-version: 2023-06-01" \ | ||
| -H "content-type: application/json" \ | ||
| -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' | ||
| ``` | ||
|
|
||
| ### After (CPOA — API Key Mode) | ||
|
|
||
| ```bash | ||
| curl -X POST https://aws-external-anthropic.us-west-2.api.aws/v1/messages \ | ||
| -H "x-api-key: <your-cpoa-api-key>" \ | ||
| -H "anthropic-workspace-id: wrkspc_XXXXX" \ | ||
| -H "anthropic-version: 2023-06-01" \ | ||
| -H "content-type: application/json" \ | ||
| -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' | ||
| ``` | ||
|
|
||
| ### After (CPOA — SigV4 Mode) | ||
|
|
||
| ```bash | ||
| # Uses curl's --aws-sigv4 flag (requires curl 7.75+) | ||
| curl -X POST "https://aws-external-anthropic.us-west-2.api.aws/v1/messages" \ | ||
| --aws-sigv4 "aws:amz:us-west-2:aws-external-anthropic" \ | ||
| --user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \ | ||
| -H "x-amz-security-token: $AWS_SESSION_TOKEN" \ | ||
| -H "anthropic-workspace-id: $ANTHROPIC_AWS_WORKSPACE_ID" \ | ||
| -H "anthropic-version: 2023-06-01" \ | ||
| -H "content-type: application/json" \ | ||
| -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' | ||
| ``` | ||
|
|
||
| ## Self-Hosted Sandbox Workers | ||
|
|
||
| ### Before (1P — Environment Key) | ||
|
|
||
| ```python | ||
| import asyncio | ||
| import os | ||
| from anthropic import AsyncAnthropic | ||
| from anthropic.lib.environments import EnvironmentWorker | ||
|
|
||
| async def main() -> None: | ||
| environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"] # sk-ant-oat01-... | ||
| environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] | ||
| async with AsyncAnthropic(auth_token=environment_key) as client: | ||
| await EnvironmentWorker( | ||
| client, | ||
| environment_id=environment_id, | ||
| environment_key=environment_key, | ||
| workdir="/workspace", | ||
| ).run() | ||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| ### After (CPOA — Worker with IAM Auth) | ||
|
|
||
| ```python | ||
| import asyncio | ||
| import os | ||
| from anthropic import AsyncAnthropic | ||
| from anthropic.lib.environments import EnvironmentWorker | ||
|
|
||
| async def main() -> None: | ||
| # On CPOA: no environment key needed — authenticate via IAM | ||
| # Requires AnthropicSelfHostedEnvironmentAccess IAM policy | ||
| environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] | ||
| async with AsyncAnthropic() as client: # SigV4 via AWS credential chain | ||
| await EnvironmentWorker( | ||
| client, | ||
| environment_id=environment_id, | ||
| workdir="/workspace", | ||
| ).run() | ||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| ### After (CPOA — Worker with API Key) | ||
|
|
||
| ```python | ||
| import asyncio | ||
| import os | ||
| from anthropic import AsyncAnthropic | ||
| from anthropic.lib.environments import EnvironmentWorker | ||
|
|
||
| async def main() -> None: | ||
| # On CPOA: use API key generated in AWS Console | ||
| api_key = os.environ["ANTHROPIC_AWS_API_KEY"] | ||
| environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] | ||
| async with AsyncAnthropic(auth_token=api_key) as client: | ||
| await EnvironmentWorker( | ||
| client, | ||
| environment_id=environment_id, | ||
| workdir="/workspace", | ||
| ).run() | ||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| ### Session Creation (CPOA) | ||
|
|
||
| ```python | ||
| from anthropic import AnthropicAWS | ||
|
|
||
| client = AnthropicAWS(aws_region="us-west-2") | ||
|
|
||
| # Create session targeting an agent in a self-hosted environment | ||
| session = client.beta.sessions.create( | ||
| agent="agent_XXXXX", | ||
| environment_id="env_XXXXX", | ||
| ) | ||
|
|
||
| # Send user message | ||
| client.beta.sessions.events.send( | ||
| session_id=session.id, | ||
| events=[{ | ||
| "type": "user.message", | ||
| "content": [{"type": "text", "text": "Analyze the data in /mnt/data/"}], | ||
| }], | ||
| ) | ||
|
|
||
| # Session transitions: idle → running (work queued for worker pickup) | ||
| ``` | ||
|
|
||
| ## Environment Variables Mapping | ||
|
|
||
| | 1P Variable | CPOA Variable | Notes | | ||
| |-------------|---------------|-------| | ||
| | `ANTHROPIC_API_KEY` | (constructor `api_key`) | CPOA keys generated in AWS Console | | ||
| | (none) | `ANTHROPIC_AWS_WORKSPACE_ID` | Required — SDK reads automatically | | ||
| | (none) | `AWS_REGION` | Required — no fallback default | | ||
| | `ANTHROPIC_BASE_URL` | (handled by SDK) | `AnthropicAWS`/`AnthropicAws` sets it from region | | ||
| | `ANTHROPIC_ENVIRONMENT_KEY` | (not used on CPOA) | 1P uses `sk-ant-oat01-...`; CPOA workers use IAM or API key instead | | ||
|
|
||
| ## Validation Checklist | ||
|
|
||
| After conversion, verify: | ||
|
|
||
| 1. [ ] Using platform SDK client (`AnthropicAWS` / `AnthropicAws`), not base `Anthropic` with manual URL | ||
| 2. [ ] `AWS_REGION` and `ANTHROPIC_AWS_WORKSPACE_ID` environment variables set | ||
| 3. [ ] No references to `sk-ant-api03-` or `sk-ant-oat01-` keys (1P credentials) | ||
| 4. [ ] Outbound web identity federation enabled (`aws iam enable-outbound-web-identity-federation`) | ||
| 5. [ ] Correct IAM policy: `AnthropicInferenceAccess` for `/v1/messages`, `AnthropicSelfHostedEnvironmentAccess` for workers | ||
| 6. [ ] `/v1/models` endpoint returns 200 (basic connectivity test) | ||
| 7. [ ] Short-term token refresh logic in place if using generated tokens (no auto-refresh) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing that slipped past the earlier round: the "Worker with IAM Auth" block still uses bare
AsyncAnthropic(), and the comment# SigV4 via AWS credential chainis inaccurate — baseAsyncAnthropictargetsapi.anthropic.comand can't SigV4-sign (same defect we fixed for the inference examples at :49/:109). We corrected the prose for this in SKILL.md:19 but not this code block.Two things to reconcile:
EnvironmentWorkerisn't actually a documented pattern (the self-hosted docs only show the async worker withauth_token=<key>), mark this variant as unverified rather than shipping it as working. The API-key worker block just below is fine as-is.