GCP Secret Manager
Overview
All Eli Health services are migrating to Google Cloud Secret Manager for centralized secrets management. This eliminates the need for:
.envfiles on local machines- Secrets stored in GitHub Actions
- Manual secret sharing between developers
- Secrets in Terraform tfvars files
Why Secret Manager?
| Before (Old Way) | After (GCP Secret Manager) |
|---|---|
Secrets in .env files | Secrets in GCP |
Copy .env from colleague | gcloud secrets versions access |
| Secrets in GitHub Actions | Cloud Run reads from Secret Manager |
Update .env on every machine | Update once in GCP, applies everywhere |
| Risk of committing secrets | No secrets on disk |
| No audit trail | Full audit log of who accessed what |
Architecture
Service Migration Status
| Service | Status | Secrets Location | Notes |
|---|---|---|---|
| eli-kpi | ✅ Complete | GCP Secret Manager | All secrets migrated Jan 2026 |
| eli-hae | ⏳ Pending | Terraform tfvars | Needs migration |
| eli-backend | ⏳ Pending | Terraform tfvars | Needs migration |
| eli-docs | ⏳ Pending | GitHub Actions | Low priority (no sensitive secrets) |
| eli-app | ⏳ Pending | .env files | Mobile app - different pattern |
Secrets Inventory
eli-kpi Secrets (Migrated)
| Secret Name | Purpose | Used By |
|---|---|---|
anthropic-api-key | AI analysis API | KPI insights |
loopwork-api-token | Subscription data | Loopwork dashboard |
ads-data-reader-key | Ads analytics | Ads dashboard |
kpi-google-client-id | OAuth login | Google authentication |
kpi-google-client-secret | OAuth login | Google authentication |
kpi-session-secret | Cookie encryption | Session management |
Common Secrets (To Be Migrated)
| Secret Name | Used By | Current Location |
|---|---|---|
ANTHROPIC_API_KEY | HAE, Backend | Terraform |
SENDGRID_API_KEY | Backend | Terraform |
STRIPE_API_KEY | Backend | Terraform |
FIREBASE_* | Multiple | Terraform/GitHub |
How It Works
For Cloud Run Services
In the GitHub Actions workflow, secrets are mounted at deploy time:
gcloud run deploy $SERVICE_NAME \
--update-secrets=ANTHROPIC_API_KEY=anthropic-api-key:latest \
--update-secrets=LOOPWORK_API_TOKEN=loopwork-api-token:latest
Cloud Run automatically:
- Fetches the secret value from Secret Manager
- Injects it as an environment variable
- Refreshes on each new deployment
For Local Development
Instead of maintaining .env files, developers fetch secrets directly:
# One-time setup: authenticate with GCP
gcloud auth login
gcloud auth application-default login
# Fetch secrets to a local file
./scripts/fetch-secrets.sh > .env.local
source .env.local
# Or use inline for a single command
ANTHROPIC_API_KEY=$(gcloud secrets versions access latest --secret=anthropic-api-key --project=eli-health-prod) npm run dev
Example fetch-secrets.sh script:
#!/bin/bash
PROJECT="eli-health-prod"
echo "# Secrets from GCP Secret Manager"
echo "# Generated: $(date)"
echo ""
echo "ANTHROPIC_API_KEY=$(gcloud secrets versions access latest --secret=anthropic-api-key --project=$PROJECT)"
echo "LOOPWORK_API_TOKEN=$(gcloud secrets versions access latest --secret=loopwork-api-token --project=$PROJECT)"
Creating New Secrets
1. Create the Secret
# Create a new secret
echo -n "your-secret-value" | gcloud secrets create my-new-secret \
--project=eli-health-prod \
--data-file=-
# Or create empty and add version later
gcloud secrets create my-new-secret --project=eli-health-prod
echo -n "your-secret-value" | gcloud secrets versions add my-new-secret \
--project=eli-health-prod \
--data-file=-
2. Grant Access to Service Account
# Find the service account for your Cloud Run service
# Format: SERVICE-NAME@PROJECT.iam.gserviceaccount.com
gcloud secrets add-iam-policy-binding my-new-secret \
--project=eli-health-prod \
--member="serviceAccount:kpi-service-us@eli-health-prod.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
3. Use in Cloud Run Deploy
Add to your deploy workflow:
--update-secrets=MY_ENV_VAR=my-new-secret:latest
Rotating Secrets
# Add a new version (becomes "latest" automatically)
echo -n "new-secret-value" | gcloud secrets versions add my-secret \
--project=eli-health-prod \
--data-file=-
# Force Cloud Run to pick up new version
gcloud run services update SERVICE_NAME \
--region=us-east1 \
--project=eli-health-prod \
--update-secrets=MY_ENV_VAR=my-secret:latest
Viewing Secrets
# List all secrets
gcloud secrets list --project=eli-health-prod
# List versions of a secret
gcloud secrets versions list my-secret --project=eli-health-prod
# Access a specific version
gcloud secrets versions access latest --secret=my-secret --project=eli-health-prod
gcloud secrets versions access 2 --secret=my-secret --project=eli-health-prod
Access Control
Secrets are protected by IAM. Only these principals can access secrets:
| Principal | Access Level |
|---|---|
Service accounts (e.g., kpi-service-us@...) | Individual secrets they need |
| Admin users (chip@eli.health, etc.) | All secrets |
| GitHub Actions (via Workload Identity) | Deploy-time access only |
Developers access secrets through their Google account (gcloud auth login).
Migration Guide
To migrate a service from .env/Terraform to Secret Manager:
Step 1: Identify Current Secrets
# Check what environment variables the service uses
grep -r "process.env" src/ | grep -oP 'process\.env\.\K[A-Z_]+' | sort | uniq
Step 2: Create Secrets in GCP
# For each secret, create in Secret Manager
echo -n "$CURRENT_VALUE" | gcloud secrets create secret-name \
--project=eli-health-prod \
--data-file=-
Step 3: Grant Service Account Access
# Grant access for each secret
gcloud secrets add-iam-policy-binding secret-name \
--project=eli-health-prod \
--member="serviceAccount:SERVICE@eli-health-prod.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
Step 4: Update Deploy Workflow
Replace --set-env-vars with --update-secrets:
# Before
--set-env-vars="API_KEY=${{ secrets.API_KEY }}"
# After
--update-secrets=API_KEY=api-key-secret:latest
Step 5: Remove Old Secrets
- Remove from GitHub Actions secrets
- Remove from Terraform tfvars
- Update
.env.exampleto reference Secret Manager
Troubleshooting
"Permission denied" when accessing secret
# Check if you're logged in
gcloud auth list
# Re-authenticate if needed
gcloud auth login
gcloud auth application-default login
Cloud Run fails to start with secret error
# Check service account has access
gcloud secrets get-iam-policy my-secret --project=eli-health-prod
# Grant access if missing
gcloud secrets add-iam-policy-binding my-secret \
--project=eli-health-prod \
--member="serviceAccount:YOUR-SERVICE-ACCOUNT" \
--role="roles/secretmanager.secretAccessor"
Secret shows "0 versions"
The secret was created but no value was added:
echo -n "your-value" | gcloud secrets versions add my-secret \
--project=eli-health-prod \
--data-file=-
Best Practices
- Never hardcode secrets in code - always use environment variables
- Use descriptive names -
kpi-google-client-idnotgoogle-id - Document in this page when adding new secrets
- Rotate regularly - at least quarterly for sensitive secrets
- Use separate secrets per environment - don't share prod/dev secrets
- Add
.env*to.gitignore- prevent accidental commits
Related Documentation
- Internal Security Systems Overview - Full security documentation
- Terraform Guide - Infrastructure as Code
- BigQuery Access - Database access control