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).4---56# gcp-cli Best Practices78The `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.910## 1. Code Organization and Structure1112Always structure your shell scripts for readability and robustness. Use `bash` and adhere to a consistent style.1314* **Use Bash with Strict Mode**: Always start scripts with the correct shebang and enable strict mode for early error detection.15 * `#!/usr/bin/env bash`16 * `set -euo pipefail` ensures scripts exit on error, undefined variables, and pipe failures.17 * `IFS=$'\n\t'` prevents unexpected word splitting.1819 ❌ BAD:20 ```bash21 #!/bin/sh22 # No strict mode, prone to silent failures23 gcloud compute instances list --format="value(name)" | while read instance; do24 echo "Processing $instance"25 # ... potentially fails silently if gcloud command exits non-zero26 done27 ```2829 ✅ GOOD:30 ```bash31 #!/usr/bin/env bash32 set -euo pipefail33 IFS=$'\n\t'3435 # Function for consistent error logging36 err() {37 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&238 }3940 main() {41 # Explicitly set project for clarity and safety42 local PROJECT_ID="your-gcp-project-id"4344 # Example: Iterate and process instances45 gcloud compute instances list --project="${PROJECT_ID}" --format="value(name)" | while read -r instance; do46 echo "Processing instance: ${instance}"47 # Perform gcloud operation, check exit code if not using set -e for specific commands48 if ! gcloud compute instances stop "${instance}" --project="${PROJECT_ID}" --zone="us-central1-a" --quiet; then49 err "Failed to stop instance: ${instance}"50 exit 1 # Exit script on critical failure51 fi52 done53 }5455 main "$@"56 ```5758* **Functions for Reusability**: Encapsulate logic in functions. Use `local` for function-scoped variables.59 * Include clear function comments (description, globals, arguments, outputs, returns).6061 ❌ BAD:62 ```bash63 # Global variable pollution, hard to debug64 PROJECT="my-project"65 gcloud config set project "${PROJECT}"66 # ... more script ...67 ```6869 ✅ GOOD:70 ```bash71 # Function header comment as per Google Shell Style Guide72 ########################################73 # Sets the active gcloud project.74 # Globals:75 # None76 # Arguments:77 # $1: The project ID to set.78 # Returns:79 # 0 if successful, non-zero on error.80 #######################################81 set_gcp_project() {82 local project_id="$1"83 if ! gcloud config set project "${project_id}" --quiet; then84 err "Failed to set gcloud project to ${project_id}"85 return 186 fi87 echo "Active gcloud project set to: ${project_id}"88 return 089 }9091 # Usage92 set_gcp_project "my-gcp-project-id"93 ```9495## 2. Common Patterns and Anti-patterns9697* **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.9899 ❌ BAD:100 ```bash101 # Relies on configured project/zone, brittle in automation102 gcloud compute instances create my-vm --machine-type=e2-medium103 ```104105 ✅ GOOD:106 ```bash107 gcloud compute instances create my-vm \108 --project="my-gcp-project-id" \109 --zone="us-central1-a" \110 --machine-type="e2-medium"111 ```112113* **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.114115 ❌ BAD:116 ```bash117 # Fragile: depends on output format, breaks with gcloud updates118 gcloud compute instances list | grep "RUNNING" | awk '{print $1}'119 ```120121 ✅ GOOD:122 ```bash123 # Robust: parses JSON output reliably124 gcloud compute instances list --project="my-gcp-project-id" --format="json" | \125 jq -r '.[] | select(.status == "RUNNING") | .name'126 ```127128* **Service Account Impersonation**: For automation, prefer `--impersonate-service-account` over activating service account keys directly. This uses short-lived tokens and improves security.129130 ❌ BAD:131 ```bash132 # Activates a service account key, long-lived credential risk133 gcloud auth activate-service-account --key-file=/path/to/key.json134 gcloud compute instances list # ...135 ```136137 ✅ GOOD:138 ```bash139 # Impersonates a service account using current user's permissions140 gcloud compute instances list \141 --project="my-gcp-project-id" \142 --impersonate-service-account="my-service-account@my-gcp-project-id.iam.gserviceaccount.com"143 ```144145## 3. Performance Considerations146147* **Minimize API Calls**: Batch operations where possible. Avoid calling `gcloud` commands inside tight loops if a single call can fetch all necessary data.148149 ❌ BAD:150 ```bash151 # N+1 problem: many API calls for each instance152 gcloud compute instances list --project="my-gcp-project-id" --format="value(name)" | while read -r instance; do153 gcloud compute instances describe "${instance}" --project="my-gcp-project-id" --format="value(status)"154 done155 ```156157 ✅ GOOD:158 ```bash159 # Single API call, process data locally160 gcloud compute instances list --project="my-gcp-project-id" --format="json" | \161 jq -r '.[] | "\(.name) \(.status)"' | \162 while read -r name status; do163 echo "Instance ${name} has status ${status}"164 done165 ```166167## 4. Common Pitfalls and Gotchas168169* **Unquoted Variables**: Always quote variable expansions to prevent word splitting and globbing.170171 ❌ BAD:172 ```bash173 # If INSTANCE_NAME contains spaces or wildcards, this will fail174 INSTANCE_NAME="my instance"175 gcloud compute instances describe $INSTANCE_NAME176 ```177178 ✅ GOOD:179 ```bash180 INSTANCE_NAME="my instance"181 gcloud compute instances describe "${INSTANCE_NAME}" --project="my-gcp-project-id"182 ```183184* **Interactive Prompts in Automation**: Use `--quiet` (`-q`) for non-interactive scripts.185186 ❌ BAD:187 ```bash188 # Will prompt for confirmation, blocking automation189 gcloud compute instances delete my-vm --project="my-gcp-project-id"190 ```191192 ✅ GOOD:193 ```bash194 gcloud compute instances delete my-vm --project="my-gcp-project-id" --quiet195 ```196197* **Keep `gcloud` Updated**: Regularly update your Cloud SDK to access the latest features and security patches.198199 ```bash200 gcloud components update --quiet201 ```202203## 5. Configuration Management204205* **Named Configurations**: Use `gcloud config configurations` to manage distinct environments (e.g., dev, staging, prod).206207 ```bash208 # Create a new configuration209 gcloud config configurations create staging210 # Activate and initialize it211 gcloud config configurations activate staging212 gcloud init --console-only # Use --console-only for non-browser init213 # Switch back to default214 gcloud config configurations activate default215 ```216217* **Store Configs Outside Source Control**: Never commit `gcloud` configuration files or service account keys to source control.218219## 6. Environment Variables220221* **Override Configuration**: Use `CLOUDSDK_ACTIVE_CONFIG_NAME` to temporarily switch configurations without modifying the default.222223 ```bash224 # Run a command against the 'staging' config without activating it globally225 CLOUDSDK_ACTIVE_CONFIG_NAME=staging gcloud compute instances list --format="value(name)"226 ```227228* **Disable Prompts**: `CLOUDSDK_CORE_DISABLE_PROMPTS=1` is equivalent to `--quiet`.229230 ```bash231 # Non-interactive deletion via environment variable232 CLOUDSDK_CORE_DISABLE_PROMPTS=1 gcloud compute instances delete old-vm --project="my-gcp-project-id"233 ```234235## 7. Logging236237* **Separate STDOUT and STDERR**: Direct command output to `STDOUT` and informational/error messages to `STDERR`.238239 ```bash240 #!/usr/bin/env bash241 set -euo pipefail242 IFS=$'\n\t'243244 err() {245 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][ERROR]: $*" >&2246 }247248 log() {249 echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')][INFO]: $*"250 }251252 log "Starting instance creation..."253 if ! gcloud compute instances create my-new-vm --project="my-gcp-project-id" --zone="us-central1-a" --machine-type="e2-small" --quiet; then254 err "Failed to create instance my-new-vm."255 exit 1256 fi257 log "Instance my-new-vm created successfully."258 ```259260* **Verbose Logging**: Use `--verbosity=debug` for detailed `gcloud` command output during debugging.261262## 8. Testing Approaches263264* **ShellCheck**: Integrate ShellCheck into your CI/CD pipeline and local development workflow to catch common shell scripting errors.265266 ```bash267 shellcheck your-script.sh268 ```269270* **Unit Testing**: For complex shell scripts, use frameworks like `shunit2` to write unit tests for individual functions.271* **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.