Prerequisites
Overview
To successfully integrate your Google Cloud environment with Upwind, ensure the following prerequisites are met:
- 
Google Cloud Environment Structure:
- For Organization/Folder onboarding: Ensure you have a Google Cloud environment structured under an organization node. This is required for centralized visibility and access control across projects and folders. If you need to create one, refer to the Creating and managing organizations documentation.
 - For Multiple Project onboarding: Ensure you have access to the specific project(s) you want to onboard.
 
 - 
Required Permissions:
- Organization/Folder onboarding: Administrator access to your Google Cloud organization
 - Multiple Projects onboarding: Administrator access to the individual projects you want to onboard (reduced permissions - no organization admin needed)
 
 - 
gcloud CLI Setup. Ensure
gcloudis installed and authenticated with your Google Cloud environment. Ensuregcloudis updated to the latest version.gcloud auth login
gcloud auth application-default login - 
Terraform Installed. All Upwind related infrastructure will be deployed via Terraform. The minimum required version of Terraform is
1.11.0. 
If you are a Mac user who has installed Terraform using Brew note that this is an older, unsupported version. Please update your Terraform installation using the Hashicorp provided tap.
- 
Google Cloud APIs & Services. Ensure the following APIs are enabled in the Google Cloud projects you would like to be covered by Upwind. For more information on how to do this, refer to the Enable and Disable APIs guide.
Mandatory APIs
These are required for basic Upwind functionality.
Title Name Cloud Asset API cloudasset.googleapis.comCloud Resource Manager API cloudresourcemanager.googleapis.comCompute Engine API compute.googleapis.comIAM Service Account Credentials API iamcredentials.googleapis.comIdentity and Access Management API iam.googleapis.comKubernetes Engine API container.googleapis.comSecret Manager API secretmanager.googleapis.comSecurity Token Service API sts.googleapis.comService Usage API serviceusage.googleapis.com
Additional APIs for Operation and Posture
These enable additional functionality in the Upwind Platform by allowing access to a wider variety of Google Cloud services.
Title Name Access Approval API accessapproval.googleapis.comAdmin SDK Directory API admin.googleapis.comAlloyDB Admin API alloydb.googleapis.comAPI Keys API apikeys.googleapis.comBigQuery API bigquery.googleapis.comCloud Dataproc API dataproc.googleapis.comCloud DNS API dns.googleapis.comCloud Functions API cloudfunctions.googleapis.comCloud Key Management Service API cloudkms.googleapis.comCloud Logging API logging.googleapis.comCloud Monitoring API monitoring.googleapis.comCloud Redis API redis.googleapis.comCloud SQL Admin API sqladmin.googleapis.comCloud Storage API storage.googleapis.comCloud Storage Insights API storageinsights.googleapis.comEssential Contacts API essentialcontacts.googleapis.com
Additional APIs for Cloud Scanners
These APIs are required for Cloud Scanner deployments, which use both Cloud Run and Cloud Scheduler.
Title Name Cloud Run Admin API run.googleapis.comCloud Scheduler API cloudscheduler.googleapis.com 
The required APIs can be enabled using the following bash script as a Google Cloud admin:
google-cloud-enable-apis.sh
#!/bin/bash
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# API Lists
REQUIRED_APIS=(
    "cloudasset.googleapis.com"
    "cloudresourcemanager.googleapis.com"
    "compute.googleapis.com"
    "iamcredentials.googleapis.com"
    "iam.googleapis.com"
    "container.googleapis.com"
    "secretmanager.googleapis.com"
    "sts.googleapis.com"
    "serviceusage.googleapis.com"
)
POSTURE_APIS=(
    "accessapproval.googleapis.com"
    "admin.googleapis.com"
    "alloydb.googleapis.com"
    "apikeys.googleapis.com"
    "bigquery.googleapis.com"
    "dataproc.googleapis.com"
    "dns.googleapis.com"
    "cloudfunctions.googleapis.com"
    "cloudkms.googleapis.com"
    "logging.googleapis.com"
    "monitoring.googleapis.com"
    "redis.googleapis.com"
    "sqladmin.googleapis.com"
    "storage.googleapis.com"
    "storageinsights.googleapis.com"
    "essentialcontacts.googleapis.com"
)
CLOUDSCANNER_APIS=(
    "run.googleapis.com"
    "cloudscheduler.googleapis.com"
)
# Check gcloud is available and authenticated
check_gcloud() {
	if ! command -v gcloud &>/dev/null; then
		echo -e "${RED}Error: gcloud CLI not found. Please install it first.${NC}"
		exit 1
	fi
	if ! gcloud auth list --filter=status:ACTIVE --format="value(account)" &>/dev/null; then
		echo -e "${RED}Error: Not authenticated. Run: gcloud auth login${NC}"
		exit 1
	fi
	echo -e "${GREEN}✓ gcloud authenticated${NC}"
}
# Get all projects
get_projects() {
	echo -e "${BLUE}Fetching projects...${NC}"
	# Get active projects only
	PROJECTS=$(gcloud projects list --filter="lifecycleState:ACTIVE" --format="value(projectId)" 2>/dev/null)
	if [ -z "$PROJECTS" ]; then
		echo -e "${RED}No active projects found${NC}"
		exit 1
	fi
	# Convert to array
	PROJECT_LIST=()
	while IFS= read -r line; do
		PROJECT_LIST+=("$line")
	done <<<"$PROJECTS"
	echo -e "${GREEN}Found ${#PROJECT_LIST[@]} projects${NC}"
}
# Display projects for selection
select_projects() {
	echo -e "\n${BLUE}Available projects:${NC}"
	for i in "${!PROJECT_LIST[@]}"; do
		echo "  $((i + 1)). ${PROJECT_LIST[i]}"
	done
	echo -e "\n${YELLOW}Select projects:${NC}"
	echo "1. All projects"
	echo "2. Specific projects"
	while true; do
		read -p "Choice [1-2]: " choice
		case $choice in
		1)
			SELECTED_PROJECTS=("${PROJECT_LIST[@]}")
			echo -e "${GREEN}Selected all ${#SELECTED_PROJECTS[@]} projects${NC}"
			break
			;;
		2)
			select_specific_projects
			break
			;;
		*)
			echo -e "${RED}Invalid choice${NC}"
			;;
		esac
	done
}
# Select specific projects
select_specific_projects() {
	SELECTED_PROJECTS=()
	echo -e "\n${YELLOW}Enter project numbers (space-separated, e.g., '1 3 5'):${NC}"
	while true; do
		read -p "Project numbers (or 'done'): " input
		if [ "$input" = "done" ]; then
			if [ ${#SELECTED_PROJECTS[@]} -eq 0 ]; then
				echo -e "${RED}Please select at least one project${NC}"
				continue
			fi
			break
		fi
		for num in $input; do
			if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#PROJECT_LIST[@]} ]; then
				project="${PROJECT_LIST[$((num - 1))]}"
				# Add if not already selected
				if [[ ${#SELECTED_PROJECTS[@]} -eq 0 ]] || [[ ! " ${SELECTED_PROJECTS[@]} " =~ " ${project} " ]]; then
					SELECTED_PROJECTS+=("$project")
				fi
			else
				echo -e "${RED}Invalid number: $num${NC}"
			fi
		done
		if [ ${#SELECTED_PROJECTS[@]} -gt 0 ]; then
			echo -e "${GREEN}Selected projects:${NC}"
			for project in "${SELECTED_PROJECTS[@]}"; do
				echo "  - $project"
			done
		fi
	done
}
# Check required APIs and warn user
check_required_apis() {
    echo -e "\n${BLUE}Checking required APIs...${NC}"
    local missing_required=()
    local total_checked=0
    for project in "${SELECTED_PROJECTS[@]}"; do
        echo -n "  Checking $project... "
        local enabled_apis
        enabled_apis=$(get_enabled_apis "$project")
        local project_missing=()
        for api in "${REQUIRED_APIS[@]}"; do
            ((total_checked++))
            if [[ ! "$enabled_apis" =~ $api ]]; then
                project_missing+=("$api")
                missing_required+=("$project:$api")
            fi
        done
        if [[ ${#project_missing[@]} -eq 0 ]]; then
            echo -e "${GREEN}all required APIs enabled${NC}"
        else
            echo -e "${YELLOW}${#project_missing[@]} required APIs missing${NC}"
        fi
    done
    if [[ ${#missing_required[@]} -gt 0 ]]; then
        echo -e "\n${RED}WARNING: Required APIs are missing!${NC}"
        echo -e "${YELLOW}The following required APIs are not enabled:${NC}"
        # Group by project for better readability
        local current_project=""
        for item in "${missing_required[@]}"; do
            IFS=':' read -r project api <<< "$item"
            if [[ "$project" != "$current_project" ]]; then
                current_project="$project"
                echo -e "\n${BLUE}Project: $project${NC}"
            fi
            echo "  - $api"
        done
        echo -e "\n${YELLOW}These APIs will be automatically added to your selection.${NC}"
        echo -e "${BLUE}You may also want to consider adding APIs from these categories:${NC}"
        echo -e "  • ${GREEN}Posture APIs${NC} (${#POSTURE_APIS[@]} APIs): Security posture and compliance monitoring"
        echo -e "  • ${GREEN}Cloud Scanner APIs${NC} (${#CLOUDSCANNER_APIS[@]} APIs): Cloud resource scanning and analysis"
        echo -e "\n${YELLOW}Press Enter to continue with required APIs, or Ctrl+C to exit and enable them manually.${NC}"
        read -r
        # Set flag to indicate required APIs need to be included
        INCLUDE_REQUIRED_APIS=true
    else
        echo -e "${GREEN}✓ All required APIs are enabled${NC}"
        INCLUDE_REQUIRED_APIS=false
    fi
}
# Select API set
select_api_set() {
    echo -e "\n${BLUE}Select API set:${NC}"
    echo "1. Upwind Required APIs (${#REQUIRED_APIS[@]} APIs)"
    echo "2. Upwind Posture APIs (${#POSTURE_APIS[@]} APIs)"
    echo "3. Upwind Cloud Scanner APIs (${#CLOUDSCANNER_APIS[@]} APIs)"
    echo "4. All APIs (combined)"
    while true; do
        read -p "Choice [1-4]: " choice
        case $choice in
            1)
                SELECTED_APIS=("${REQUIRED_APIS[@]}")
                API_SET_NAME="Required APIs"
                break
                ;;
            2)
                SELECTED_APIS=("${POSTURE_APIS[@]}")
                API_SET_NAME="Posture APIs"
                break
                ;;
            3)
                SELECTED_APIS=("${CLOUDSCANNER_APIS[@]}")
                API_SET_NAME="Cloud Scanner APIs"
                break
                ;;
            4)
                # Combine all APIs and remove duplicates
                ALL_APIS=("${REQUIRED_APIS[@]}" "${POSTURE_APIS[@]}" "${CLOUDSCANNER_APIS[@]}")
                SELECTED_APIS=($(printf "%s\n" "${ALL_APIS[@]}" | sort -u))
                API_SET_NAME="All APIs"
                break
                ;;
            *)
                echo -e "${RED}Invalid choice${NC}"
                ;;
        esac
    done
    # If required APIs are missing, automatically include them
    if [[ "$INCLUDE_REQUIRED_APIS" == "true" ]]; then
        echo -e "\n${YELLOW}Adding missing required APIs to selection...${NC}"
        # Combine selected APIs with required APIs and remove duplicates
        COMBINED_APIS=("${SELECTED_APIS[@]}" "${REQUIRED_APIS[@]}")
        SELECTED_APIS=($(printf "%s\n" "${COMBINED_APIS[@]}" | sort -u))
        if [[ "$API_SET_NAME" == "Required APIs" ]]; then
            API_SET_NAME="Required APIs (all)"
        else
            API_SET_NAME="$API_SET_NAME + Required APIs"
        fi
    fi
    echo -e "${GREEN}Selected: $API_SET_NAME (${#SELECTED_APIS[@]} APIs)${NC}"
}
# Confirm before proceeding
confirm() {
	echo -e "\n${YELLOW}Summary:${NC}"
	echo "Projects: ${#SELECTED_PROJECTS[@]}"
	echo "APIs: $API_SET_NAME (${#SELECTED_APIS[@]})"
	echo "Total operations: $((${#SELECTED_PROJECTS[@]} * ${#SELECTED_APIS[@]}))"
	while true; do
		read -p "Proceed? [y/N]: " confirm
		case $confirm in
		[Yy]*)
			break
			;;
		[Nn]* | "")
			echo -e "${YELLOW}Cancelled${NC}"
			exit 0
			;;
		esac
	done
}
# Get enabled APIs for a project
get_enabled_apis() {
	local project=$1
	gcloud services list --enabled --project="$project" --format="value(name)" 2>/dev/null || echo ""
}
# Get missing APIs for a project
get_missing_apis() {
	local project=$1
	local enabled_apis
	enabled_apis=$(get_enabled_apis "$project")
	local missing_apis=()
	for api in "${SELECTED_APIS[@]}"; do
		if [[ ! "$enabled_apis" =~ $api ]]; then
			missing_apis+=("$api")
		fi
	done
	# Return missing APIs or empty string if none
	if [[ ${#missing_apis[@]} -gt 0 ]]; then
		printf '%s\n' "${missing_apis[@]}"
	fi
}
enable_apis() {
	echo -e "\n${BLUE}Analyzing current API status...${NC}"
	# Build list of operations needed
	local operations=()
	local skipped_count=0
	for project in "${SELECTED_PROJECTS[@]}"; do
		echo -n "  Checking $project... "
		local missing_apis
		missing_apis=$(get_missing_apis "$project")
		if [[ -z "$missing_apis" ]]; then
			skipped_count=$((skipped_count + ${#SELECTED_APIS[@]}))
			echo -e "${GREEN}all APIs already enabled${NC}"
		else
			local missing_count
			missing_count=$(echo "$missing_apis" | wc -l | tr -d ' ')
			local already_enabled=$((${#SELECTED_APIS[@]} - missing_count))
			skipped_count=$((skipped_count + already_enabled))
			echo -e "${YELLOW}$missing_count APIs needed${NC}"
			# Add each missing API as an operation
			while IFS= read -r api; do
				[[ -n "$api" ]] && operations+=("$project:$api")
			done <<<"$missing_apis"
		fi
	done
	local total_ops=${#operations[@]}
	if [[ $total_ops -eq 0 ]]; then
		echo -e "\n${GREEN}All APIs are already enabled! Nothing to do.${NC}"
		return
	fi
	echo -e "\n${BLUE}Operations needed: $total_ops (skipping $skipped_count already enabled)${NC}"
	echo -e "${BLUE}Starting API enablement...${NC}"
	local completed=0
	local successful=0
	echo -e "${BLUE}Project: ${NC}Starting..."
	local current_project=""
	for operation in "${operations[@]}"; do
		IFS=':' read -r project api <<<"$operation"
		if [[ "$project" != "$current_project" ]]; then
			if [[ -n "$current_project" ]]; then
				printf "\033[2A\033[K${BLUE}Project: ${NC}%s ${GREEN}✓ Complete${NC}\n" "$current_project"
				printf "\033[1B"
			fi
			current_project="$project"
			printf "\033[1A\033[K${BLUE}Project: ${NC}%s\n" "$project"
			echo "Operation: Initializing..."
		fi
		printf "\033[1A\033[K${YELLOW}Operation: ${NC}Enabling %s..." "$api"
		# Enable the API
		if gcloud services enable "$api" --project="$project" &>/dev/null; then
			((successful++))
			printf " ${GREEN}✓${NC}\n"
		else
			printf " ${RED}✗${NC}\n"
		fi
		((completed++))
		echo
	done
	# Mark final project as complete
	if [[ -n "$current_project" ]]; then
		printf "\033[2A\033[K${BLUE}Project: ${NC}%s ${GREEN}✓ Complete${NC}\n" "$current_project"
		printf "\033[1B" # Move down past the operation line
	fi
	# Final summary
	echo -e "\n${BLUE}=== Results ===${NC}"
	echo -e "${GREEN}Successfully enabled: $successful/$total_ops${NC}"
	echo -e "${BLUE}Already enabled (skipped): $skipped_count${NC}"
	local total_checked=$((total_ops + skipped_count))
	if [ $successful -eq $total_ops ]; then
		echo -e "${GREEN}All required APIs are now enabled! ($successful enabled, $skipped_count were already active)${NC}"
	else
		local failed=$((total_ops - successful))
		echo -e "${YELLOW}Failed to enable: $failed APIs${NC}"
		echo -e "${GREEN}Total APIs now active: $((successful + skipped_count))/$total_checked${NC}"
	fi
}
# Main execution
main() {
	echo -e "${BLUE}=== Google Cloud API Enabler ===${NC}"
    check_gcloud
    get_projects
    select_projects
    check_required_apis
    select_api_set
    confirm
    enable_apis
	echo -e "${GREEN}Complete!${NC}"
}
main "$@"
Troubleshooting
If you encounter issues during deployment or operation, consult the Troubleshooting guide for solutions and best practices.