Skip to content

Admin Account Setup

Already completed. This was done during initial CampusCore setup. You only need this if you're migrating to a new admin AWS account or re-creating the OIDC trust from scratch.

How the CampusCore admin AWS account is set up — the central account that holds Terraform state and authenticates GitHub Actions deployments.


What the Admin Account Does

The admin account is a lightweight coordination layer. It does NOT host any client infrastructure. It has two jobs:

  1. Stores Terraform state — One S3 bucket per client (campuscore-tfstate-{client}) holds all infrastructure state files.
  2. OIDC trust anchor — GitHub Actions authenticates to this account first via OIDC, then chains into each client's deploy role.
GitHub Actions  ──OIDC──>  Admin Account  ──AssumeRole──>  Client Account
                           (state bucket)                  (ECS, RDS, etc.)

Permissions Summary

The admin role (CampusCore-Admin-Role) needs only these permissions:

Category Actions Scope
S3 — Create buckets CreateBucket, PutBucketVersioning, PutBucketEncryption, PutPublicAccessBlock, GetBucketLocation arn:aws:s3:::campuscore-tfstate-*
S3 — Read/write state GetObject, PutObject, DeleteObject arn:aws:s3:::campuscore-tfstate-*/tfstate/*
S3 — List/check buckets HeadBucket, ListBucket arn:aws:s3:::campuscore-tfstate-*
STS — Role chaining AssumeRole arn:aws:iam::*:role/CampusCore-Deploy-Role
STS — Identity check GetCallerIdentity *
Route53 — DNS management ChangeResourceRecordSets, GetHostedZone, ListResourceRecordSets arn:aws:route53:::hostedzone/*
Route53 — Change tracking GetChange arn:aws:route53:::change/*
Route53 — List zones ListHostedZones *

Route53 permissions are used to manage the campuscoreai.com hosted zone — creating subdomain CNAMEs and ACM certificate validation records for each deployment. No EC2, RDS, ECS, or other compute permissions — all infrastructure lives in client accounts.


Deploy the Admin Role CloudFormation Stack

Log in to the admin AWS account and deploy the template:

aws cloudformation deploy \
  --template-file infrastructure/deploy-roles/admin-oidc-role.yaml \
  --stack-name CampusCore-Admin-Role \
  --parameter-overrides \
    GitHubOrg=CampusCoreAI \
    GitHubRepo=campuscore \
    CreateOIDCProvider=true \
  --capabilities CAPABILITY_NAMED_IAM \
  --no-fail-on-empty-changeset \
  --profile campuscore-admin

Parameters:

Parameter Description Example
GitHubOrg GitHub org or username campuscore
GitHubRepo Repository name campuscore
CreateOIDCProvider Create the GitHub OIDC provider (set false if it already exists) true
StateBucketPrefix S3 bucket prefix for state buckets (default: campuscore-tfstate-) campuscore-tfstate-

Get the Admin Role ARN

aws cloudformation describe-stacks \
  --stack-name CampusCore-Admin-Role \
  --query 'Stacks[0].Outputs[?OutputKey==`AdminRoleArn`].OutputValue' \
  --output text \
  --profile campuscore-admin

Example output: arn:aws:iam::111111111111:role/CampusCore-Admin-Role

Save this ARN — it's used in the Repo & Pipeline Setup and when onboarding each client in Step 03 — Deployment.


Updating the Stack

To update permissions or parameters:

aws cloudformation deploy \
  --template-file infrastructure/deploy-roles/admin-oidc-role.yaml \
  --stack-name CampusCore-Admin-Role \
  --parameter-overrides \
    GitHubOrg=CampusCoreAI \
    GitHubRepo=campuscore \
    CreateOIDCProvider=false \
  --capabilities CAPABILITY_NAMED_IAM \
  --no-fail-on-empty-changeset \
  --profile campuscore-admin

Important: Set CreateOIDCProvider=false on updates if the OIDC provider already exists (it was created on first deploy). Setting it to true when it already exists will cause a conflict. The GitHubOrg and GitHubRepo values are case-sensitive and must match the GitHub repository exactly.


Security Notes

  • The admin role follows least privilege — it can manage S3 state buckets, assume client deploy roles, and manage Route53 records in the campuscoreai.com hosted zone. No compute, database, or networking permissions.
  • S3 bucket access is scoped to the campuscore-tfstate-* prefix only.
  • State file access is further scoped to the tfstate/* key prefix inside those buckets.
  • Route53 access is used only for subdomain provisioning ({env}.campuscoreai.com) and ACM certificate validation records.
  • The AssumeRole permission targets CampusCore-Deploy-Role by name. Each client's deploy role independently controls who can assume it via its trust policy.
  • The OIDC trust is scoped to your specific GitHub org/repo — no other repos can assume this role.
  • The admin role has no long-lived credentials. GitHub Actions authenticates via OIDC (short-lived tokens). The only time you use static credentials is when running create-new-client-tf-state-bucket.sh locally.

See also: Repo & Pipeline Setup