Last active
June 11, 2026 08:45
-
-
Save salekh/58502463356836d87429ee5354d0f6d3 to your computer and use it in GitHub Desktop.
Set up Claude Code on GCP
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
| #!/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() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
gcloud) is installed.gcloudproject as default), region, authentication method, and model pinning.~/.claude/settings.json), merging the Vertex AI configuration with any existing settings.aiplatform.googleapis.com) on your selected project.gcloud auth application-default logindirectly 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 | bashUsage
Run the script using python3:
Or run it directly (as it has executable permissions):
Setup Options
During execution, the script will prompt you for:
global(recommended),us,eu, or custom/specific regions.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
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.
roles/aiplatform.user(Vertex AI User)aiplatform.endpoints.predict(used for model invocation and token counting).aiplatform.endpoints.predictpermission and assign it to the identity running Claude Code.3. Vertex AI Model Garden 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 a403 PERMISSION_DENIEDerror.Opting in is performed by calling the
setPublisherModelConfigAPI on Vertex AI. You can execute this call usingcurlas follows:The interactive setup script will offer to run this command automatically if a Fable model is selected.
Troubleshooting
roles/aiplatform.userIAM role (which includes theaiplatform.endpoints.predictpermission).