Skip to content

Instantly share code, notes, and snippets.

@salekh
Last active June 11, 2026 08:45
Show Gist options
  • Select an option

  • Save salekh/58502463356836d87429ee5354d0f6d3 to your computer and use it in GitHub Desktop.

Select an option

Save salekh/58502463356836d87429ee5354d0f6d3 to your computer and use it in GitHub Desktop.
Set up Claude Code on GCP
#!/usr/bin/env python3
import os
import sys
import json
import subprocess
import shutil
from pathlib import Path
# ANSI color codes for rich CLI terminal aesthetics
COLOR_HEADER = "\033[95m"
COLOR_BLUE = "\033[94m"
COLOR_CYAN = "\033[96m"
COLOR_GREEN = "\033[92m"
COLOR_YELLOW = "\033[93m"
COLOR_RED = "\033[91m"
COLOR_BOLD = "\033[1m"
COLOR_UNDERLINE = "\033[4m"
COLOR_RESET = "\033[0m"
def print_header(text):
print(f"\n{COLOR_BOLD}{COLOR_HEADER}{'='*60}")
print(f" {text.center(58)}")
print(f"{'='*60}{COLOR_RESET}\n")
def print_step(num, text):
print(f"\n{COLOR_BOLD}{COLOR_CYAN}[Step {num}] {text}{COLOR_RESET}")
def print_success(text):
print(f"{COLOR_GREEN}{text}{COLOR_RESET}")
def print_info(text):
print(f"{COLOR_BLUE}{text}{COLOR_RESET}")
def print_warning(text):
print(f"{COLOR_YELLOW}{text}{COLOR_RESET}")
def print_error(text):
print(f"{COLOR_RED}{text}{COLOR_RESET}")
def prompt_input(prompt_text, default=None, validator=None):
prompt_suffix = f" [{COLOR_YELLOW}{default}{COLOR_RESET}]" if default is not None else ""
while True:
try:
val = input(f"{COLOR_BOLD}{prompt_text}{prompt_suffix}: {COLOR_RESET}").strip()
if not val and default is not None:
return default
if not val:
print_warning("Input cannot be empty. Please enter a value.")
continue
if validator and not validator(val):
continue
return val
except KeyboardInterrupt:
print("\n")
print_error("Configuration cancelled by user.")
sys.exit(1)
def prompt_yes_no(prompt_text, default=True):
default_str = "Y/n" if default else "y/N"
while True:
try:
val = input(f"{COLOR_BOLD}{prompt_text} ({default_str}): {COLOR_RESET}").strip().lower()
if not val:
return default
if val in ['y', 'yes']:
return True
if val in ['n', 'no']:
return False
print_warning("Invalid input. Please enter 'y' or 'n'.")
except KeyboardInterrupt:
print("\n")
print_error("Configuration cancelled by user.")
sys.exit(1)
def prompt_choice(prompt_text, choices, default=None):
"""
choices: list of tuples (key/index, description, value)
"""
print(f"\n{COLOR_BOLD}{prompt_text}:{COLOR_RESET}")
for idx, (key, desc, _) in enumerate(choices, 1):
print(f" {COLOR_CYAN}{idx}){COLOR_RESET} {desc}")
default_idx = None
if default is not None:
for idx, (_, _, val) in enumerate(choices, 1):
if val == default:
default_idx = idx
break
while True:
choice_input = prompt_input("Select an option", default=str(default_idx) if default_idx else None)
try:
idx = int(choice_input)
if 1 <= idx <= len(choices):
return choices[idx - 1][2]
print_warning(f"Please enter a number between 1 and {len(choices)}.")
except ValueError:
print_warning("Please enter a valid number.")
def check_gcloud():
gcloud_path = shutil.which("gcloud")
if gcloud_path:
print_success(f"Google Cloud SDK is installed at: {gcloud_path}")
return True
else:
print_warning("Google Cloud SDK (gcloud) is not found in your PATH.")
print_info("You can still manually configure settings, but automatic login and validation will be skipped.")
return False
def get_gcloud_default_project():
try:
result = subprocess.run(
["gcloud", "config", "get-value", "project"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
project = result.stdout.strip()
if project and "(unset)" not in project:
return project
except Exception:
pass
return None
def run_command(cmd_args, capture_output=False, show_output=True):
try:
if capture_output:
result = subprocess.run(
cmd_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
return result.returncode == 0, result.stdout.strip(), result.stderr.strip()
else:
# Let it run interactively
stdout_dest = None if show_output else subprocess.DEVNULL
stderr_dest = None if show_output else subprocess.DEVNULL
result = subprocess.run(cmd_args, stdout=stdout_dest, stderr=stderr_dest)
return result.returncode == 0, "", ""
except Exception as e:
return False, "", str(e)
def configure_data_sharing(project_id, region, model_id):
# Strip any version suffix or @default to get the base model name
# e.g., "claude-fable-5@default" -> "claude-fable-5"
base_model_name = model_id.split('@')[0]
# Determine the location and endpoint
location = region
if region == "global":
endpoint = "aiplatform.googleapis.com"
else:
endpoint = f"{region}-aiplatform.googleapis.com"
print_info(f"Enabling Anthropic data sharing for model '{base_model_name}' on project '{project_id}'...")
# 1. Get access token from gcloud
success, token, err = run_command(["gcloud", "auth", "print-access-token"], capture_output=True, show_output=False)
if not success or not token:
print_error(f"Failed to retrieve gcloud access token: {err or 'Token empty'}")
print_warning("Please make sure you are logged in via 'gcloud auth login' and try again.")
return False
url = f"https://{endpoint}/v1beta1/projects/{project_id}/locations/{location}/publishers/anthropic/models/{base_model_name}:setPublisherModelConfig"
# 2. Run curl to call the API
curl_cmd = [
"curl", "-sS", "-X", "POST",
"-H", f"Authorization: Bearer {token}",
"-H", "Content-Type: application/json",
"-d", json.dumps({"publisherModelConfig": {"dataSharingEnabledProvider": "anthropic"}}),
url
]
success, stdout, stderr = run_command(curl_cmd, capture_output=True, show_output=False)
if success:
# Check if response has error
try:
resp = json.loads(stdout)
if "error" in resp:
print_error(f"API Error configuring data sharing: {resp['error'].get('message')}")
return False
print_success(f"Successfully enabled data sharing for '{base_model_name}'!")
return True
except Exception:
# If it's not JSON but success
print_success(f"Data sharing configured (Response: {stdout})")
return True
else:
print_error(f"Failed to connect to Vertex AI API: {stderr or stdout}")
return False
def main():
print_header("Claude Code - Google Vertex AI Interactive Setup")
print_info("This script will guide you through setting up Claude Code to run on Google Vertex AI.")
print_info("Reference: https://code.claude.com/docs/en/google-vertex-ai")
# 1. System check
print_step(1, "Checking environment prerequisites...")
gcloud_available = check_gcloud()
# 2. Get Project ID
print_step(2, "Configuring Google Cloud Project ID")
default_project = None
if gcloud_available:
default_project = get_gcloud_default_project()
if default_project:
print_info(f"Detected active gcloud project: {COLOR_BOLD}{default_project}{COLOR_RESET}")
project_id = prompt_input("Enter your GCP Project ID", default=default_project)
# 3. Configure Region
print_step(3, "Configuring Vertex AI Region")
print_info("Claude Code works best using the 'global' multi-region endpoint for model routing,")
print_info("but you can also use regional endpoints if needed.")
region_choices = [
("global", "global (Recommended - automatically routes to nearest available endpoint)", "global"),
("us", "us (United States multi-region)", "us"),
("eu", "eu (Europe multi-region)", "eu"),
("us-east5", "us-east5 (Specific Region)", "us-east5"),
("europe-west1", "europe-west1 (Specific Region)", "europe-west1"),
("custom", "Other (enter custom region)", "custom")
]
region = prompt_choice("Select Vertex AI Region", region_choices, default="global")
if region == "custom":
region = prompt_input("Enter your custom GCP Region (e.g. us-central1)")
# 4. Authentication Method
print_step(4, "Configuring GCP Authentication")
auth_choices = [
("adc", "Application Default Credentials (Recommended for personal/local setups)", "adc"),
("sa", "Service Account Key File (Recommended for CI/CD or service identities)", "sa"),
("env", "Environment Variables (Already configured or handled externally)", "env")
]
auth_method = prompt_choice("Select Authentication Method", auth_choices, default="adc")
sa_key_path = None
if auth_method == "sa":
while True:
sa_key_path = prompt_input("Enter the absolute path to your Service Account JSON key file")
expanded_path = os.path.abspath(os.path.expanduser(sa_key_path))
if os.path.exists(expanded_path):
sa_key_path = expanded_path
print_success(f"Found key file at: {sa_key_path}")
break
else:
print_error(f"File not found at: {sa_key_path}. Please check the path and try again.")
# 5. Model Pinning (Optional)
print_step(5, "Configuring Claude Models (Pinning)")
print_info("By default, Claude Code uses built-in default aliases:")
print_info(" - Primary model: claude-sonnet-4-5@20250929")
print_info(" - Small/fast model: Same as primary model (or Haiku-class if configured)")
print_info("You can pin custom model versions if desired.")
customize_models = prompt_yes_no("Do you want to customize model pins (override defaults)?", default=False)
model_pins = {}
fable_sharing_models = []
if customize_models:
while True:
print_info("Leave blank to use the system default for that category.")
sonnet_model = prompt_input("Primary/Sonnet Model ID (e.g., claude-sonnet-4-6)", default="")
haiku_model = prompt_input("Fast/Haiku Model ID (e.g., claude-haiku-4-5@20251001)", default="")
opus_model = prompt_input("Opus Model ID (e.g., claude-opus-4-8)", default="")
fable_models = [m for m in [sonnet_model, haiku_model, opus_model] if m.startswith("claude-fable-")]
if fable_models:
print_warning(f"Data Sharing Required: The model(s) {', '.join(fable_models)} require data sharing to be enabled for publisher 'anthropic'.")
print_warning("This shares prompt inputs and response outputs with Anthropic for misuse detection.")
grant = prompt_yes_no("Do you explicitly grant permission to enable data sharing with Anthropic?", default=False)
if not grant:
print_error("Permission denied. Claude Fable models cannot be used without enabling data sharing.")
print_info("Please select different models.")
continue
else:
fable_sharing_models = fable_models
if sonnet_model:
model_pins["ANTHROPIC_DEFAULT_SONNET_MODEL"] = sonnet_model
if haiku_model:
model_pins["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = haiku_model
if opus_model:
model_pins["ANTHROPIC_DEFAULT_OPUS_MODEL"] = opus_model
break
# 6. Apply configuration to ~/.claude/settings.json
print_step(6, "Applying configuration to Claude Code settings")
settings_dir = Path.home() / ".claude"
settings_file = settings_dir / "settings.json"
settings = {}
if settings_file.exists():
try:
with open(settings_file, "r") as f:
settings = json.load(f)
print_info("Found existing user settings file. Merging configuration...")
except Exception as e:
print_warning(f"Could not parse existing settings.json ({e}). Creating a new one.")
else:
# Ensure directory exists
settings_dir.mkdir(parents=True, exist_ok=True)
print_info(f"Creating settings directory at {settings_dir}")
# Update settings dictionary
if "env" not in settings:
settings["env"] = {}
settings["env"]["CLAUDE_CODE_USE_VERTEX"] = "1"
settings["env"]["CLOUD_ML_REGION"] = region
settings["env"]["ANTHROPIC_VERTEX_PROJECT_ID"] = project_id
# Configure Authentication
if auth_method == "adc":
settings["gcpAuthRefresh"] = "gcloud auth application-default login"
# Remove GOOGLE_APPLICATION_CREDENTIALS if it was previously set
settings["env"].pop("GOOGLE_APPLICATION_CREDENTIALS", None)
elif auth_method == "sa" and sa_key_path:
settings["env"]["GOOGLE_APPLICATION_CREDENTIALS"] = sa_key_path
settings.pop("gcpAuthRefresh", None)
else: # env / external
settings.pop("gcpAuthRefresh", None)
settings["env"].pop("GOOGLE_APPLICATION_CREDENTIALS", None)
# Apply model pins
if customize_models:
for var, val in model_pins.items():
settings["env"][var] = val
# Write back to settings.json
try:
with open(settings_file, "w") as f:
json.dump(settings, f, indent=2)
print_success(f"Successfully updated settings in: {COLOR_BOLD}{settings_file}{COLOR_RESET}")
except Exception as e:
print_error(f"Failed to write settings file: {e}")
sys.exit(1)
# 7. Post-configuration actions & verification
print_step(7, "Verification & GCP Initialization")
# Set default project in gcloud if selected
if gcloud_available and auth_method != "env":
set_proj = prompt_yes_no(f"Do you want to set '{project_id}' as your active gcloud project?", default=True)
if set_proj:
success, _, err = run_command(["gcloud", "config", "set", "project", project_id])
if success:
print_success(f"Active project set to {project_id}")
else:
print_error(f"Failed to set active project: {err}")
# Ask to enable Vertex AI API
enable_api = prompt_yes_no("Do you want to enable the Vertex AI API (aiplatform.googleapis.com) in this project?", default=True)
if enable_api:
print_info("Enabling Vertex AI API. This may take a few moments...")
success, _, err = run_command(["gcloud", "services", "enable", "aiplatform.googleapis.com"])
if success:
print_success("Vertex AI API successfully enabled!")
else:
print_error(f"Failed to enable Vertex AI API: {err}")
# Ask to run application-default login if ADC selected
if auth_method == "adc":
run_login = prompt_yes_no("Do you want to authenticate with Application Default Credentials (ADC) right now?", default=True)
if run_login:
print_info("Running gcloud auth application-default login...")
success, _, err = run_command(["gcloud", "auth", "application-default", "login"])
if success:
print_success("Successfully authenticated Application Default Credentials!")
else:
print_error(f"Authentication failed: {err}")
# Check and enable data sharing for Fable models if needed
if fable_sharing_models:
if gcloud_available:
print_info(f"Fable model(s) detected: {fable_sharing_models}")
enable_ds = prompt_yes_no("Do you want to enable Anthropic data sharing for your Claude Fable models now?", default=True)
if enable_ds:
for model_id in fable_sharing_models:
configure_data_sharing(project_id, region, model_id)
else:
print_warning("Google Cloud SDK (gcloud) is not available. We cannot automatically run setPublisherModelConfig.")
print_warning("You must manually enable data sharing for Anthropic on these model(s) to avoid 403 errors.")
# Conclusion & instructions
print_header("Configuration Complete!")
print(f"{COLOR_BOLD}{COLOR_GREEN}Claude Code is now configured to run on Google Vertex AI!{COLOR_RESET}\n")
print(f"{COLOR_BOLD}Summary of Settings applied:{COLOR_RESET}")
print(f" - Use Vertex AI: {COLOR_GREEN}Enabled{COLOR_RESET}")
print(f" - Project ID: {COLOR_CYAN}{project_id}{COLOR_RESET}")
print(f" - Region: {COLOR_CYAN}{region}{COLOR_RESET}")
print(f" - Auth Method: {COLOR_CYAN}{auth_method.upper()}{COLOR_RESET}")
if sa_key_path:
print(f" - Service Account Key: {COLOR_CYAN}{sa_key_path}{COLOR_RESET}")
if customize_models:
print(f" - Model Pins: {COLOR_CYAN}{model_pins}{COLOR_RESET}")
print(f"\n{COLOR_BOLD}Next Steps:{COLOR_RESET}")
print(f" 1. Ensure you have requested access to the Claude models in the {COLOR_BOLD}Vertex AI Model Garden{COLOR_RESET}.")
print(f" URL: {COLOR_UNDERLINE}https://console.cloud.google.com/vertex-ai/model-garden{COLOR_RESET}")
print(f" 2. Assign the necessary IAM permissions ({COLOR_BOLD}roles/aiplatform.user{COLOR_RESET} or custom role with")
print(f" {COLOR_BOLD}aiplatform.endpoints.predict{COLOR_RESET} permissions) to your authenticated user or service account.")
print(f" 3. Launch Claude Code by running:")
print(f" {COLOR_GREEN}{COLOR_BOLD}claude{COLOR_RESET}")
print("\nEnjoy using Claude Code on Google Cloud Vertex AI!")
if __name__ == "__main__":
main()
@salekh

salekh commented Jun 11, 2026

Copy link
Copy Markdown
Author

Claude Code Google Vertex AI Setup

An interactive script to configure Claude Code to run on Google Cloud Vertex AI using your GCP credentials and project settings.

Features

  • Prequisite Check: Verifies if Google Cloud SDK (gcloud) is installed.
  • Interactive Prompts: Asks for your Project ID (detects active gcloud project as default), region, authentication method, and model pinning.
  • Automatic Settings Update: Safely updates or creates your Claude Code user configuration (~/.claude/settings.json), merging the Vertex AI configuration with any existing settings.
  • API Activation: Offers to automatically enable the Vertex AI API (aiplatform.googleapis.com) on your selected project.
  • ADC Login: Offers to run gcloud auth application-default login directly from the script if Application Default Credentials (ADC) are selected.

Prerequisites

If you do not have the Claude Code CLI (claude) installed, you can install it using:

curl -fsSL https://claude.ai/install.sh | bash

Usage

Run the script using python3:

python3 setup_claude_vertex.py

Or run it directly (as it has executable permissions):

./setup_claude_vertex.py

Setup Options

During execution, the script will prompt you for:

  1. GCP Project ID: The ID of the project where you want to access the Claude models.
  2. Vertex AI Region: Choice between global (recommended), us, eu, or custom/specific regions.
  3. Authentication Method:
    • Application Default Credentials (ADC) (default & recommended)
    • Service Account Key File (absolute path to service account JSON key file)
    • Environment Variables (handles auth externally)
  4. Model Pinning (Optional): Pin specific model IDs to override defaults for Sonnet, Haiku, or Opus.

Required GCP Permissions & APIs

To run Claude Code via Google Vertex AI, your GCP project and user/service account must have the following APIs and permissions configured:

1. Enabled APIs

  • Vertex AI API (aiplatform.googleapis.com): This is the core API required to run inference on generative models hosted on Vertex AI.

2. IAM Roles & Permissions

The user or service account authenticating Claude Code needs permission to invoke the models.

  • Recommended Role: roles/aiplatform.user (Vertex AI User)
  • Minimum Required Permission: aiplatform.endpoints.predict (used for model invocation and token counting).
  • Least Privilege (Custom Role): If you want to restrict access as much as possible, you can create a custom IAM role containing only the aiplatform.endpoints.predict permission and assign it to the identity running Claude Code.

3. Vertex AI Model Garden Access

  • You must request and be granted access to the Claude models within your GCP Project.
  • Navigate to the Vertex AI Model Garden, search for "Claude", select the models you want to use (such as Claude 4.6 Sonnet or Claude 4.8 Opus), and click Enable or request access.

4. Data Sharing for Claude Fable Models

Advanced models such as Claude Fable 5 (claude-fable-5@default) require you to explicitly opt-in to data sharing with Anthropic for misuse and abuse monitoring. If data sharing is not enabled, attempting to use the model will result in a 403 PERMISSION_DENIED error.

Opting in is performed by calling the setPublisherModelConfig API on Vertex AI. You can execute this call using curl as follows:

# Set your configuration variables
PROJECT_ID="YOUR_PROJECT_ID"
LOCATION="global"             # global, us, eu, us-east5, etc.
MODEL_NAME="claude-fable-5"   # base model name without @default

# Call setPublisherModelConfig to opt-in
ENDPOINT="aiplatform.googleapis.com"
if [ "$LOCATION" != "global" ]; then
  ENDPOINT="${LOCATION}-aiplatform.googleapis.com"
fi

curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{"publisherModelConfig":{"dataSharingEnabledProvider":"anthropic"}}' \
  "https://${ENDPOINT}/v1beta1/projects/${PROJECT_ID}/locations/${LOCATION}/publishers/anthropic/models/${MODEL_NAME}:setPublisherModelConfig"

The interactive setup script will offer to run this command automatically if a Fable model is selected.

Troubleshooting

  • Access Denied / Permissions error: Ensure that the user account or service account has been granted the roles/aiplatform.user IAM role (which includes the aiplatform.endpoints.predict permission).
  • Model not found / Not enabled: Request access to the Claude models in the Vertex AI Model Garden: Model Garden Console.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment