Created
March 26, 2026 13:09
-
-
Save w3irdrobot/76fe8ad88c027009e12b1bc1e88b5a15 to your computer and use it in GitHub Desktop.
Script for getting a rundown of an existing GKE cluster
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
| #!/bin/bash | |
| # GKE Cluster and NAT Information Script | |
| # Usage: ./gke-nat-info.sh <cluster_name> [zone_or_region] | |
| set -e | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| # Check arguments | |
| if [ $# -lt 1 ]; then | |
| echo -e "${RED}Usage: $0 <cluster_name> [zone_or_region]${NC}" | |
| echo " zone_or_region: Optional. Defaults to gcloud config or prompts" | |
| exit 1 | |
| fi | |
| CLUSTER_NAME="$1" | |
| LOCATION="${2:-}" | |
| # Function to print section headers | |
| print_header() { | |
| echo -e "\n${CYAN}═══════════════════════════════════════════════════════════════${NC}" | |
| echo -e "${CYAN} $1${NC}" | |
| echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" | |
| } | |
| # Function to print subsection | |
| print_subheader() { | |
| echo -e "\n${YELLOW}► $1${NC}" | |
| } | |
| # Function to print info line | |
| print_info() { | |
| printf " %-35s " "$1:" | |
| echo -e "$2" | |
| } | |
| # Function to print error | |
| print_error() { | |
| echo -e "${RED}✗ $1${NC}" | |
| } | |
| # Function to print success | |
| print_success() { | |
| echo -e "${GREEN}✓ $1${NC}" | |
| } | |
| # Check if gcloud is installed | |
| if ! command -v gcloud &> /dev/null; then | |
| print_error "gcloud CLI is not installed" | |
| exit 1 | |
| fi | |
| # Check if jq is installed | |
| if ! command -v jq &> /dev/null; then | |
| print_error "jq is not installed. Please install jq to run this script." | |
| exit 1 | |
| fi | |
| # Determine location if not provided | |
| if [ -z "$LOCATION" ]; then | |
| # Try to get from gcloud config | |
| LOCATION=$(gcloud config get-value compute/zone 2>/dev/null || true) | |
| if [ -z "$LOCATION" ]; then | |
| LOCATION=$(gcloud config get-value compute/region 2>/dev/null || true) | |
| fi | |
| if [ -z "$LOCATION" ]; then | |
| print_error "No zone or region specified and none found in gcloud config" | |
| exit 1 | |
| fi | |
| fi | |
| # Detect if location is zone or region | |
| if [[ "$LOCATION" =~ ^[a-z]+-[a-z]+[0-9]-[a-z]$ ]]; then | |
| LOCATION_FLAG="--zone=$LOCATION" | |
| REGION=$(echo "$LOCATION" | sed 's/-[a-z]$//') | |
| else | |
| LOCATION_FLAG="--region=$LOCATION" | |
| REGION="$LOCATION" | |
| fi | |
| echo -e "\n${GREEN}🔍 Analyzing GKE Cluster: ${CLUSTER_NAME}${NC}" | |
| echo -e "${GREEN}📍 Location: ${LOCATION}${NC}" | |
| # ============================================ | |
| # CLUSTER INFORMATION | |
| # ============================================ | |
| print_header "CLUSTER INFORMATION" | |
| # Get cluster details | |
| CLUSTER_INFO=$(gcloud container clusters describe "$CLUSTER_NAME" $LOCATION_FLAG --format='json' 2>/dev/null) || { | |
| print_error "Failed to get cluster information. Check cluster name and location." | |
| exit 1 | |
| } | |
| # Extract basic info | |
| CLUSTER_STATUS=$(echo "$CLUSTER_INFO" | jq -r '.status // "Unknown"') | |
| NETWORK=$(echo "$CLUSTER_INFO" | jq -r '.network // "N/A"') | |
| # Extract just the network name from potential URL format (e.g., "projects/PROJECT/global/networks/NAME") | |
| NETWORK_NAME=$(echo "$NETWORK" | awk -F'/' '{print $NF}') | |
| SUBNETWORK=$(echo "$CLUSTER_INFO" | jq -r '.subnetwork // "N/A"') | |
| NETWORK_CONFIG=$(echo "$CLUSTER_INFO" | jq -r '.ipAllocationPolicy.useIpAliases // false') | |
| # Check if private nodes are enabled | |
| PRIVATE_NODES=$(echo "$CLUSTER_INFO" | jq -r '.privateClusterConfig.enablePrivateNodes // false') | |
| # Check private endpoint | |
| PRIVATE_ENDPOINT=$(echo "$CLUSTER_INFO" | jq -r '.privateClusterConfig.enablePrivateEndpoint // false') | |
| # Master CIDR | |
| MASTER_CIDR=$(echo "$CLUSTER_INFO" | jq -r '.privateClusterConfig.masterIpv4CidrBlock // "N/A"') | |
| # Print cluster details | |
| print_subheader "Basic Configuration" | |
| print_info "Cluster Name" "$CLUSTER_NAME" | |
| print_info "Location" "$LOCATION" | |
| print_info "Network" "$NETWORK" | |
| print_info "Subnetwork" "$SUBNETWORK" | |
| print_info "VPC-native (IP Alias)" "$NETWORK_CONFIG" | |
| print_subheader "Privacy Configuration" | |
| if [ "$PRIVATE_NODES" = "true" ]; then | |
| print_info "Private Nodes" "${GREEN}Enabled${NC}" | |
| else | |
| print_info "Private Nodes" "${RED}Disabled (Public)${NC}" | |
| fi | |
| if [ "$PRIVATE_ENDPOINT" = "true" ]; then | |
| print_info "Private Endpoint" "${GREEN}Enabled${NC}" | |
| else | |
| print_info "Private Endpoint" "${YELLOW}Disabled (Public)${NC}" | |
| fi | |
| print_info "Master CIDR Block" "$MASTER_CIDR" | |
| # ============================================ | |
| # NETWORK INFORMATION | |
| # ============================================ | |
| print_header "NETWORK & SUBNET DETAILS" | |
| if [ "$NETWORK" != "N/A" ] && [ -n "$NETWORK" ]; then | |
| # Get subnet details | |
| SUBNET_INFO=$(gcloud compute networks subnets describe "$SUBNETWORK" --region="$REGION" --format='json' 2>/dev/null || echo "{}") | |
| SUBNET_RANGE=$(echo "$SUBNET_INFO" | jq -r '.ipCidrRange // "N/A"') | |
| PRIVATE_GOOGLE_ACCESS=$(echo "$SUBNET_INFO" | jq -r '.privateIpGoogleAccess // false') | |
| print_info "Subnet Primary Range" "$SUBNET_RANGE" | |
| if [ "$PRIVATE_GOOGLE_ACCESS" = "true" ]; then | |
| print_info "Private Google Access" "${GREEN}Enabled${NC}" | |
| else | |
| print_info "Private Google Access" "${RED}Disabled${NC}" | |
| fi | |
| # Get secondary ranges (pod and service ranges) | |
| SECONDARY_RANGES=$(echo "$SUBNET_INFO" | jq -r '.secondaryIpRanges[]? | " \(.rangeName): \(.ipCidrRange)"' 2>/dev/null || echo "") | |
| if [ -n "$SECONDARY_RANGES" ]; then | |
| echo -e "\n ${YELLOW}Secondary IP Ranges:${NC}" | |
| echo "$SECONDARY_RANGES" | |
| fi | |
| else | |
| print_error "Could not retrieve network information" | |
| fi | |
| # ============================================ | |
| # CLOUD NAT & ROUTER INFORMATION | |
| # ============================================ | |
| print_header "CLOUD NAT & ROUTER CONFIGURATION" | |
| # Find Cloud Routers in the region | |
| ROUTERS=$(gcloud compute routers list --regions="$REGION" --filter="network:$NETWORK_NAME" --format='table[no-heading](name)' 2>/dev/null || echo "") | |
| if [ -z "$ROUTERS" ]; then | |
| print_error "No Cloud Routers found in region $REGION for network $NETWORK_NAME" | |
| echo -e "\n ${YELLOW}Note: Without Cloud NAT, private nodes will have no internet access.${NC}" | |
| else | |
| echo -e "\n ${GREEN}Found Cloud Routers:${NC}" | |
| NAT_IP_ALLOCATION="" | |
| for ROUTER in $ROUTERS; do | |
| print_subheader "Router: $ROUTER" | |
| # Get router details | |
| ROUTER_INFO=$(gcloud compute routers describe "$ROUTER" --region="$REGION" --format='json' 2>/dev/null || echo "{}") | |
| # List NAT configs | |
| NAT_CONFIGS=$(gcloud compute routers nats list --router="$ROUTER" --region="$REGION" --format='table[no-heading](name)' 2>/dev/null || echo "") | |
| if [ -z "$NAT_CONFIGS" ]; then | |
| print_info "NAT Configuration" "${RED}None${NC}" | |
| else | |
| for NAT in $NAT_CONFIGS; do | |
| print_info "NAT Gateway" "$NAT" | |
| # Get NAT details | |
| NAT_INFO=$(gcloud compute routers nats describe "$NAT" --router="$ROUTER" --region="$REGION" --format='json' 2>/dev/null || echo "{}") | |
| NAT_IP_ALLOCATION_RAW=$(echo "$NAT_INFO" | jq -r '.natIpAllocateOption // "AUTO_ONLY"') | |
| case "$NAT_IP_ALLOCATION_RAW" in | |
| "MANUAL_ONLY") | |
| NAT_IP_ALLOCATION="Manual (Static)" | |
| ;; | |
| "AUTO_ONLY") | |
| NAT_IP_ALLOCATION="Automatic (Dynamic)" | |
| ;; | |
| *) | |
| NAT_IP_ALLOCATION="$NAT_IP_ALLOCATION_RAW" | |
| ;; | |
| esac | |
| SOURCE_RANGES=$(echo "$NAT_INFO" | jq -r '.sourceSubnetworkIpRangesToNat // "ALL_SUBNETWORKS_ALL_IP_RANGES"') | |
| print_info " IP Allocation" "$NAT_IP_ALLOCATION" | |
| print_info " Source Ranges" "$SOURCE_RANGES" | |
| # Get NAT IPs - try manual first, then auto | |
| NAT_IPS=$(echo "$NAT_INFO" | jq -r '.natIps[]? | split("/") | last' 2>/dev/null || echo "") | |
| if [ -z "$NAT_IPS" ]; then | |
| # Try auto-allocated IPs | |
| NAT_IPS=$(echo "$NAT_INFO" | jq -r '.autoAllocatedNatIps[]? | split("/") | last' 2>/dev/null || echo "") | |
| fi | |
| if [ -n "$NAT_IPS" ]; then | |
| echo -e "\n ${YELLOW} NAT IP Addresses:${NC}" | |
| for IP_NAME in $NAT_IPS; do | |
| # Get actual IP address | |
| IP_ADDR=$(gcloud compute addresses describe "$IP_NAME" --region="$REGION" --format='value(address)' 2>/dev/null || echo "N/A") | |
| if [ "$IP_ADDR" != "N/A" ] && [ -n "$IP_ADDR" ]; then | |
| echo -e " ${GREEN}→ $IP_ADDR${NC} (Name: $IP_NAME)" | |
| else | |
| echo -e " → $IP_NAME (IP address not found)" | |
| fi | |
| done | |
| fi | |
| # Additional NAT settings | |
| DYNAMIC_PORT_ALLOC=$(echo "$NAT_INFO" | jq -r '.enableDynamicPortAllocation // false') | |
| MIN_PORTS=$(echo "$NAT_INFO" | jq -r '.minPortsPerVm // "N/A"') | |
| MAX_PORTS=$(echo "$NAT_INFO" | jq -r '.maxPortsPerVm // "N/A"') | |
| print_info " Dynamic Port Allocation" "$DYNAMIC_PORT_ALLOC" | |
| print_info " Min Ports per VM" "$MIN_PORTS" | |
| print_info " Max Ports per VM" "$MAX_PORTS" | |
| done | |
| fi | |
| done | |
| fi | |
| # ============================================ | |
| # FIREWALL RULES | |
| # ============================================ | |
| print_header "FIREWALL RULES" | |
| FIREWALL_RULES=$(gcloud compute firewall-rules list --filter="network:$NETWORK_NAME AND name~gke-$CLUSTER_NAME" --format='table[no-heading](name, sourceRanges.list():label=SRC_RANGES, allowed[0].ports:label=PORTS)' 2>/dev/null || echo "") | |
| if [ -n "$FIREWALL_RULES" ]; then | |
| echo -e " ${GREEN}GKE-related firewall rules found:${NC}\n" | |
| gcloud compute firewall-rules list --filter="network:$NETWORK_NAME AND name~gke-$CLUSTER_NAME" --format='table(name, direction, sourceRanges.list():label=SRC_RANGES, allowed[0].ports:label=PORTS)' 2>/dev/null || echo " Could not retrieve firewall rules" | |
| else | |
| print_info "GKE Firewall Rules" "No specific rules found (using default or broad rules)" | |
| fi | |
| # ============================================ | |
| # SUMMARY | |
| # ============================================ | |
| print_header "SUMMARY" | |
| echo "" | |
| if [ "$PRIVATE_NODES" = "true" ]; then | |
| echo -e " ${GREEN}✓ Cluster has private nodes${NC}" | |
| if [ -n "$ROUTERS" ] && [ -n "$NAT_CONFIGS" ]; then | |
| echo -e " ${GREEN}✓ Cloud NAT is configured${NC}" | |
| if [ "$NAT_IP_ALLOCATION" = "Manual (Static)" ]; then | |
| echo -e " ${GREEN}✓ Using static NAT IPs - ready for IP whitelisting${NC}" | |
| else | |
| echo -e " ${YELLOW}⚠ Using automatic NAT IPs - IPs may change over time${NC}" | |
| fi | |
| else | |
| echo -e " ${RED}✗ No Cloud NAT found - private nodes cannot reach internet!${NC}" | |
| fi | |
| else | |
| echo -e " ${YELLOW}⚠ Cluster has public nodes${NC}" | |
| echo -e " Nodes have external IPs and egress traffic bypasses NAT" | |
| echo -e " To get static egress IP, either:" | |
| echo -e " 1. Create private node pool (Standard clusters)" | |
| echo -e " 2. Convert cluster to private nodes (Autopilot)" | |
| fi | |
| echo "" | |
| echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment