Skip to main content

Prerequisites

Overview

To successfully integrate your Google Cloud environment with Upwind, ensure the following prerequisites are met:

  1. 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.
  2. 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)
  3. gcloud CLI Setup. Ensure gcloud is installed and authenticated with your Google Cloud environment. Ensure gcloud is updated to the latest version.

    gcloud auth login
    gcloud auth application-default login
  4. Terraform Installed. All Upwind related infrastructure will be deployed via Terraform. The minimum required version of Terraform is 1.11.0.

MacOS

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.

  1. 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.

    TitleName
    Cloud Asset APIcloudasset.googleapis.com
    Cloud Resource Manager APIcloudresourcemanager.googleapis.com
    Compute Engine APIcompute.googleapis.com
    IAM Service Account Credentials APIiamcredentials.googleapis.com
    Identity and Access Management APIiam.googleapis.com
    Kubernetes Engine APIcontainer.googleapis.com
    Secret Manager APIsecretmanager.googleapis.com
    Security Token Service APIsts.googleapis.com
    Service Usage APIserviceusage.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.

    TitleName
    Access Approval APIaccessapproval.googleapis.com
    Admin SDK Directory APIadmin.googleapis.com
    AlloyDB Admin APIalloydb.googleapis.com
    API Keys APIapikeys.googleapis.com
    BigQuery APIbigquery.googleapis.com
    Cloud Dataproc APIdataproc.googleapis.com
    Cloud DNS APIdns.googleapis.com
    Cloud Functions APIcloudfunctions.googleapis.com
    Cloud Key Management Service APIcloudkms.googleapis.com
    Cloud Logging APIlogging.googleapis.com
    Cloud Monitoring APImonitoring.googleapis.com
    Cloud Redis APIredis.googleapis.com
    Cloud SQL Admin APIsqladmin.googleapis.com
    Cloud Storage APIstorage.googleapis.com
    Cloud Storage Insights APIstorageinsights.googleapis.com
    Essential Contacts APIessentialcontacts.googleapis.com

    Additional APIs for Cloud Scanners


    These APIs are required for Cloud Scanner deployments, which use both Cloud Run and Cloud Scheduler.

    TitleName
    Cloud Run Admin APIrun.googleapis.com
    Cloud Scheduler APIcloudscheduler.googleapis.com
Enable Google Cloud APIs Automatically

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.