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:
- Stores Terraform state — One S3 bucket per client (
campuscore-tfstate-{client}) holds all infrastructure state files. - 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=falseon updates if the OIDC provider already exists (it was created on first deploy). Setting it totruewhen it already exists will cause a conflict. TheGitHubOrgandGitHubRepovalues 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.comhosted 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
AssumeRolepermission targetsCampusCore-Deploy-Roleby 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.shlocally.
See also: Repo & Pipeline Setup