gcp-cli Best Practices
The gcloud CLI is your primary interface for Google Cloud. These guidelines ensure your gcloud scripts and commands are reliable, secure, and easy to maintain, adhering to Google's own Shell Style Guide and modern cloud security principles.
1. Code Organization and Structure
Always structure your shell scripts for readability and robustness. Use bash and adhere to a consistent style.
Use Bash with Strict Mode: Always start scripts with the correct shebang and enable strict mode for early error detection.
#!/usr/bin/env bash
set -euo pipefail ensures scripts exit on error, undefined variables, and pipe failures.
IFS=$'\n\t' prevents unexpected word splitting.
❌ BAD:
#!/bin/sh
# No strict mode, prone to silent failures
gcloud compute instances list --format="value(name)" | while read instance; do
echo "Processing $instance"
# ... potentially fails silently if gcloud command exits non-zero
done
✅ GOOD:
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# Function for consistent error logging
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
main() {
# Explicitly set project for clarity and safety
local PROJECT_ID="your-gcp-project-id"
# Example: Iterate and process instances
gcloud compute instances list --project="${PROJECT_ID}" --format="value(name)" | while read -r instance; do
echo "Processing instance: ${instance}"
# Perform gcloud operation, check exit code if not using set -e for specific commands
if ! gcloud compute instances stop "${instance}" --project="${PROJECT_ID}" --zone="us-central1-a" --quiet; then
err "Failed to stop instance: ${instance}"
exit 1 # Exit script on critical failure
fi
done
}
main "$@"
Functions for Reusability: Encapsulate logic in functions. Use local for function-scoped variables.
- Include clear function comments (description, globals, arguments, outputs, returns).
❌ BAD:
# Global variable pollution, hard to debug
PROJECT="my-project"
gcloud config set project "${PROJECT}"
# ... more script ...
✅ GOOD:
# Function header comment as per Google Shell Style Guide
########################################
# Sets the active gcloud project.
# Globals:
# None
# Arguments:
# $1: The project ID to set.
# Returns:
# 0 if successful, non-zero on error.
#######################################
set_gcp_project() {
local project_id="$1"
if ! gcloud config set project "${project_id}" --quiet; then
err "Failed to set gcloud project to ${project_id}"
return 1
fi
echo "Active gcloud project set to: ${project_id}"
return 0
}
# Usage
set_gcp_project "my-gcp-project-id"
2. Common Patterns and Anti-patterns
Explicit Project and Zone/Region: Always specify --project, --zone, and --region flags. Relying on gcloud config for critical operations can lead to errors in different environments.
❌ BAD:
# Relies on configured project/zone, brittle in automation
gcloud compute instances create my-vm --machine-type=e2-medium
✅ GOOD:
gcloud compute instances create my-vm \
--project="my-gcp-project-id" \
--zone="us-central1-a" \
--machine-type="e2-medium"
Structured Output Parsing: Use --format=json or --format=yaml with jq or yq for reliable output parsing. Avoid grep, awk, sed on human-readable output.
❌ BAD:
# Fragile: depends on output format, breaks with gcloud updates
gcloud compute instances list | grep "RUNNING" | awk '{print $1}'
✅ GOOD:
# Robust: parses JSON output reliably
gcloud compute instances list --project="my-gcp-project-id" --format="json" | \
jq -r '.[] | select(.status == "RUNNING") | .name'
Service Account Impersonation: For automation, prefer --impersonate-service-account over activating service account keys directly. This uses short-lived tokens and improves security.
❌ BAD:
# Activates a service account key, long-lived credential risk
gcloud auth activate-service-account --key-file=/path/to/key.json
gcloud compute instances list # ...
✅ GOOD:
# Impersonates a service account using current user's permissions
gcloud compute instances list \
--project="my-gcp-project-id" \
--impersonate-service-account="my-service-account@my-gcp-project-id.iam.gserviceaccount.com"
3. Performance Considerations
Minimize API Calls: Batch operations where possible. Avoid calling gcloud commands inside tight loops if a single call can fetch all necessary data.
❌ BAD:
# N+1 problem: many API calls for each instance
gcloud compute instances list --project="my-gcp-project-id" --format="value(name)" | while read -r instance; do
gcloud compute instances describe "${instance}" --project="my-gcp-project-id" --format="value(status)"
done
✅ GOOD:
# Single API call, process data locally
gcloud compute instances list --project="my-gcp-project-id" --format="json" | \
jq -r '.[] | "\(.name) \(.status)"' | \
while read -r name status; do
echo "Instance ${name} has status ${status}"
done
4. Common Pitfalls and Gotchas
Unquoted Variables: Always quote variable expansions to prevent word splitting and globbing.
❌ BAD:
# If INSTANCE_NAME contains spaces or wildcards, this will fail
INSTANCE_NAME="my instance"
gcloud compute instances describe $INSTANCE_NAME
✅ GOOD:
INSTANCE_NAME="my instance"
gcloud compute instances describe "${INSTANCE_NAME}" --project="my-gcp-project-id"
Interactive Prompts in Automation: Use --quiet (-q) for non-interactive scripts.
❌ BAD:
# Will prompt for confirmation, blocking automation
gcloud compute instances delete my-vm --project="my-gcp-project-id"
✅ GOOD:
gcloud compute instances delete my-vm --project="my-gcp-project-id" --quiet
Keep gcloud Updated: Regularly update your Cloud SDK to access the latest features and security patches.
gcloud components update --quiet
5. Configuration Management
Named Configurations: Use gcloud config configurations to manage distinct environments (e.g., dev, staging, prod).
# Create a new configuration
gcloud config configurations create staging
# Activate and initialize it
gcloud config configurations activate staging
gcloud init --console-only # Use --console-only for non-browser init
# Switch back to default
gcloud config configurations activate default
Store Configs Outside Source Control: Never commit gcloud configuration files or service account keys to source control.
6. Environment Variables
Override Configuration: Use CLOUDSDK_ACTIVE_CONFIG_NAME to temporarily switch configurations without modifying the default.
# Run a command against the 'staging' config without activating it globally
CLOUDSDK_ACTIVE_CONFIG_NAME=staging gcloud compute instances list --format="value(name)"
Disable Prompts: CLOUDSDK_CORE_DISABLE_PROMPTS=1 is equivalent to --quiet.
# Non-interactive deletion via environment variable
CLOUDSDK_CORE_DISABLE_PROMPTS=1 gcloud compute instances delete old-vm --project="my-gcp-project-id"
7. Logging
Separate STDOUT and STDERR: Direct command output to STDOUT and informational/error messages to STDERR.
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][ERROR]: $*" >&2
}
log() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][INFO]: $*"
}
log "Starting instance creation..."
if ! gcloud compute instances create my-new-vm --project="my-gcp-project-id" --zone="us-central1-a" --machine-type="e2-small" --quiet; then
err "Failed to create instance my-new-vm."
exit 1
fi
log "Instance my-new-vm created successfully."
Verbose Logging: Use --verbosity=debug for detailed gcloud command output during debugging.
8. Testing Approaches
ShellCheck: Integrate ShellCheck into your CI/CD pipeline and local development workflow to catch common shell scripting errors.
shellcheck your-script.sh
Unit Testing: For complex shell scripts, use frameworks like shunit2 to write unit tests for individual functions.
Integration Testing: Create a dedicated, isolated GCP project for integration tests. Use gcloud commands to provision resources, run tests, and then tear down resources. This ensures your scripts work against real GCP services.
1---2name: gcp-cli3description: "[Applies to: **/*] Definitive guidelines for writing robust, secure, and maintainable scripts and workflows using the Google Cloud CLI (gcloud)."4source: "cursor_mdc"5---67# gcp-cli Best Practices89The `gcloud` CLI is your primary interface for Google Cloud. These guidelines ensure your `gcloud` scripts and commands are reliable, secure, and easy to maintain, adhering to Google's own Shell Style Guide and modern cloud security principles.1011## 1. Code Organization and Structure1213Always structure your shell scripts for readability and robustness. Use `bash` and adhere to a consistent style.1415* **Use Bash with Strict Mode**: Always start scripts with the correct shebang and enable strict mode for early error detection.16 * `#!/usr/bin/env bash`17 * `set -euo pipefail` ensures scripts exit on error, undefined variables, and pipe failures.18 * `IFS=$'\n\t'` prevents unexpected word splitting.1920 ❌ BAD:21 ```bash22 #!/bin/sh23 # No strict mode, prone to silent failures24 gcloud compute instances list --format="value(name)" | while read instance; do25 echo "Processing $instance"26 # ... potentially fails silently if gcloud command exits non-zero27 done28 ```2930 ✅ GOOD:31 ```bash32 #!/usr/bin/env bash33 set -euo pipefail34 IFS=$'\n\t'3536 # Function for consistent error logging37 err() {38 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&239 }4041 main() {42 # Explicitly set project for clarity and safety43 local PROJECT_ID="your-gcp-project-id"4445 # Example: Iterate and process instances46 gcloud compute instances list --project="${PROJECT_ID}" --format="value(name)" | while read -r instance; do47 echo "Processing instance: ${instance}"48 # Perform gcloud operation, check exit code if not using set -e for specific commands49 if ! gcloud compute instances stop "${instance}" --project="${PROJECT_ID}" --zone="us-central1-a" --quiet; then50 err "Failed to stop instance: ${instance}"51 exit 1 # Exit script on critical failure52 fi53 done54 }5556 main "$@"57 ```5859* **Functions for Reusability**: Encapsulate logic in functions. Use `local` for function-scoped variables.60 * Include clear function comments (description, globals, arguments, outputs, returns).6162 ❌ BAD:63 ```bash64 # Global variable pollution, hard to debug65 PROJECT="my-project"66 gcloud config set project "${PROJECT}"67 # ... more script ...68 ```6970 ✅ GOOD:71 ```bash72 # Function header comment as per Google Shell Style Guide73 ########################################74 # Sets the active gcloud project.75 # Globals:76 # None77 # Arguments:78 # $1: The project ID to set.79 # Returns:80 # 0 if successful, non-zero on error.81 #######################################82 set_gcp_project() {83 local project_id="$1"84 if ! gcloud config set project "${project_id}" --quiet; then85 err "Failed to set gcloud project to ${project_id}"86 return 187 fi88 echo "Active gcloud project set to: ${project_id}"89 return 090 }9192 # Usage93 set_gcp_project "my-gcp-project-id"94 ```9596## 2. Common Patterns and Anti-patterns9798* **Explicit Project and Zone/Region**: Always specify `--project`, `--zone`, and `--region` flags. Relying on `gcloud config` for critical operations can lead to errors in different environments.99100 ❌ BAD:101 ```bash102 # Relies on configured project/zone, brittle in automation103 gcloud compute instances create my-vm --machine-type=e2-medium104 ```105106 ✅ GOOD:107 ```bash108 gcloud compute instances create my-vm \109 --project="my-gcp-project-id" \110 --zone="us-central1-a" \111 --machine-type="e2-medium"112 ```113114* **Structured Output Parsing**: Use `--format=json` or `--format=yaml` with `jq` or `yq` for reliable output parsing. Avoid `grep`, `awk`, `sed` on human-readable output.115116 ❌ BAD:117 ```bash118 # Fragile: depends on output format, breaks with gcloud updates119 gcloud compute instances list | grep "RUNNING" | awk '{print $1}'120 ```121122 ✅ GOOD:123 ```bash124 # Robust: parses JSON output reliably125 gcloud compute instances list --project="my-gcp-project-id" --format="json" | \126 jq -r '.[] | select(.status == "RUNNING") | .name'127 ```128129* **Service Account Impersonation**: For automation, prefer `--impersonate-service-account` over activating service account keys directly. This uses short-lived tokens and improves security.130131 ❌ BAD:132 ```bash133 # Activates a service account key, long-lived credential risk134 gcloud auth activate-service-account --key-file=/path/to/key.json135 gcloud compute instances list # ...136 ```137138 ✅ GOOD:139 ```bash140 # Impersonates a service account using current user's permissions141 gcloud compute instances list \142 --project="my-gcp-project-id" \143 --impersonate-service-account="my-service-account@my-gcp-project-id.iam.gserviceaccount.com"144 ```145146## 3. Performance Considerations147148* **Minimize API Calls**: Batch operations where possible. Avoid calling `gcloud` commands inside tight loops if a single call can fetch all necessary data.149150 ❌ BAD:151 ```bash152 # N+1 problem: many API calls for each instance153 gcloud compute instances list --project="my-gcp-project-id" --format="value(name)" | while read -r instance; do154 gcloud compute instances describe "${instance}" --project="my-gcp-project-id" --format="value(status)"155 done156 ```157158 ✅ GOOD:159 ```bash160 # Single API call, process data locally161 gcloud compute instances list --project="my-gcp-project-id" --format="json" | \162 jq -r '.[] | "\(.name) \(.status)"' | \163 while read -r name status; do164 echo "Instance ${name} has status ${status}"165 done166 ```167168## 4. Common Pitfalls and Gotchas169170* **Unquoted Variables**: Always quote variable expansions to prevent word splitting and globbing.171172 ❌ BAD:173 ```bash174 # If INSTANCE_NAME contains spaces or wildcards, this will fail175 INSTANCE_NAME="my instance"176 gcloud compute instances describe $INSTANCE_NAME177 ```178179 ✅ GOOD:180 ```bash181 INSTANCE_NAME="my instance"182 gcloud compute instances describe "${INSTANCE_NAME}" --project="my-gcp-project-id"183 ```184185* **Interactive Prompts in Automation**: Use `--quiet` (`-q`) for non-interactive scripts.186187 ❌ BAD:188 ```bash189 # Will prompt for confirmation, blocking automation190 gcloud compute instances delete my-vm --project="my-gcp-project-id"191 ```192193 ✅ GOOD:194 ```bash195 gcloud compute instances delete my-vm --project="my-gcp-project-id" --quiet196 ```197198* **Keep `gcloud` Updated**: Regularly update your Cloud SDK to access the latest features and security patches.199200 ```bash201 gcloud components update --quiet202 ```203204## 5. Configuration Management205206* **Named Configurations**: Use `gcloud config configurations` to manage distinct environments (e.g., dev, staging, prod).207208 ```bash209 # Create a new configuration210 gcloud config configurations create staging211 # Activate and initialize it212 gcloud config configurations activate staging213 gcloud init --console-only # Use --console-only for non-browser init214 # Switch back to default215 gcloud config configurations activate default216 ```217218* **Store Configs Outside Source Control**: Never commit `gcloud` configuration files or service account keys to source control.219220## 6. Environment Variables221222* **Override Configuration**: Use `CLOUDSDK_ACTIVE_CONFIG_NAME` to temporarily switch configurations without modifying the default.223224 ```bash225 # Run a command against the 'staging' config without activating it globally226 CLOUDSDK_ACTIVE_CONFIG_NAME=staging gcloud compute instances list --format="value(name)"227 ```228229* **Disable Prompts**: `CLOUDSDK_CORE_DISABLE_PROMPTS=1` is equivalent to `--quiet`.230231 ```bash232 # Non-interactive deletion via environment variable233 CLOUDSDK_CORE_DISABLE_PROMPTS=1 gcloud compute instances delete old-vm --project="my-gcp-project-id"234 ```235236## 7. Logging237238* **Separate STDOUT and STDERR**: Direct command output to `STDOUT` and informational/error messages to `STDERR`.239240 ```bash241 #!/usr/bin/env bash242 set -euo pipefail243 IFS=$'\n\t'244245 err() {246 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][ERROR]: $*" >&2247 }248249 log() {250 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][INFO]: $*"251 }252253 log "Starting instance creation..."254 if ! gcloud compute instances create my-new-vm --project="my-gcp-project-id" --zone="us-central1-a" --machine-type="e2-small" --quiet; then255 err "Failed to create instance my-new-vm."256 exit 1257 fi258 log "Instance my-new-vm created successfully."259 ```260261* **Verbose Logging**: Use `--verbosity=debug` for detailed `gcloud` command output during debugging.262263## 8. Testing Approaches264265* **ShellCheck**: Integrate ShellCheck into your CI/CD pipeline and local development workflow to catch common shell scripting errors.266267 ```bash268 shellcheck your-script.sh269 ```270271* **Unit Testing**: For complex shell scripts, use frameworks like `shunit2` to write unit tests for individual functions.272* **Integration Testing**: Create a dedicated, isolated GCP project for integration tests. Use `gcloud` commands to provision resources, run tests, and then tear down resources. This ensures your scripts work against real GCP services.