django-rest-api/devops/gcp/setup-project.sh

225 lines
7.1 KiB
Bash

#!/usr/bin/env bash
#
# GCP Project Setup Script for Django on Cloud Run
#
# This script creates all the GCP resources needed to deploy a Django app:
# - GCP Project (or uses existing)
# - Cloud Run, Cloud SQL, Secret Manager, Cloud Build APIs
# - Cloud Storage bucket for media files
# - Cloud SQL database (on shared instance)
# - Secret Manager secrets for Django settings
# - IAM permissions for Cloud Run and Cloud Build
#
# Usage:
# ./setup-project.sh <project-id> [--staging]
#
# Example:
# ./setup-project.sh myproject
# ./setup-project.sh myproject-staging --staging
#
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Check arguments
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <project-id> [--staging]"
echo "Example: $0 myproject"
exit 1
fi
PROJECT_ID=$1
IS_STAGING=false
if [ "$2" == "--staging" ]; then
IS_STAGING=true
fi
# Configuration - EDIT THESE FOR YOUR ORGANIZATION
ORGANIZATION_ID="${GCP_ORGANIZATION_ID:-}" # Optional: your GCP org ID
BILLING_ACCOUNT="${GCP_BILLING_ACCOUNT:-}" # Required: your billing account
REGION="${GCP_REGION:-{{ cookiecutter.gcp_region }}}"
CLOUD_SQL_INSTANCE="${CLOUD_SQL_INSTANCE:-{{ cookiecutter.cloud_sql_instance }}}"
CLOUD_SQL_PROJECT="${CLOUD_SQL_PROJECT:-{{ cookiecutter.cloud_sql_project }}}"
# Derived values
GS_BUCKET_NAME="${PROJECT_ID}"
if [ "$IS_STAGING" = true ]; then
SECRETS_NAME="application_settings_staging"
else
SECRETS_NAME="application_settings"
fi
confirm_continue() {
read -p "$1 (y/N)? " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
}
# Validate billing account
if [ -z "$BILLING_ACCOUNT" ]; then
log_error "GCP_BILLING_ACCOUNT environment variable is required"
echo "Set it with: export GCP_BILLING_ACCOUNT=XXXXXX-XXXXXX-XXXXXX"
exit 1
fi
log_info "Setting up GCP project: $PROJECT_ID"
log_info "Region: $REGION"
log_info "Staging: $IS_STAGING"
# Create or select project
log_info "Creating/selecting project..."
if [ -n "$ORGANIZATION_ID" ]; then
gcloud projects create "$PROJECT_ID" --organization "$ORGANIZATION_ID" 2>/dev/null || \
log_warn "Project already exists or creation failed, continuing..."
else
gcloud projects create "$PROJECT_ID" 2>/dev/null || \
log_warn "Project already exists or creation failed, continuing..."
fi
# Link billing
log_info "Linking billing account..."
gcloud beta billing projects link "$PROJECT_ID" --billing-account "$BILLING_ACCOUNT" || \
log_error "Failed to link billing account"
# Enable APIs
log_info "Enabling Cloud APIs (this may take a few minutes)..."
gcloud services --project "$PROJECT_ID" enable \
run.googleapis.com \
sql-component.googleapis.com \
sqladmin.googleapis.com \
compute.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
storage.googleapis.com
# Get service account emails
PROJECTNUM=$(gcloud projects describe "$PROJECT_ID" --format 'value(projectNumber)')
CLOUDRUN_SA="${PROJECTNUM}-compute@developer.gserviceaccount.com"
CLOUDBUILD_SA="${PROJECTNUM}@cloudbuild.gserviceaccount.com"
log_info "Cloud Run SA: $CLOUDRUN_SA"
log_info "Cloud Build SA: $CLOUDBUILD_SA"
# IAM permissions for Cloud Build
log_info "Setting up IAM permissions..."
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member "serviceAccount:${CLOUDBUILD_SA}" \
--role roles/iam.serviceAccountUser --quiet
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member "serviceAccount:${CLOUDBUILD_SA}" \
--role roles/run.admin --quiet
# Cloud SQL permissions (if using shared instance)
if [ "$CLOUD_SQL_PROJECT" != "$PROJECT_ID" ]; then
log_info "Setting up Cloud SQL permissions on $CLOUD_SQL_PROJECT..."
gcloud projects add-iam-policy-binding "$CLOUD_SQL_PROJECT" \
--member "serviceAccount:${CLOUDRUN_SA}" \
--role roles/cloudsql.client --quiet
gcloud projects add-iam-policy-binding "$CLOUD_SQL_PROJECT" \
--member "serviceAccount:${CLOUDBUILD_SA}" \
--role roles/cloudsql.client --quiet
fi
# Create database
log_info "Creating database on $CLOUD_SQL_INSTANCE..."
gcloud sql databases create "$PROJECT_ID" \
--instance "$CLOUD_SQL_INSTANCE" \
--project "$CLOUD_SQL_PROJECT" 2>/dev/null || \
log_warn "Database already exists, continuing..."
# Create database user with random password
log_info "Creating database user..."
PGPASS="$(LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 30 | head -n 1)"
gcloud sql users create "$PROJECT_ID" \
--instance "$CLOUD_SQL_INSTANCE" \
--project "$CLOUD_SQL_PROJECT" \
--password "$PGPASS" 2>/dev/null || \
log_warn "User already exists, you may need to reset the password"
# Create storage bucket
log_info "Creating storage bucket: $GS_BUCKET_NAME..."
gsutil mb -l "$REGION" -p "$PROJECT_ID" "gs://${GS_BUCKET_NAME}" 2>/dev/null || \
log_warn "Bucket already exists, continuing..."
# Set CORS on bucket
log_info "Setting CORS configuration..."
cat > /tmp/cors.json << 'EOF'
[
{
"origin": ["*"],
"responseHeader": ["Content-Type"],
"method": ["GET", "HEAD"],
"maxAgeSeconds": 3600
}
]
EOF
gsutil cors set /tmp/cors.json "gs://$GS_BUCKET_NAME"
rm /tmp/cors.json
# Create secrets
log_info "Creating secrets in Secret Manager..."
SECRET_KEY="$(LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 50 | head -n 1)"
DATABASE_URL="postgres://${PROJECT_ID}:${PGPASS}@//cloudsql/${CLOUD_SQL_PROJECT}:${REGION}:${CLOUD_SQL_INSTANCE}/${PROJECT_ID}"
cat > /tmp/secrets.env << EOF
DATABASE_URL="${DATABASE_URL}"
GS_BUCKET_NAME="${GS_BUCKET_NAME}"
SECRET_KEY="${SECRET_KEY}"
DEBUG="False"
ALLOWED_HOSTS=".run.app"
CORS_ALLOWED_ORIGINS=""
EOF
gcloud secrets create "$SECRETS_NAME" \
--data-file /tmp/secrets.env \
--project "$PROJECT_ID" 2>/dev/null || \
gcloud secrets versions add "$SECRETS_NAME" \
--data-file /tmp/secrets.env \
--project "$PROJECT_ID"
rm /tmp/secrets.env
# Grant secret access
log_info "Granting secret access..."
gcloud secrets add-iam-policy-binding "$SECRETS_NAME" \
--member "serviceAccount:${CLOUDRUN_SA}" \
--role roles/secretmanager.secretAccessor \
--project "$PROJECT_ID" --quiet
gcloud secrets add-iam-policy-binding "$SECRETS_NAME" \
--member "serviceAccount:${CLOUDBUILD_SA}" \
--role roles/secretmanager.secretAccessor \
--project "$PROJECT_ID" --quiet
# Summary
echo ""
log_info "=========================================="
log_info "GCP Project Setup Complete!"
log_info "=========================================="
echo ""
echo "Project ID: $PROJECT_ID"
echo "Region: $REGION"
echo "Database: $PROJECT_ID on $CLOUD_SQL_INSTANCE"
echo "Storage Bucket: gs://$GS_BUCKET_NAME"
echo "Secrets: $SECRETS_NAME"
echo ""
echo "Next steps:"
echo " 1. Update your .env file with the project settings"
echo " 2. Build and deploy: fab deploy --env=production"
echo " 3. Run migrations: fab migrate --env=production"
echo ""
log_info "Done!"