Skip to content

Instantly share code, notes, and snippets.

@swateek
Last active April 20, 2026 16:55
Show Gist options
  • Select an option

  • Save swateek/1c666799a9a7c722ca645e8aca39aa89 to your computer and use it in GitHub Desktop.

Select an option

Save swateek/1c666799a9a7c722ca645e8aca39aa89 to your computer and use it in GitHub Desktop.
Setup Claude Code In Envitonment

setup-claude-code-developer

Onboards a developer with Claude Code on AWS Bedrock, creating per-developer Application Inference Profiles for cost tracking.

Requirements

  • Ubuntu/Linux or macOS (Bash 4+ required on macOS — install via brew install bash)
  • AWS CLI v2 installed and configured with a dev profile
  • Bedrock Anthropic model access enabled in at least one AWS region
  • Administrator access to install global npm packages
  • Node.js 18+ (script will install if missing)

Quick Start

Linux

./setup-claude-code-developer.sh --create --developer "Your Name"
source ~/.bashrc  # or ~/.zshrc
claude

macOS

The default macOS bash is version 3.2. The script detects this and re-executes with Homebrew's bash (4+).

# Install Bash 4+ if not already installed
brew install bash

/opt/homebrew/bin/bash setup-claude-code-developer.sh --create --developer "Your Name"
source ~/.zshrc  # or ~/.bashrc
claude

The script will:

  • Validate prerequisites (AWS CLI, Node.js, AWS profile)
  • Auto-detect the Bedrock region with Anthropic model support
  • Create six Application Inference Profiles (Opus 4.6, Opus 4.7, Sonnet, Haiku, Qwen3 x2)
  • Configure Claude Code with model overrides and customApiKeyModels for the /model picker
  • Set up environment variables and install Claude Code CLI
  • Configure cost tracking and verify the complete setup

Usage

# Provision profiles and configure the environment
./setup-claude-code-developer.sh --create --developer <name>

# Add any missing profiles without touching existing ones (idempotent)
./setup-claude-code-developer.sh --update --developer <name>

# Tear down all profiles and local config for a developer
./setup-claude-code-developer.sh --delete --developer <name>

# Override AWS region (default: us-east-1, auto-fallback to nearest region with Anthropic support)
AWS_REGION=eu-west-1 ./setup-claude-code-developer.sh --create --developer <name>

# Bypass auto-discovery if you already know the Bedrock region
./setup-claude-code-developer.sh --create --developer <name> --bedrock-region us-east-1

What Gets Created

Bedrock Application Inference Profiles

Six profiles are created in the auto-discovered Bedrock region. Profiles are skipped with a warning if the model is unavailable in that region.

Profile name Model Purpose
claude-opus-4-6-<name> Claude Opus 4.6 Complex reasoning, architecture design, debugging
claude-opus-4-7-<name> Claude Opus 4.7 Most demanding tasks, latest and highest-capability model
claude-sonnet-<name> Claude Sonnet 4.6 Balanced use — recommended default for most tasks
claude-haiku-<name> Claude Haiku 4.5 Fast queries, simple tasks, lowest cost
qwen3-next-80b-a3b-<name> Qwen3 Next 80B A3B Large open-weight reasoning and coding alternative
qwen3-coder-next-<name> Qwen3 Coder Next Coding-specialized Qwen3 model

Each profile is tagged with developer=<name>, model=<type>, and cost-tracking=enabled for Cost Explorer filtering.

The script automatically discovers which Bedrock region supports Anthropic models. If your preferred AWS region doesn't have Anthropic models, it finds and uses the closest available region.

Local Configuration

Created in ~/.claude-code/:

File Purpose
aws-config.sh AWS region exports (includes BEDROCK_REGION)
activate.sh Shell environment with model ARN exports
claude-code-config.json Model overrides for Claude Code
COST_TRACKING_GUIDE.md Cost tracking instructions
cost-tracking-queries.txt CloudWatch Insights queries

Daily Usage

# Start Claude Code
claude

# Resume previous session
/continue

# Switch models (inside Claude Code)
/model

# Check current status
/status

# View all available commands
/help

Available Models

Model Use case Cost
Haiku 4.5 Quick questions, simple edits, summaries Cheapest
Sonnet 4.6 General coding, analysis — default choice Balanced
Opus 4.6 Complex reasoning, architecture design High
Opus 4.7 Most demanding tasks, latest model Highest
qwen3-next-80b-a3b Large reasoning/coding alternative (open-weight)
qwen3-coder-next Coding-specialized Qwen3 model

Switch models with /model inside Claude Code without losing conversation context.

Pro Tips

  • Start with Haiku for quick questions; switch to Sonnet if you need more depth
  • Use claude for new projects, /continue to resume the last session
  • Change model mid-conversation with /model — context is preserved
  • Costs appear in Cost Explorer after 24 hours; CloudWatch Insights is real-time

Updating an Existing Setup

If new models are added to the script after your initial --create, use --update to sync without disrupting existing profiles:

./setup-claude-code-developer.sh --update --developer "Your Name"
source ~/.bashrc  # or ~/.zshrc

The update is idempotent and additive: checks each of the 6 expected profiles, creates any that are missing, leaves existing profiles untouched, and regenerates ~/.claude-code/claude-code-config.json and ~/.claude-code/activate.sh with current ARNs. It does not duplicate shell RC entries already added by --create.

Use --update when:

  • New models were added to the setup script after your initial --create
  • Your local config files (activate.sh, claude-code-config.json) are out of date
  • You want to verify and repair your setup without disrupting existing profiles

Cost Tracking

Costs are tracked via AWS Cost Allocation Tags on each profile (developer=<name>, model=<type>).

Viewing Costs — AWS Cost Explorer (Simplest)

  1. AWS Console → Cost Management → Cost Explorer
  2. Click FiltersTagsdeveloper = your name
  3. Click Group ByTagsmodel
  4. Set date range and view costs

Viewing Costs — CloudWatch Logs (Real-time)

  1. AWS Console → BedrockModel Invocation Logging → enable CloudWatch Logs
  2. Use the queries from ~/.claude-code/cost-tracking-queries.txt

Example query:

fields @timestamp, model, inputTokens, outputTokens
| filter tags.developer = "john-smith"
| stats sum(inputTokens) as input, sum(outputTokens) as output by model

Cost Optimization

Model Best for Approx. input cost
Haiku 4.5 Simple questions, summaries, quick edits $0.80/M tokens
Sonnet 4.6 General coding, analysis $3/M tokens
Opus 4.6 Complex reasoning, architecture $15/M tokens
Opus 4.7 Most demanding tasks, latest model $15/M tokens

Strategy: Default to Haiku for quick queries, Sonnet for normal development, Opus only for complex multi-step reasoning. Monitor weekly in Cost Explorer.

Team-Level Cost Tracking

To track costs by team in addition to individual developer:

aws bedrock create-inference-profile \
  --inference-profile-name "claude-sonnet-john-smith" \
  --tags \
    "key=developer,value=john-smith" \
    "key=team,value=engineering" \
    "key=model,value=sonnet"

Then filter in Cost Explorer by both team and developer tags.

Environment Variables & Configuration

# View current setup
cat ~/.claude-code/aws-config.sh
env | grep -E "ANTHROPIC|BEDROCK|AWS"

# View active model ARN
echo $ANTHROPIC_MODEL

# Switch models by exporting a different ARN variable
export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_HAIKU_MODEL"    # Fast & cheap
export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_SONNET_MODEL"   # Default
export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_OPUS_46_MODEL"  # Opus 4.6
export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_OPUS_47_MODEL"  # Opus 4.7 (latest)
export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_QWEN3_NEXT_MODEL"  # Qwen3 large
export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_QWEN3_CODER_MODEL" # Qwen3 coder

# Reload environment
source ~/.claude-code/activate.sh

# Check Bedrock region being used
grep BEDROCK_REGION ~/.claude-code/aws-config.sh

Troubleshooting

Problem Solution
"AWS profile not found" aws configure --profile dev
"Claude Code won't start" npm install -g @anthropic-ai/claude-code
"No Bedrock region found" Enable Anthropic model access in AWS Bedrock console
"Can't see costs" Wait 24 hours for Cost Explorer to update
"Using wrong model" source ~/.claude-code/activate.sh
"Missing Qwen3 profiles" ./setup-claude-code-developer.sh --update --developer "Your Name"

"AWS credentials not found"

aws --profile dev sts get-caller-identity
# If it fails:
aws configure --profile dev

"Inference profile not found"

# List profiles in the Bedrock region
aws --profile dev bedrock list-inference-profiles --region us-east-1

# Add missing profiles without touching existing ones (safe, idempotent)
./setup-claude-code-developer.sh --update --developer "your-name"

# Recreate all profiles from scratch (destructive — deletes and recreates)
./setup-claude-code-developer.sh --create --developer "your-name"

"No Bedrock region with Anthropic models found"

The script prints per-region diagnostic lines when it checks each candidate region. To verify manually:

for region in us-east-1 us-west-2 eu-west-1; do
  echo -n "Checking $region... foundation models: "
  aws --profile dev bedrock list-foundation-models \
    --region "$region" --by-provider anthropic \
    --query "length(modelSummaries)" --output text
  echo -n "  system inference profiles: "
  aws --profile dev bedrock list-inference-profiles \
    --region "$region" --type-equals SYSTEM_DEFINED \
    --query "length(inferenceProfileSummaries[?contains(inferenceProfileId, 'claude')])" \
    --output text
done
  • Calls succeed but return 0 → Go to AWS Console → Bedrock → Model Access, request Anthropic model access (recommended: us-east-1), wait for approval, re-run setup.
  • Calls fail with access-denied → Ensure your dev profile has bedrock:ListFoundationModels and bedrock:ListInferenceProfiles permissions.
  • You already know the region → Bypass discovery with --bedrock-region:
./setup-claude-code-developer.sh --create --developer "john" --bedrock-region us-east-1

"Claude Code won't start"

node --version  # must be 18+

# Install Node.js 18 if missing
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs

npm install -g @anthropic-ai/claude-code

Advanced Configuration

Customizing the Default Model

Edit ~/.claude-code/activate.sh and change:

export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_HAIKU_MODEL"  # Start with Haiku instead

Multiple AWS Regions

# Default region (us-east-1)
./setup-claude-code-developer.sh --create --developer "john-smith"

# Different region (creates separate profiles)
AWS_REGION=eu-west-1 ./setup-claude-code-developer.sh --create --developer "john-smith-eu"

Cleanup & Offboarding

# Remove all setup: Bedrock profiles, ~/.claude-code/, and shell RC block
./setup-claude-code-developer.sh --delete --developer "Your Name"
source ~/.bashrc  # or ~/.zshrc

The Claude Code CLI is not removed automatically. To uninstall it:

npm uninstall -g @anthropic-ai/claude-code

To delete a single profile manually:

aws --profile dev bedrock delete-inference-profile \
  --region us-east-1 \
  --inference-profile-arn arn:aws:bedrock:us-east-1:ACCOUNT:application-inference-profile/claude-sonnet-old-employee

The setup script is safe to re-run — it checks if profiles already exist and skips creation to avoid duplicates.

Architecture

Developer Machine
├── ~/.claude-code/
│   ├── aws-config.sh (includes BEDROCK_REGION)
│   ├── claude-code-config.json
│   ├── activate.sh
│   └── COST_TRACKING_GUIDE.md
│
└── Claude Code CLI
    └── ANTHROPIC_MODEL env var
        └── Points to Inference Profile ARN
            └── AWS Bedrock (auto-discovered region)
                ├── Application Inference Profile
                │   └── Tagged: developer=john-smith, model=sonnet
                └── Anthropic Model (Claude Sonnet 4.6)
                    └── Usage metrics → CloudWatch / Cost Explorer

Region discovery flow:

Script Start
├── Check preferred AWS_REGION (default: us-east-1)
├── If Anthropic models available → Use it
└── If not → Check candidates (us-west-2, eu-west-1, etc.)
    └── Create profiles in first region with Anthropic support

FAQ

Q: Can multiple developers share the same profiles? A: No — each developer gets their own profiles for accurate cost tracking. Sharing prevents per-developer cost isolation.

Q: What happens if I switch AWS regions? A: New profiles are created in the new region. Old profiles remain but won't be used.

Q: Can I see costs in real-time? A: Use CloudWatch Insights queries for real-time data. Cost Explorer updates daily.

Q: What's the cheapest way to use Claude Code? A: Use Haiku for most tasks; switch to Sonnet or Opus only when needed.

Q: Can I use Qwen3 models as an alternative to Claude? A: Yes. Two Qwen3 profiles are provisioned automatically. Switch with /model or export ANTHROPIC_MODEL="$ANTHROPIC_DEFAULT_QWEN3_NEXT_MODEL" (or QWEN3_CODER). The default model remains Sonnet — Qwen3 is opt-in.

Q: How do I add models that were added to the script after my initial setup? A: Run ./setup-claude-code-developer.sh --update --developer "Your Name". It adds missing profiles without touching existing ones.

Q: How do I delete my profiles? A: Run ./setup-claude-code-developer.sh --delete --developer "Your Name". This removes all Bedrock inference profiles, the local config directory, and the shell RC block.

Q: What if a model isn't available in my Bedrock region? A: That profile is skipped with a warning and setup continues. Use --update once the model becomes available in your region.

Q: How do I verify tags are set correctly? A: Run:

aws --profile dev bedrock describe-inference-profile \
  --inference-profile-arn arn:aws:bedrock:us-east-1:ACCOUNT:application-inference-profile/claude-sonnet-john-smith

Q: How often should I review costs? A: Weekly in Cost Explorer to catch unexpected spikes; monthly for total spend per developer.

References

#!/bin/bash
# Ensure we’re running on Bash 4+ for associative array functionality (on macOS default bash is 3.2).
if [ -n "$BASH_VERSION" ] && [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
if command -v bash >/dev/null 2>&1 && bash -c '[ ${BASH_VERSINFO[0]} -ge 4 ]'; then
exec bash "$0" "$@"
fi
echo "[ERROR] Bash 4+ is required. Install with: brew install bash and rerun via /opt/homebrew/bin/bash $0"
exit 1
fi
################################################################################
# Claude Code Developer Setup Script
# Purpose: Onboard new developers with Claude Code, AWS Bedrock, and cost tracking
# Requirements: Ubuntu/Linux, AWS CLI installed, AWS profile "dev" configured
################################################################################
set -e
# Colors for output — use $'...' so variables hold real ANSI escape bytes
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'
NC=$'\033[0m'
# Configuration
AWS_REGION="${AWS_REGION:-us-east-1}"
BEDROCK_REGION="" # resolved dynamically
AWS_PROFILE="dev"
ACTION=""
DEVELOPER_NAME=""
while [ $# -gt 0 ]; do
case "$1" in
--create|--delete|--update) ACTION="$1" ; shift ;;
--developer) DEVELOPER_NAME="${2:-}" ; shift 2 ;;
*) echo "[ERROR] Unknown argument: $1" ; exit 1 ;;
esac
done
AWS_ACCOUNT_ID=""
INSTALL_DIR="${HOME}/.claude-code"
BEDROCK_CANDIDATE_REGIONS=(
"$AWS_REGION"
us-east-1
us-west-2
eu-west-1
eu-central-1
ap-northeast-1
ap-southeast-1
ap-south-1
)
CREATED_PROFILES=()
UPDATED_NEW_PROFILES=() # profiles created during --update
UPDATED_KEPT_PROFILES=() # profiles that already existed, kept during --update
# macOS bash (3.2) does not support associative arrays. Use explicit model vars.
PROFILE_ARN_OPUS46=""
PROFILE_ARN_OPUS47=""
PROFILE_ARN_SONNET=""
PROFILE_ARN_HAIKU=""
PROFILE_ARN_QWEN3_NEXT=""
PROFILE_ARN_QWEN3_CODER=""
################################################################################
# Helper Functions
################################################################################
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
sanitize_bedrock_description() {
local raw="$1"
local cleaned
cleaned=$(echo "$raw" | tr -cd '[:alnum:]:._ -' | sed -E 's/[ _-]+/ /g; s/^ +//; s/ +$//')
if [ -z "$cleaned" ]; then
cleaned="Claude Code profile"
fi
echo "$cleaned"
}
model_type_hint() {
local model_type="$1"
case "$model_type" in
opus46) echo "claude-opus-4-6" ;;
opus47) echo "claude-opus-4-7" ;;
sonnet) echo "claude-sonnet" ;;
haiku) echo "claude-haiku" ;;
*) return 1 ;;
esac
}
model_foundation_id() {
local model_type="$1"
case "$model_type" in
opus46) echo "anthropic.claude-opus-4-6" ;;
opus47) echo "anthropic.claude-opus-4-7" ;;
sonnet) echo "anthropic.claude-sonnet-4-6" ;;
haiku) echo "anthropic.claude-haiku-4-5" ;;
*) return 1 ;;
esac
}
################################################################################
# Bedrock Region Discovery
################################################################################
region_has_anthropic_models() {
local region="$1"
local count
count=$(aws --profile "$AWS_PROFILE" bedrock list-foundation-models \
--region "$region" \
--by-provider anthropic \
--query "length(modelSummaries)" \
--output text 2>/dev/null || echo "0")
if [ "$count" != "0" ] && [ "$count" != "None" ]; then
return 0
fi
# Fallback: accounts that expose Anthropic models only via cross-region
# inference profiles return 0 from list-foundation-models; check system-defined
# profiles instead.
local profile_count
profile_count=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$region" \
--type-equals SYSTEM_DEFINED \
--query "length(inferenceProfileSummaries[?contains(inferenceProfileId, 'anthropic') || contains(inferenceProfileId, 'claude')])" \
--output text 2>/dev/null || echo "0")
[ "$profile_count" != "0" ] && [ "$profile_count" != "None" ]
}
discover_bedrock_region() {
log_info "Discovering Bedrock region with Anthropic model support..."
for candidate in "${BEDROCK_CANDIDATE_REGIONS[@]}"; do
log_info " Checking region: $candidate"
if region_has_anthropic_models "$candidate"; then
BEDROCK_REGION="$candidate"
log_success "Found Anthropic models in region: $BEDROCK_REGION"
return 0
fi
done
log_error "No Bedrock region with Anthropic models found. Tried: ${BEDROCK_CANDIDATE_REGIONS[*]}"
log_error "Ensure your AWS account has Bedrock Anthropic model access enabled in at least one region."
exit 1
}
################################################################################
# Dynamic Model Resolution (all queries target BEDROCK_REGION)
################################################################################
resolve_model_source_arn() {
local model_type="$1"
local model_hint
model_hint=$(model_type_hint "$model_type") || return 1
local result
result=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$BEDROCK_REGION" \
--type-equals SYSTEM_DEFINED \
--query "reverse(sort_by(inferenceProfileSummaries[?contains(inferenceProfileId, '${model_hint}')], &inferenceProfileId))[0].inferenceProfileArn" \
--output text 2>/dev/null)
if [ -n "$result" ] && [ "$result" != "None" ]; then
log_info " Resolved $model_type -> $result (system-defined profile)" >&2
echo "$result"
return 0
fi
# Fallback: cross-region inference profile may not exist; try foundation model directly.
local foundation_id
foundation_id=$(model_foundation_id "$model_type") || return 1
result=$(resolve_foundation_model_arn "$foundation_id")
if [ -n "$result" ] && [ "$result" != "None" ]; then
log_info " Resolved $model_type -> $result (foundation model fallback)" >&2
echo "$result"
return 0
fi
return 1
}
resolve_foundation_model_arn() {
local model_id="$1"
local result
result=$(aws --profile "$AWS_PROFILE" bedrock list-foundation-models \
--region "$BEDROCK_REGION" \
--query "modelSummaries[?modelId=='${model_id}'].modelArn | [0]" \
--output text 2>/dev/null)
if [ -n "$result" ] && [ "$result" != "None" ]; then
log_info " Resolved $model_id -> $result" >&2
echo "$result"
return 0
fi
return 1
}
################################################################################
# Validation
################################################################################
validate_inputs() {
log_info "Validating setup requirements..."
if [ -z "$DEVELOPER_NAME" ]; then
log_error "Developer name is required"
echo "Usage: ./setup-claude-code-developer.sh --create --developer <name>"
echo " ./setup-claude-code-developer.sh --delete --developer <name>"
echo " ./setup-claude-code-developer.sh --update --developer <name>"
exit 1
fi
if ! command -v aws &> /dev/null; then
log_error "AWS CLI not found. Please install AWS CLI v2"
exit 1
fi
if ! command -v node &> /dev/null; then
log_warning "Node.js not found. Claude Code requires Node.js 18+. Installing..."
install_nodejs
fi
if [ "$AWS_PROFILE" != "dev" ]; then
log_error "AWS_PROFILE must be 'dev' for this setup script. Current: '$AWS_PROFILE'"
exit 1
fi
if ! aws --profile "dev" sts get-caller-identity &> /dev/null; then
log_error "AWS profile 'dev' not found or not configured correctly"
exit 1
fi
log_success "All validation checks passed"
}
install_nodejs() {
log_info "Installing Node.js 18+..."
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
log_success "Node.js installed"
}
################################################################################
# AWS Setup
################################################################################
setup_aws_credentials() {
log_info "Setting up AWS credentials for developer: $DEVELOPER_NAME"
AWS_ACCOUNT_ID=$(aws --profile "$AWS_PROFILE" sts get-caller-identity --query Account --output text)
log_success "AWS Account ID: $AWS_ACCOUNT_ID"
mkdir -p "$INSTALL_DIR"
log_success "AWS credentials configured"
}
write_aws_config() {
log_info "Writing AWS config files..."
cat > "$INSTALL_DIR/aws-config.sh" << EOF
#!/bin/bash
# Auto-generated AWS configuration for $DEVELOPER_NAME
export AWS_PROFILE="$AWS_PROFILE"
export AWS_REGION="$AWS_REGION"
export BEDROCK_REGION="$BEDROCK_REGION"
export AWS_ACCOUNT_ID="$AWS_ACCOUNT_ID"
EOF
chmod +x "$INSTALL_DIR/aws-config.sh"
log_success "AWS config written (BEDROCK_REGION=$BEDROCK_REGION)"
}
################################################################################
# Application Inference Profiles Setup
################################################################################
create_inference_profiles() {
log_info "Creating Application Inference Profiles for cost tracking..."
log_info "Bedrock region: $BEDROCK_REGION"
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
for model_type in sonnet haiku opus46 opus47; do
case "$model_type" in
opus46) profile_name="claude-opus-4-6-${PROFILE_PREFIX}" ;;
opus47) profile_name="claude-opus-4-7-${PROFILE_PREFIX}" ;;
*) profile_name="claude-${model_type}-${PROFILE_PREFIX}" ;;
esac
log_info "Creating inference profile: $profile_name"
model_source_arn=$(resolve_model_source_arn "$model_type" || true)
if [ -z "$model_source_arn" ]; then
log_warning "Skipping '$model_type': no compatible source found in $BEDROCK_REGION."
continue
fi
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles --region "$BEDROCK_REGION" \
--type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \
--output text 2>/dev/null)
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then
log_warning "Profile $profile_name already exists. Deleting..."
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-identifier "$existing_arn" > /dev/null
log_success "Deleted old profile: $profile_name"
fi
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $model_type model")
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-name "$profile_name" \
--description "$profile_description" \
--model-source "copyFrom=${model_source_arn}" \
--tags \
"key=developer,value=${PROFILE_PREFIX}" \
"key=model,value=${model_type}" \
"key=cost-tracking,value=enabled" \
"key=created-date,value=$(date +%Y-%m-%d)" \
--query "inferenceProfileArn" \
--output text)
log_success "Created inference profile: $profile_name ($created_arn)"
CREATED_PROFILES+=("$profile_name")
case "$model_type" in
opus46) PROFILE_ARN_OPUS46="$created_arn" ;;
opus47) PROFILE_ARN_OPUS47="$created_arn" ;;
sonnet) PROFILE_ARN_SONNET="$created_arn" ;;
haiku) PROFILE_ARN_HAIKU="$created_arn" ;;
esac
done
QWEN3_KEYS=("qwen3-next-80b-a3b" "qwen3-coder-next")
QWEN3_IDS=("qwen.qwen3-next-80b-a3b" "qwen.qwen3-coder-next")
for i in 0 1; do
qwen_key="${QWEN3_KEYS[$i]}"
qwen_model_id="${QWEN3_IDS[$i]}"
profile_name="${qwen_key}-${PROFILE_PREFIX}"
log_info "Creating inference profile: $profile_name"
model_source_arn=$(resolve_foundation_model_arn "$qwen_model_id" || true)
if [ -z "$model_source_arn" ]; then
log_warning "Skipping '$qwen_key': model '$qwen_model_id' not found in $BEDROCK_REGION."
continue
fi
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles --region "$BEDROCK_REGION" \
--type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \
--output text 2>/dev/null)
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then
log_warning "Profile $profile_name already exists. Deleting..."
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-identifier "$existing_arn" > /dev/null
log_success "Deleted old profile: $profile_name"
fi
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $qwen_key model")
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-name "$profile_name" \
--description "$profile_description" \
--model-source "copyFrom=${model_source_arn}" \
--tags \
"key=developer,value=${PROFILE_PREFIX}" \
"key=model,value=${qwen_key}" \
"key=cost-tracking,value=enabled" \
"key=created-date,value=$(date +%Y-%m-%d)" \
--query "inferenceProfileArn" \
--output text)
log_success "Created inference profile: $profile_name ($created_arn)"
CREATED_PROFILES+=("$profile_name")
case "$qwen_key" in
qwen3-next-80b-a3b) PROFILE_ARN_QWEN3_NEXT="$created_arn" ;;
qwen3-coder-next) PROFILE_ARN_QWEN3_CODER="$created_arn" ;;
esac
done
if [ ${#CREATED_PROFILES[@]} -eq 0 ]; then
log_error "No inference profiles were created. Cannot continue."
exit 1
fi
log_success "${#CREATED_PROFILES[@]} inference profile(s) created/verified: ${CREATED_PROFILES[*]}"
}
################################################################################
# Update Application Inference Profiles (additive-only, no delete)
################################################################################
update_inference_profiles() {
log_info "Syncing Application Inference Profiles (additive only)..."
log_info "Bedrock region: $BEDROCK_REGION"
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
for model_type in sonnet haiku opus46 opus47; do
case "$model_type" in
opus46) profile_name="claude-opus-4-6-${PROFILE_PREFIX}" ;;
opus47) profile_name="claude-opus-4-7-${PROFILE_PREFIX}" ;;
*) profile_name="claude-${model_type}-${PROFILE_PREFIX}" ;;
esac
log_info "Checking inference profile: $profile_name"
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$BEDROCK_REGION" --type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \
--output text 2>/dev/null)
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then
log_info " Profile already exists (keeping): $profile_name"
UPDATED_KEPT_PROFILES+=("$profile_name")
case "$model_type" in
opus46) PROFILE_ARN_OPUS46="$existing_arn" ;;
opus47) PROFILE_ARN_OPUS47="$existing_arn" ;;
sonnet) PROFILE_ARN_SONNET="$existing_arn" ;;
haiku) PROFILE_ARN_HAIKU="$existing_arn" ;;
esac
continue
fi
model_source_arn=$(resolve_model_source_arn "$model_type" || true)
if [ -z "$model_source_arn" ]; then
log_warning "Skipping '$model_type': no compatible source found in $BEDROCK_REGION."
continue
fi
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $model_type model")
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-name "$profile_name" \
--description "$profile_description" \
--model-source "copyFrom=${model_source_arn}" \
--tags \
"key=developer,value=${PROFILE_PREFIX}" \
"key=model,value=${model_type}" \
"key=cost-tracking,value=enabled" \
"key=created-date,value=$(date +%Y-%m-%d)" \
--query "inferenceProfileArn" --output text)
log_success "Created missing profile: $profile_name ($created_arn)"
UPDATED_NEW_PROFILES+=("$profile_name")
CREATED_PROFILES+=("$profile_name")
case "$model_type" in
opus46) PROFILE_ARN_OPUS46="$created_arn" ;;
opus47) PROFILE_ARN_OPUS47="$created_arn" ;;
sonnet) PROFILE_ARN_SONNET="$created_arn" ;;
haiku) PROFILE_ARN_HAIKU="$created_arn" ;;
esac
done
QWEN3_KEYS=("qwen3-next-80b-a3b" "qwen3-coder-next")
QWEN3_IDS=("qwen.qwen3-next-80b-a3b" "qwen.qwen3-coder-next")
for i in 0 1; do
qwen_key="${QWEN3_KEYS[$i]}"
profile_name="${qwen_key}-${PROFILE_PREFIX}"
log_info "Checking inference profile: $profile_name"
existing_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$BEDROCK_REGION" --type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name'].inferenceProfileArn | [0]" \
--output text 2>/dev/null)
if [ -n "$existing_arn" ] && [ "$existing_arn" != "None" ]; then
log_info " Profile already exists (keeping): $profile_name"
UPDATED_KEPT_PROFILES+=("$profile_name")
case "$qwen_key" in
qwen3-next-80b-a3b) PROFILE_ARN_QWEN3_NEXT="$existing_arn" ;;
qwen3-coder-next) PROFILE_ARN_QWEN3_CODER="$existing_arn" ;;
esac
continue
fi
model_source_arn=$(resolve_foundation_model_arn "${QWEN3_IDS[$i]}" || true)
if [ -z "$model_source_arn" ]; then
log_warning "Skipping '$qwen_key': model '${QWEN3_IDS[$i]}' not found in $BEDROCK_REGION."
continue
fi
profile_description=$(sanitize_bedrock_description "Claude Code for $DEVELOPER_NAME $qwen_key model")
created_arn=$(aws --profile "$AWS_PROFILE" bedrock create-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-name "$profile_name" \
--description "$profile_description" \
--model-source "copyFrom=${model_source_arn}" \
--tags \
"key=developer,value=${PROFILE_PREFIX}" \
"key=model,value=${qwen_key}" \
"key=cost-tracking,value=enabled" \
"key=created-date,value=$(date +%Y-%m-%d)" \
--query "inferenceProfileArn" --output text)
log_success "Created missing profile: $profile_name ($created_arn)"
UPDATED_NEW_PROFILES+=("$profile_name")
CREATED_PROFILES+=("$profile_name")
case "$qwen_key" in
qwen3-next-80b-a3b) PROFILE_ARN_QWEN3_NEXT="$created_arn" ;;
qwen3-coder-next) PROFILE_ARN_QWEN3_CODER="$created_arn" ;;
esac
done
log_success "Sync complete: ${#UPDATED_NEW_PROFILES[@]} created, ${#UPDATED_KEPT_PROFILES[@]} kept"
}
################################################################################
# Update Summary
################################################################################
print_update_summary() {
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
echo ""
echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}Update Complete!${NC}"
echo -e "${GREEN}================================${NC}"
echo ""
echo "Developer: $DEVELOPER_NAME"
echo "AWS Region: $AWS_REGION"
echo "Bedrock Region: $BEDROCK_REGION"
echo "AWS Account: $AWS_ACCOUNT_ID"
echo "Profile Prefix: $PROFILE_PREFIX"
echo ""
if [ ${#UPDATED_NEW_PROFILES[@]} -gt 0 ]; then
echo -e "${BLUE}Newly Created Profiles (${#UPDATED_NEW_PROFILES[@]}):${NC}"
for p in "${UPDATED_NEW_PROFILES[@]}"; do
echo -e " ${GREEN}+${NC} $p"
done
echo ""
else
echo -e "${BLUE}Newly Created Profiles:${NC} none (all profiles already existed)"
echo ""
fi
if [ ${#UPDATED_KEPT_PROFILES[@]} -gt 0 ]; then
echo -e "${BLUE}Existing Profiles Kept (${#UPDATED_KEPT_PROFILES[@]}):${NC}"
for p in "${UPDATED_KEPT_PROFILES[@]}"; do
echo " $p"
done
echo ""
fi
echo -e "${BLUE}Regenerated Config Files:${NC}"
echo " - $INSTALL_DIR/claude-code-config.json"
echo " - $INSTALL_DIR/activate.sh"
echo ""
echo -e "${BLUE}Next Steps:${NC}"
echo " 1. Reload your shell to pick up updated ARNs:"
echo " source ~/.zshrc (or ~/.bashrc for bash)"
echo ""
echo " 2. Verify the setup:"
echo " env | grep ANTHROPIC"
echo ""
echo " 3. Monitor costs in AWS Console:"
echo " Cost Explorer -> Filter by tags -> developer = ${PROFILE_PREFIX}"
echo ""
echo -e "${GREEN}Happy coding!${NC}"
echo ""
}
################################################################################
# Delete Application Inference Profiles
################################################################################
delete_inference_profiles() {
log_info "Deleting Application Inference Profiles for developer: $DEVELOPER_NAME"
local profile_prefix
profile_prefix=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
for profile_name in \
"claude-opus-4-6-${profile_prefix}" \
"claude-opus-4-7-${profile_prefix}" \
"claude-sonnet-${profile_prefix}" \
"claude-haiku-${profile_prefix}"; do
log_info " Looking for profile: $profile_name"
local profile_arn
profile_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$BEDROCK_REGION" \
--type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='${profile_name}'].inferenceProfileArn | [0]" \
--output text 2>/dev/null || true)
if [ -z "$profile_arn" ] || [ "$profile_arn" = "None" ]; then
log_warning " Profile not found (skipping): $profile_name"
continue
fi
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-identifier "$profile_arn" > /dev/null
log_success " Deleted profile: $profile_name ($profile_arn)"
done
for qwen_key in "qwen3-next-80b-a3b" "qwen3-coder-next"; do
local profile_name="${qwen_key}-${profile_prefix}"
log_info " Looking for profile: $profile_name"
local profile_arn
profile_arn=$(aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$BEDROCK_REGION" \
--type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='${profile_name}'].inferenceProfileArn | [0]" \
--output text 2>/dev/null || true)
if [ -z "$profile_arn" ] || [ "$profile_arn" = "None" ]; then
log_warning " Profile not found (skipping): $profile_name"
continue
fi
aws --profile "$AWS_PROFILE" bedrock delete-inference-profile \
--region "$BEDROCK_REGION" \
--inference-profile-identifier "$profile_arn" > /dev/null
log_success " Deleted profile: $profile_name ($profile_arn)"
done
}
################################################################################
# Claude Code Configuration
################################################################################
setup_claude_code_config() {
log_info "Setting up Claude Code configuration..."
local opus46_arn="$PROFILE_ARN_OPUS46"
local opus47_arn="$PROFILE_ARN_OPUS47"
local sonnet_arn="$PROFILE_ARN_SONNET"
local haiku_arn="$PROFILE_ARN_HAIKU"
local qwen3_next_arn="$PROFILE_ARN_QWEN3_NEXT"
local qwen3_coder_arn="$PROFILE_ARN_QWEN3_CODER"
local qwen3_overrides=""
if [ -n "$qwen3_next_arn" ]; then
qwen3_overrides="${qwen3_overrides},
\"qwen3-next-80b-a3b\": \"$qwen3_next_arn\""
fi
if [ -n "$qwen3_coder_arn" ]; then
qwen3_overrides="${qwen3_overrides},
\"qwen3-coder-next\": \"$qwen3_coder_arn\""
fi
cat > "$INSTALL_DIR/claude-code-config.json" << EOF
{
"env": {
"CLAUDE_CODE_USE_BEDROCK": "1",
"AWS_REGION": "$BEDROCK_REGION",
"AWS_PROFILE": "$AWS_PROFILE",
"AWS_ACCOUNT_ID": "$AWS_ACCOUNT_ID",
"DEVELOPER_NAME": "$DEVELOPER_NAME"
},
"modelOverrides": {
"claude-opus-4-6": "$opus46_arn",
"claude-opus-4-7": "$opus47_arn",
"claude-sonnet-4-6": "$sonnet_arn",
"claude-haiku-4-5": "$haiku_arn"${qwen3_overrides}
},
"defaultModel": "claude-sonnet-4-6",
"defaultFastModel": "claude-haiku-4-5",
"defaultHeavyModel": "claude-opus-4-7"
}
EOF
log_success "Claude Code configuration created"
log_info "Updating ~/.claude/settings.json with customApiKeyModels..."
local settings_file="$HOME/.claude/settings.json"
mkdir -p "$HOME/.claude"
# Read existing settings or start fresh, then inject customApiKeyModels and model
if [ -f "$settings_file" ]; then
local tmp
tmp=$(mktemp)
# Use python3 to safely merge JSON (available on all target platforms)
python3 - "$settings_file" "$sonnet_arn" "$opus46_arn" "$opus47_arn" "$haiku_arn" > "$tmp" << 'PYEOF'
import json, sys
with open(sys.argv[1]) as f:
cfg = json.load(f)
cfg["model"] = sys.argv[2]
cfg["customApiKeyModels"] = [sys.argv[3], sys.argv[4], sys.argv[2], sys.argv[5]]
print(json.dumps(cfg, indent=2))
PYEOF
mv "$tmp" "$settings_file"
else
cat > "$settings_file" << EOF
{
"model": "$sonnet_arn",
"customApiKeyModels": [
"$opus46_arn",
"$opus47_arn",
"$sonnet_arn",
"$haiku_arn"
]
}
EOF
fi
log_success "$HOME/.claude/settings.json updated with all model ARNs"
}
################################################################################
# Environment Setup
################################################################################
setup_environment() {
log_info "Setting up shell environment..."
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
local opus46_arn="$PROFILE_ARN_OPUS46"
local opus47_arn="$PROFILE_ARN_OPUS47"
local sonnet_arn="$PROFILE_ARN_SONNET"
local haiku_arn="$PROFILE_ARN_HAIKU"
local qwen3_next_arn="$PROFILE_ARN_QWEN3_NEXT"
local qwen3_coder_arn="$PROFILE_ARN_QWEN3_CODER"
local qwen3_exports=""
if [ -n "$qwen3_next_arn" ]; then
qwen3_exports="${qwen3_exports}
export ANTHROPIC_DEFAULT_QWEN3_NEXT_MODEL=\"${qwen3_next_arn}\""
fi
if [ -n "$qwen3_coder_arn" ]; then
qwen3_exports="${qwen3_exports}
export ANTHROPIC_DEFAULT_QWEN3_CODER_MODEL=\"${qwen3_coder_arn}\""
fi
cat > "$INSTALL_DIR/activate.sh" << EOF
#!/bin/bash
CLAUDE_CODE_DIR="\${HOME}/.claude-code"
source "\$CLAUDE_CODE_DIR/aws-config.sh"
export ANTHROPIC_DEFAULT_OPUS_46_MODEL="${opus46_arn}"
export ANTHROPIC_DEFAULT_OPUS_47_MODEL="${opus47_arn}"
export ANTHROPIC_DEFAULT_SONNET_MODEL="${sonnet_arn}"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="${haiku_arn}"${qwen3_exports}
export ANTHROPIC_MODEL="\$ANTHROPIC_DEFAULT_SONNET_MODEL"
EOF
chmod +x "$INSTALL_DIR/activate.sh"
SHELL_RC="$HOME/.bashrc"
if [ -f "$HOME/.zshrc" ]; then
SHELL_RC="$HOME/.zshrc"
fi
if ! grep -q "claude-code" "$SHELL_RC"; then
cat >> "$SHELL_RC" << 'EOF'
# Claude Code environment
export CLAUDE_CODE_USE_BEDROCK=1
export CLAUDE_CODE_DIR="${HOME}/.claude-code"
if [ -f "$CLAUDE_CODE_DIR/activate.sh" ]; then
source "$CLAUDE_CODE_DIR/activate.sh"
fi
EOF
log_success "Added Claude Code to $SHELL_RC"
fi
if ! grep -q "CLAUDE_CODE_USE_BEDROCK" "$SHELL_RC"; then
sed -i '/# Claude Code environment/a export CLAUDE_CODE_USE_BEDROCK=1' "$SHELL_RC"
log_success "Added CLAUDE_CODE_USE_BEDROCK=1 to $SHELL_RC"
fi
log_success "Shell environment configured"
}
################################################################################
# Install Claude Code CLI
################################################################################
install_claude_code_cli() {
log_info "Installing Claude Code CLI..."
if npm list -g @anthropic-ai/claude-code &> /dev/null; then
log_warning "Claude Code CLI already installed"
else
npm install -g @anthropic-ai/claude-code
log_success "Claude Code CLI installed"
fi
}
################################################################################
# Cost Tracking Setup
################################################################################
setup_cost_tracking() {
log_info "Setting up cost tracking configuration..."
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
cat > "$INSTALL_DIR/cost-tracking-queries.txt" << EOF
# CloudWatch Insights Queries for Cost Tracking
## Query 1: Total costs by model for this developer
fields @timestamp, model, inputTokens, outputTokens
| filter tags.developer = "$PROFILE_PREFIX"
| stats sum(inputTokens) as total_input, sum(outputTokens) as total_output by model
## Query 2: Daily costs breakdown
fields @timestamp, model, inputTokens, outputTokens
| filter tags.developer = "$PROFILE_PREFIX"
| stats sum(inputTokens) as input, sum(outputTokens) as output by datefloor(@timestamp, 1d), model
## Query 3: Real-time usage (last hour)
fields @timestamp, model, inputTokens, outputTokens
| filter tags.developer = "$PROFILE_PREFIX" and @timestamp > ago(1h)
| stats count() as requests, sum(inputTokens) as input, sum(outputTokens) as output by model
EOF
cat > "$INSTALL_DIR/COST_TRACKING_GUIDE.md" << EOF
# Cost Tracking Guide for $DEVELOPER_NAME
## Overview
Your Claude Code usage is tracked using AWS Application Inference Profiles with cost allocation tags.
## Viewing Your Costs
### Method 1: AWS Cost Explorer (Recommended)
1. Log in to AWS Console
2. Navigate to **Cost Explorer**
3. Select **Filters** -> **Tags** -> **developer** = **${PROFILE_PREFIX}**
4. Group by **Tags** -> **model** to see costs per model
5. Set date range to see trends
### Method 2: CloudWatch Logs (Real-time)
1. Go to **CloudWatch** -> **Logs**
2. Search for logs from Bedrock model invocations
3. Use Insights queries from \`cost-tracking-queries.txt\`
### Method 3: AWS Billing Dashboard
1. Navigate to **Billing** -> **Bills**
2. Filter by service: **Bedrock**
3. Filter by tags matching your profile
## Cost Breakdown by Model
- **Opus 4.7**: Latest, highest capability - use for the most complex tasks
- **Opus 4.6**: Strong reasoning and heavy lifting
- **Sonnet (4-6)**: Balanced - recommended for most tasks
- **Haiku (4-5)**: Fast, cheap - use for simple queries
- **Qwen3 Next 80B A3B**: Large open-weight reasoning and coding alternative
- **Qwen3 Coder Next**: Coding-specialized Qwen3 model
## Optimizing Your Costs
1. Use Haiku for simple tasks (significantly cheaper)
2. Start with Sonnet, upgrade to Opus only if needed
3. Monitor your usage with Cost Explorer weekly
4. Review inference profile tags: developer=$PROFILE_PREFIX, model=
## Troubleshooting
### Costs not appearing?
- Wait 24 hours for AWS Cost Explorer to update
- Check that tags are applied correctly:
\`\`\`
aws bedrock describe-inference-profile \\
--inference-profile-arn arn:aws:bedrock:${BEDROCK_REGION}:${AWS_ACCOUNT_ID}:application-inference-profile/claude-sonnet-${PROFILE_PREFIX}
\`\`\`
### Using wrong model?
- Check environment variables: \`env | grep ANTHROPIC\`
- Use \`/model\` command in Claude Code to switch models
- Default model: Sonnet (set in configuration)
EOF
log_success "Cost tracking configuration created"
}
################################################################################
# Remove Shell RC Block
################################################################################
remove_shell_rc_block() {
log_info "Removing Claude Code block from shell RC file(s)..."
local removed_any=false
for rc_file in "$HOME/.bashrc" "$HOME/.zshrc"; do
if [ ! -f "$rc_file" ]; then
continue
fi
if ! grep -q "# Claude Code environment" "$rc_file"; then
continue
fi
# Use Python3 for portable multi-line block removal.
# BSD sed (macOS) and GNU sed (Linux) differ enough that a single sed
# one-liner is not reliable across both. Python3 is available on both.
# Removes: blank line + "# Claude Code environment" block through "fi".
python3 << PYEOF
fname = "$rc_file"
with open(fname) as f:
lines = f.readlines()
result = []
i = 0
while i < len(lines):
if (lines[i].strip() == '' and
i + 1 < len(lines) and
lines[i + 1].rstrip('\n') == '# Claude Code environment'):
# Skip past the comment then consume lines until the closing 'fi'
i += 2
while i < len(lines) and lines[i].rstrip('\n') != 'fi':
i += 1
i += 1 # skip the 'fi' line itself
else:
result.append(lines[i])
i += 1
with open(fname, 'w') as f:
f.writelines(result)
PYEOF
log_success " Removed Claude Code block from $rc_file"
removed_any=true
done
if [ "$removed_any" = false ]; then
log_warning "No Claude Code block found in ~/.bashrc or ~/.zshrc — nothing to remove"
fi
}
################################################################################
# Delete Setup
################################################################################
delete_setup() {
log_info "Starting Claude Code developer teardown..."
log_info "Developer: $DEVELOPER_NAME"
discover_bedrock_region
delete_inference_profiles
if [ -d "$INSTALL_DIR" ]; then
rm -rf "$INSTALL_DIR"
log_success "Removed directory: $INSTALL_DIR"
else
log_warning "Directory not found (skipping): $INSTALL_DIR"
fi
remove_shell_rc_block
log_success "Teardown complete for developer: $DEVELOPER_NAME"
}
################################################################################
# Verification
################################################################################
verify_setup() {
log_info "Verifying setup..."
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
if aws --profile "$AWS_PROFILE" sts get-caller-identity &> /dev/null; then
log_success "AWS credentials verified"
else
log_error "AWS credentials verification failed"
return 1
fi
log_info "Checking inference profiles..."
for profile_name in "${CREATED_PROFILES[@]}"; do
if aws --profile "$AWS_PROFILE" bedrock list-inference-profiles \
--region "$BEDROCK_REGION" --type-equals APPLICATION \
--query "inferenceProfileSummaries[?inferenceProfileName=='$profile_name']" \
--output text | grep -q "$profile_name"; then
log_success "Inference profile found: $profile_name"
else
log_error "Inference profile not found: $profile_name"
return 1
fi
done
if npm list -g @anthropic-ai/claude-code &> /dev/null; then
log_success "Claude Code CLI verified"
else
log_error "Claude Code CLI not found"
return 1
fi
if [ -f "$INSTALL_DIR/claude-code-config.json" ]; then
log_success "Configuration files created"
else
log_error "Configuration files missing"
return 1
fi
return 0
}
################################################################################
# Summary
################################################################################
print_summary() {
PROFILE_PREFIX=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
echo ""
echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}Setup Complete!${NC}"
echo -e "${GREEN}================================${NC}"
echo ""
echo "Developer: $DEVELOPER_NAME"
echo "AWS Region: $AWS_REGION"
echo "Bedrock Region: $BEDROCK_REGION"
echo "AWS Account: $AWS_ACCOUNT_ID"
echo "Profile Prefix: $PROFILE_PREFIX"
echo ""
echo -e "${BLUE}Configuration Files:${NC}"
echo " - $INSTALL_DIR/aws-config.sh"
echo " - $INSTALL_DIR/claude-code-config.json"
echo " - $INSTALL_DIR/activate.sh"
echo " - $INSTALL_DIR/COST_TRACKING_GUIDE.md"
echo ""
echo -e "${BLUE}Inference Profiles (${#CREATED_PROFILES[@]}):${NC}"
for p in "${CREATED_PROFILES[@]}"; do
echo " - $p"
done
echo ""
echo -e "${BLUE}Next Steps:${NC}"
echo " 1. Reload your shell:"
echo " source ~/.zshrc (or ~/.bashrc for bash)"
echo ""
echo " 2. Start using Claude Code:"
echo " claude"
echo ""
echo " 3. Switch models in Claude Code with:"
echo " /model"
echo ""
echo " 4. Monitor costs in AWS Console:"
echo " Cost Explorer -> Filter by tags -> developer = ${PROFILE_PREFIX}"
echo ""
echo " 5. View cost tracking guide:"
echo " cat $INSTALL_DIR/COST_TRACKING_GUIDE.md"
echo ""
echo -e "${BLUE}Useful Commands:${NC}"
echo " # View your inference profiles"
echo " aws --profile dev bedrock list-inference-profiles --region $BEDROCK_REGION"
echo ""
echo " # Check costs via CloudWatch"
echo " cat $INSTALL_DIR/cost-tracking-queries.txt"
echo ""
echo -e "${GREEN}Happy coding!${NC}"
echo ""
}
################################################################################
# Delete Summary
################################################################################
print_delete_summary() {
local profile_prefix
profile_prefix=$(echo "$DEVELOPER_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
echo ""
echo -e "${GREEN}================================${NC}"
echo -e "${GREEN}Teardown Complete!${NC}"
echo -e "${GREEN}================================${NC}"
echo ""
echo "Developer: $DEVELOPER_NAME"
echo "Bedrock Region: $BEDROCK_REGION"
echo ""
echo -e "${BLUE}What was removed:${NC}"
echo " - Application Inference Profiles (if they existed):"
echo " claude-opus-${profile_prefix}"
echo " claude-opus-47-${profile_prefix}"
echo " claude-sonnet-${profile_prefix}"
echo " claude-haiku-${profile_prefix}"
echo " qwen3-next-80b-a3b-${profile_prefix}"
echo " qwen3-coder-next-${profile_prefix}"
echo " - Local directory: $INSTALL_DIR"
echo " - Claude Code block from ~/.bashrc and/or ~/.zshrc"
echo ""
echo -e "${BLUE}What was NOT removed:${NC}"
echo " - Claude Code CLI (npm package @anthropic-ai/claude-code)"
echo " To uninstall manually: npm uninstall -g @anthropic-ai/claude-code"
echo ""
echo -e "${YELLOW}Note:${NC} Reload your shell to clear leftover environment variables:"
echo " source ~/.bashrc (or ~/.zshrc for zsh)"
echo ""
}
################################################################################
# Update Orchestration
################################################################################
run_update() {
log_info "Starting Claude Code developer update (additive sync)..."
log_info "Developer: $DEVELOPER_NAME"
log_info "AWS Region: $AWS_REGION"
validate_inputs
setup_aws_credentials
discover_bedrock_region
if [ "$BEDROCK_REGION" != "$AWS_REGION" ]; then
log_warning "Anthropic models not available in $AWS_REGION. Using Bedrock region: $BEDROCK_REGION"
fi
update_inference_profiles
setup_claude_code_config
setup_environment
if verify_setup; then
log_success "All update steps completed successfully"
print_update_summary
else
log_error "Verification failed. Please check the errors above."
exit 1
fi
}
################################################################################
# Main Execution
################################################################################
main() {
if [ "$ACTION" != "--create" ] && [ "$ACTION" != "--delete" ] && [ "$ACTION" != "--update" ]; then
log_error "A valid action flag is required."
echo "Usage: ./setup-claude-code-developer.sh --create --developer <name>"
echo " ./setup-claude-code-developer.sh --delete --developer <name>"
echo " ./setup-claude-code-developer.sh --update --developer <name>"
exit 1
fi
if [ "$ACTION" = "--create" ]; then
log_info "Starting Claude Code developer setup..."
log_info "Developer: $DEVELOPER_NAME"
log_info "AWS Region: $AWS_REGION"
validate_inputs
setup_aws_credentials
discover_bedrock_region
if [ "$BEDROCK_REGION" != "$AWS_REGION" ]; then
log_warning "Anthropic models not available in $AWS_REGION. Using Bedrock region: $BEDROCK_REGION"
fi
write_aws_config
create_inference_profiles
setup_claude_code_config
setup_environment
install_claude_code_cli
setup_cost_tracking
if verify_setup; then
log_success "All setup steps completed successfully"
print_summary
else
log_error "Verification failed. Please check the errors above."
exit 1
fi
elif [ "$ACTION" = "--delete" ]; then
validate_inputs
delete_setup
print_delete_summary
elif [ "$ACTION" = "--update" ]; then
run_update
fi
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment