02 — Pipeline Setup¶
Scope: Per-client. Create the Terraform state bucket, GitHub Environment, deploy branch, and populate all secrets and variables.
Time: ~15 minutes
Two script sections do this whole page.
./scripts/onboard-client.sh pipeline <env> --client <name> --account-id <id> --ssl-mode <mode> --region <region> --security-services <true|false> --per-az-nat <true|false> covers 2a, 2b, and 2d - the two switch flags are required so the security-posture and NAT-resilience decisions are always recorded explicitly (production clients: --security-services true).
./scripts/onboard-client.sh secrets <env> covers 2c and 2e; it is interactive, so run it in your own terminal.
The sections below describe what happens and are the manual fallback.
Prerequisites¶
- All API keys from Step 01
- SSL mode decided with the client:
managed(recommended),self_managed, oroff- see Step 01
2a. Create TF State Bucket (Our Admin Account)¶
Time: ~5 minutes
Create an S3 bucket in our admin AWS account (not the client's account) for the client's Terraform state:
# Authenticate to the ADMIN account
export AWS_PROFILE=campuscore-admin
# Create the state bucket
./scripts/create-new-client-tf-state-bucket.sh howard
This creates campuscore-tfstate-howard with:
- Versioning enabled
- Server-side encryption (AES256)
- Public access blocked
One bucket per client, not per environment. If a client has multiple environments (e.g.,
howard-testandhoward-prod), they all share the same state bucket. Terraform isolates environments using separate state key paths (tfstate/howard-test,tfstate/howard-prod) and workspaces within the bucket.
2b. Create the GitHub Environment¶
- Go to the CampusCore GitHub repo > Settings > Environments
- Click New environment
- Name it to match the client identifier (e.g.,
howard)
Or from the CLI - for creation only. On an existing environment this PUT can silently reset protection settings such as reviewers, which is why the onboarding script probes for the environment first and only creates when it is absent.
Multi-Environment Clients¶
If a client needs multiple environments (e.g., test and prod), create a separate GitHub Environment for each:
howard-testhoward-prod
Each environment gets its own set of secrets and variables. Most values are identical across environments (API keys, TF_STATE_BUCKET), but some will differ (e.g., DB_PASSWORD, SSL_MODE=off on a throwaway test environment, or RDS_INSTANCE_CLASS=db.t3.medium to keep a test environment's database small).
2c. Generate Deployment Secrets¶
./scripts/onboard-client.sh secrets howard # single environment
./scripts/onboard-client.sh secrets howard-prod # multi-environment
The secrets section:
1. Auto-generates DB_USERNAME, DB_PASSWORD, DJANGO_SECRET_KEY, and APP_FERNET_KEY, uploading them straight to the GitHub Environment without ever displaying a value
2. Prompts silently for the client-provided API keys (blank skips one; rerun the section when you have it)
3. Always skips secrets that are already set - rotation is a deliberate manual act, because rotating DB_USERNAME would replace the RDS instance and rotating APP_FERNET_KEY would orphan every encrypted credential
2d. Set Environment Variables¶
Note:
AWS_ROLE_ARNis derivable from the client's account ID (arn:aws:iam::<ACCOUNT_ID>:role/CampusCore-Deploy-Role- the role name is hardcoded in the template), so the script'spipelinesection sets it here already. The role itself must still be deployed in Step 03 before anything can deploy.
Single Environment¶
# Terraform state bucket
gh variable set TF_STATE_BUCKET --env howard --body "campuscore-tfstate-howard"
# AWS region (optional, defaults to us-east-1)
gh variable set AWS_REGION --env howard --body "us-east-1"
# SSL mode - `managed` provisions {env}.campuscoreai.com + ACM certificate + HTTPS
gh variable set SSL_MODE --env howard --body "managed"
# Security posture bundle - true for production clients (Step 01 internal decision)
gh variable set ENABLE_FULL_AWS_SECURITY_SERVICES --env howard --body "true"
# Per-AZ NAT gateways - optional egress resilience, ~$37/month per extra AZ
gh variable set ENABLE_PER_AZ_NAT --env howard --body "false"
Multiple Environments¶
Set the same variables on each GitHub Environment:
# --- howard-test ---
gh variable set TF_STATE_BUCKET --env howard-test --body "campuscore-tfstate-howard"
gh variable set AWS_REGION --env howard-test --body "us-east-1"
gh variable set SSL_MODE --env howard-test --body "managed"
gh variable set ENABLE_FULL_AWS_SECURITY_SERVICES --env howard-test --body "false"
gh variable set ENABLE_PER_AZ_NAT --env howard-test --body "false"
# --- howard-prod ---
gh variable set TF_STATE_BUCKET --env howard-prod --body "campuscore-tfstate-howard"
gh variable set AWS_REGION --env howard-prod --body "us-east-1"
gh variable set SSL_MODE --env howard-prod --body "managed"
gh variable set ENABLE_FULL_AWS_SECURITY_SERVICES --env howard-prod --body "true"
gh variable set ENABLE_PER_AZ_NAT --env howard-prod --body "false"
2e. Set Environment Secrets¶
The secrets section (2c) sets all of these. Manual fallback:
Generated by Us¶
gh secret set DB_USERNAME --env howard --body "campuscore_admin"
gh secret set DB_PASSWORD --env howard --body "<generated>"
gh secret set DJANGO_SECRET_KEY --env howard --body "<generated>"
gh secret set APP_FERNET_KEY --env howard --body "<generated>"
Provided by Client¶
gh secret set OPENAI_API_KEY --env howard --body "<from-client>"
gh secret set GEMINI_API_KEY --env howard --body "<from-client>"
gh secret set COHERE_API_KEY --env howard --body "<from-client>"
2f. SSL Mode and Domains¶
SSL_MODE is the HTTPS capability gate, read by the deploy workflow with a default of off:
| Mode | What the pipeline provisions |
|---|---|
managed (recommended) |
{env}.campuscoreai.com subdomain + ACM certificate + HTTPS on the ALB, plus the in-app machinery that issues and attaches certificates for the client's own domains |
self_managed |
The subdomain, certificate, and HTTPS listener; the client terminates TLS for their own domain themselves (e.g. Cloudflare) |
off |
Nothing - no subdomain, no certificate, HTTP only. For throwaway test environments |
The environment name is sanitized for the subdomain (lowercased, underscores become dashes), so environment howard_test serves howard-test.campuscoreai.com.
The client's own domain (e.g. ai.university.edu) is not a pipeline variable: after deployment the admin adds it in Settings > Custom Domain, which walks them through the DNS records - see Custom Domain Setup.
The CUSTOM_DOMAIN_WITH_PROTOCOL variable only takes effect when SSL_MODE=off; in every other mode the pipeline substitutes the subdomain URL itself.
Note:
CAMPUSCORE_HOSTED_ZONE_IDis a repo-level variable (already configured - see Repo & Pipeline Setup), not per-environment.
2g. Deploy Branch¶
Deployments are triggered by pushing to a deploy/{client} branch. The branch name determines which GitHub Environment the pipeline uses.
# Single environment — branch name matches the GitHub Environment name
# deploy/howard → uses the "howard" GitHub Environment
git push origin main:deploy/howard
# Multi-environment
# deploy/howard-test → uses "howard-test" GitHub Environment
# deploy/howard-prod → uses "howard-prod" GitHub Environment
You don't need to create these branches ahead of time — the first git push creates them. But be aware of the naming convention: the branch suffix after deploy/ must match the GitHub Environment name exactly.
2h. Verify¶
# List environment variables
gh variable list --env howard
# List environment secrets (names only — values are write-only)
gh secret list --env howard
Confirm all variables and secrets are set except AWS_ROLE_ARN (set in the next step).
See GitHub Environment Variables Reference for the complete specification.
Next: 03 — Deployment