Skip to content

Commit 4cb9f97

Browse files
committed
Add setup-gcp.sh to provision WIF + service account in one step
1 parent 8a2b5e0 commit 4cb9f97

2 files changed

Lines changed: 141 additions & 47 deletions

File tree

README.md

Lines changed: 21 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ A minimal FastAPI "Hello, World!" service, managed with [uv](https://docs.astral
2121
├── .python-version # pins Python 3.13 for uv
2222
├── Dockerfile # uv-based container build
2323
├── .dockerignore
24+
├── setup-gcp.sh # one-time GCP setup (WIF + service account)
2425
└── .github/workflows/
2526
└── deploy.yml # GitHub Actions → Cloud Run
2627
```
@@ -78,55 +79,28 @@ A push to the `deploy-github` branch triggers [.github/workflows/deploy.yml](.gi
7879

7980
### One-time GCP setup
8081

81-
You only need to do this once per project. Replace `PROJECT_ID` and `GITHUB_REPO` (e.g. `your-org/your-repo`) with your values.
82+
Run [setup-gcp.sh](setup-gcp.sh) once per project. It enables APIs, creates the deployer service account, sets up a Workload Identity Pool + GitHub OIDC provider scoped to your repo, and prints the two secrets you need.
83+
84+
Requires `gcloud` installed and authenticated (`gcloud auth login`).
8285

8386
```bash
84-
PROJECT_ID="fullstackpro-python"
85-
GITHUB_REPO="OWNER/REPO"
86-
SA_NAME="github-deployer"
87-
POOL="github-pool"
88-
PROVIDER="github-provider"
89-
90-
gcloud config set project "$PROJECT_ID"
91-
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)")
92-
93-
# 1. Enable APIs
94-
gcloud services enable \
95-
run.googleapis.com \
96-
cloudbuild.googleapis.com \
97-
artifactregistry.googleapis.com \
98-
iamcredentials.googleapis.com
99-
100-
# 2. Create deployer service account
101-
gcloud iam service-accounts create "$SA_NAME" \
102-
--display-name="GitHub Actions Cloud Run deployer"
103-
SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
104-
105-
# 3. Grant it the roles it needs
106-
for role in roles/run.admin roles/iam.serviceAccountUser roles/cloudbuild.builds.editor roles/artifactregistry.writer roles/storage.admin; do
107-
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
108-
--member="serviceAccount:${SA_EMAIL}" --role="$role"
109-
done
110-
111-
# 4. Create a Workload Identity Pool + GitHub OIDC provider
112-
gcloud iam workload-identity-pools create "$POOL" \
113-
--location=global --display-name="GitHub Pool"
114-
115-
gcloud iam workload-identity-pools providers create-oidc "$PROVIDER" \
116-
--location=global --workload-identity-pool="$POOL" \
117-
--display-name="GitHub OIDC" \
118-
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
119-
--attribute-condition="assertion.repository=='${GITHUB_REPO}'" \
120-
--issuer-uri="https://token.actions.githubusercontent.com"
121-
122-
# 5. Let the GitHub repo impersonate the service account
123-
gcloud iam service-accounts add-iam-policy-binding "$SA_EMAIL" \
124-
--role=roles/iam.workloadIdentityUser \
125-
--member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/attribute.repository/${GITHUB_REPO}"
126-
127-
# 6. Print the values to put in GitHub secrets
128-
echo "GCP_WIF_PROVIDER = projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/providers/${PROVIDER}"
129-
echo "GCP_SERVICE_ACCOUNT = ${SA_EMAIL}"
87+
# Required: your GitHub repo in owner/repo form
88+
GITHUB_REPO=OWNER/REPO ./setup-gcp.sh
89+
90+
# Or override the defaults too:
91+
GITHUB_REPO=OWNER/REPO \
92+
PROJECT_ID=fullstackpro-python \
93+
SA_NAME=github-deployer \
94+
POOL=github-pool \
95+
PROVIDER=github-provider \
96+
./setup-gcp.sh
97+
```
98+
99+
The script is idempotent — safe to re-run if you need to re-bind a new repo or re-print the secret values. At the end it prints:
100+
101+
```
102+
GCP_WIF_PROVIDER = projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider
103+
GCP_SERVICE_ACCOUNT = github-deployer@PROJECT_ID.iam.gserviceaccount.com
130104
```
131105

132106
### Add GitHub repo secrets

setup-gcp.sh

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env bash
2+
#
3+
# One-time GCP setup for GitHub Actions → Cloud Run deploys via
4+
# Workload Identity Federation (keyless auth).
5+
#
6+
# Re-running is safe — every step is idempotent.
7+
#
8+
# Required:
9+
# GITHUB_REPO GitHub repo in OWNER/REPO form, e.g. mominali/fastapi_basic
10+
#
11+
# Optional (with defaults):
12+
# PROJECT_ID GCP project (default: fullstackpro-python)
13+
# SA_NAME Service account name (default: github-deployer)
14+
# POOL Workload Identity Pool ID (default: github-pool)
15+
# PROVIDER OIDC provider ID (default: github-provider)
16+
#
17+
# Usage:
18+
# GITHUB_REPO=mominali/fastapi_basic ./setup-gcp.sh
19+
20+
set -euo pipefail
21+
22+
PROJECT_ID="${PROJECT_ID:-fullstackpro-python}"
23+
SA_NAME="${SA_NAME:-github-deployer}"
24+
POOL="${POOL:-github-pool}"
25+
PROVIDER="${PROVIDER:-github-provider}"
26+
27+
if [[ -z "${GITHUB_REPO:-}" ]]; then
28+
echo "ERROR: GITHUB_REPO must be set (e.g. GITHUB_REPO=owner/repo $0)" >&2
29+
exit 1
30+
fi
31+
32+
echo ">>> Project: ${PROJECT_ID}"
33+
echo ">>> GitHub repo: ${GITHUB_REPO}"
34+
echo ">>> Service acct: ${SA_NAME}"
35+
echo ">>> WIF pool: ${POOL}"
36+
echo ">>> OIDC provider: ${PROVIDER}"
37+
echo
38+
39+
gcloud config set project "${PROJECT_ID}" >/dev/null
40+
PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")
41+
SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
42+
43+
echo ">>> [1/5] Enabling required APIs..."
44+
gcloud services enable \
45+
run.googleapis.com \
46+
cloudbuild.googleapis.com \
47+
artifactregistry.googleapis.com \
48+
iamcredentials.googleapis.com \
49+
iam.googleapis.com \
50+
sts.googleapis.com
51+
52+
echo ">>> [2/5] Ensuring service account exists..."
53+
if ! gcloud iam service-accounts describe "${SA_EMAIL}" >/dev/null 2>&1; then
54+
gcloud iam service-accounts create "${SA_NAME}" \
55+
--display-name="GitHub Actions Cloud Run deployer"
56+
else
57+
echo " already exists: ${SA_EMAIL}"
58+
fi
59+
60+
echo ">>> [3/5] Granting roles to ${SA_EMAIL}..."
61+
for role in \
62+
roles/run.admin \
63+
roles/iam.serviceAccountUser \
64+
roles/cloudbuild.builds.editor \
65+
roles/artifactregistry.writer \
66+
roles/storage.admin
67+
do
68+
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
69+
--member="serviceAccount:${SA_EMAIL}" \
70+
--role="${role}" \
71+
--condition=None \
72+
--quiet >/dev/null
73+
echo " + ${role}"
74+
done
75+
76+
echo ">>> [4/5] Ensuring Workload Identity Pool + OIDC provider..."
77+
if ! gcloud iam workload-identity-pools describe "${POOL}" \
78+
--location=global >/dev/null 2>&1; then
79+
gcloud iam workload-identity-pools create "${POOL}" \
80+
--location=global \
81+
--display-name="GitHub Pool"
82+
else
83+
echo " pool already exists: ${POOL}"
84+
fi
85+
86+
if ! gcloud iam workload-identity-pools providers describe "${PROVIDER}" \
87+
--location=global --workload-identity-pool="${POOL}" >/dev/null 2>&1; then
88+
gcloud iam workload-identity-pools providers create-oidc "${PROVIDER}" \
89+
--location=global \
90+
--workload-identity-pool="${POOL}" \
91+
--display-name="GitHub OIDC" \
92+
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
93+
--attribute-condition="assertion.repository=='${GITHUB_REPO}'" \
94+
--issuer-uri="https://token.actions.githubusercontent.com"
95+
else
96+
echo " provider already exists: ${PROVIDER}"
97+
fi
98+
99+
echo ">>> [5/5] Binding GitHub repo to service account..."
100+
PRINCIPAL="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/attribute.repository/${GITHUB_REPO}"
101+
gcloud iam service-accounts add-iam-policy-binding "${SA_EMAIL}" \
102+
--role=roles/iam.workloadIdentityUser \
103+
--member="${PRINCIPAL}" \
104+
--quiet >/dev/null
105+
echo " + ${PRINCIPAL}"
106+
107+
WIF_PROVIDER="projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL}/providers/${PROVIDER}"
108+
109+
cat <<EOF
110+
111+
✓ Setup complete.
112+
113+
Add these two secrets to your GitHub repo
114+
(Settings → Secrets and variables → Actions → New repository secret):
115+
116+
GCP_WIF_PROVIDER = ${WIF_PROVIDER}
117+
GCP_SERVICE_ACCOUNT = ${SA_EMAIL}
118+
119+
Then push to the deploy-github branch to trigger a Cloud Run deploy.
120+
EOF

0 commit comments

Comments
 (0)