AWS Account Setup & Access Strategy
This document describes the SPW Healthcare Innovations platform AWS account (spwhi-platform).
Ward Mitra is the first tenant. Future apps (Field Assist, etc.) will onboard to the same EKS cluster
under separate namespaces. All conventions here apply to the entire SPWHI platform — not Ward Mitra alone.
Account Structure
AWS Organizations (SPW Healthcare Innovations)
├── Existing account (POC) ← keep as-is, manual changes allowed
│ └── Ward Mitra POC deployment
└── spwhi-platform (NEW) ← this document
└── IaC-only · Terraform is the only path to change anything
Why a separate account?
| Concern | Reason |
|---|---|
| Clean blast radius | A misconfigured change in the POC cannot affect production |
| IaC discipline | Enforced by SCPs at org level — not by trust or convention |
| Audit clarity | Every change in spwhi-platform is either a Terraform apply or a BreakGlass emergency — nothing in between |
| Multi-tenancy | All SPWHI apps share this account's EKS cluster under namespace isolation |
| Cost visibility | AWS Cost Explorer shows platform vs POC spend clearly |
IaC-Only Discipline
Terraform is the only way to create, modify, or delete any resource in
spwhi-platform.
This is not a convention. It is enforced by Service Control Policies (SCPs) applied at the AWS Organizations level. SCPs override even AdministratorAccess — no IAM permission can bypass them.
What SCPs enforce
| SCP Deny | Why |
|---|---|
iam:CreateUser | No new IAM users ever — only the BreakGlass user exists (created at bootstrap) |
iam:CreateAccessKey | No static long-lived credentials anywhere in the account |
s3:DeleteBucket on spwhi-platform-tfstate | Terraform state is permanent and protected |
s3:DeleteObject on spwhi-platform-tfstate/* | No corruption of state, even by admins |
cloudtrail:StopLogging | Audit trail is always on — cannot be disabled |
cloudtrail:DeleteTrail | Even BreakGlass cannot hide their actions |
* where aws:RequestedRegion != ap-south-1 | All resources stay within India (except global services: IAM, STS, CloudFront, Route 53) |
If you need to create a resource manually, you are doing something wrong. Either fix the Terraform pipeline or follow the BreakGlass procedure. The SCPs will block any attempt to bypass this.
User Types & Access Model
Three types of access exist in spwhi-platform. No exceptions.
spwhi-platform account
│
├── 🤖 Terraform (machine, OIDC) ← GitHub Actions only
├── 🔴 BreakGlass (human, emergency) ← max 2 people, MFA required
└── 👁 ReadOnly Dev (human, SSO) ← all developers, read-only console
🤖 Terraform — Machine Access (OIDC)
Not a human. Not an IAM user. No credentials stored anywhere.
Terraform runs exclusively through GitHub Actions using AWS OIDC federation. The workflow exchanges a short-lived GitHub JWT for temporary AWS STS credentials (1-hour TTL). No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY exists in GitHub Secrets.
How it works
GitHub Actions job starts
↓
GitHub mints a signed JWT for this workflow run
(contains: repo, branch, workflow, sha)
↓
Workflow calls AWS STS AssumeRoleWithWebIdentity
(JWT + IAM role ARN)
↓
AWS validates JWT signature via GitHub OIDC JWKS endpoint
Checks trust policy: sub must match exactly
↓
STS returns temp credentials — valid 1 hour, then auto-expire
↓
Terraform plan / apply runs with these credentials
IAM Role: github-actions-terraform
| Property | Value |
|---|---|
| Type | IAM Role (OIDC-assumed, no IAM user) |
| Repo trust | SPW-HEALTHCARE-INNOVATIONS-Pvt-Ltd/spwhi-infra only |
| Branch trust | refs/heads/main only (apply) |
| Console access | None |
| Access keys | None |
| Session TTL | 1 hour (STS temp creds) |
| Permissions | S3 (tfstate), DynamoDB (lock), VPC, EKS, RDS, IAM, ACM, Route 53 — scoped to spwhi-* resources |
GitHub Actions usage
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::ACCOUNT_ID:role/github-actions-terraform
aws-region: ap-south-1
role-session-name: terraform-${{ github.run_id }}
The id-token: write permission must be set on the job for OIDC token minting to work.
Other pipeline roles (same OIDC pattern)
| Role | Trusted repo | Trusted branch | Permissions |
|---|---|---|---|
github-actions-ecr-push | SPW-HEALTHCARE-INNOVATIONS-Pvt-Ltd/wardmitra-api | any (*) | ECR push for WardMitra API Docker images |
github-actions-s3-deploy | SPW-HEALTHCARE-INNOVATIONS-Pvt-Ltd/wardmitra-ui | main only | S3 sync + CloudFront invalidation for WardMitra UI |
All three roles are provisioned by the spwhi-bootstrap Terraform repo (Day 0, one-time).
🔴 BreakGlass — Emergency Human Admin
For production emergencies only when the Terraform pipeline cannot be used.
Who has access
| Person | Role |
|---|---|
| SparkOps Lead (Sanket Pethkar) | Primary BreakGlass holder |
| SPW CTO / Senior Dev | Secondary BreakGlass holder |
Maximum 2 people. No exceptions.
Account properties
| Property | Value |
|---|---|
| IAM username | breakglass |
| Policy | AdministratorAccess (AWS managed) |
| MFA | Required — no console action possible without MFA token |
| Access keys | None (blocked by SCP iam:CreateAccessKey) |
| Console access | Yes — AWS Management Console only |
| Credential storage | Secure vault (1Password / LastPass restricted vault — shared only between 2 holders) |
Alerting
Every BreakGlass login triggers an immediate alert:
CloudTrail → EventBridge rule (userIdentity.userName = "breakglass")
→ SNS topic
→ Email: devops@spwhealthcare.in
→ Slack: #spwhi-platform-alerts
No BreakGlass login should be silent. If the team doesn't see an alert, something is wrong.
When to use BreakGlass
✅ Allowed:
- EKS node group crashed and pods cannot be rescheduled
- RDS instance unavailable and automated failover did not trigger
- Terraform pipeline is broken and production is actively degraded
- Security incident requiring immediate IAM revocation
❌ Not allowed:
- Convenience (pipeline too slow)
- Skipping code review
- Testing something before adding to Terraform
- Any non-emergency situation
Post-use mandatory steps
Every BreakGlass session must be fully reconciled with Terraform before the incident is closed.
- Document every console action taken in
#spwhi-platform-alertsSlack thread - For each resource modified via console:
- If Terraform already manages it → run
terraform importto reconcile state - If new resource created → write Terraform code +
terraform import→ verifyterraform planshows no drift
- If Terraform already manages it → run
- Verify clean state:
terraform planmust outputNo changesbefore incident closure - Rotate BreakGlass console password after every use
- Write incident report: what failed, what was changed manually, how it was reconciled
👁 ReadOnly Developer — Console Visibility
For all SPW developers and SparkOps team to observe what Terraform deployed.
No separate AWS password. Login via IAM Identity Center (SSO) using existing Google Workspace account.
How to log in
- Go to:
https://spwhi.awsapps.com/start(SSO portal) - Log in with your
@spwhealthcare.inGoogle account - Select
spwhi-platformaccount - Click Management Console — read-only session starts (8hr TTL)
What you can see
| Service | Access |
|---|---|
| EKS | View clusters, node groups, namespaces, workloads |
| CloudWatch | View logs, metrics, alarms, dashboards |
| RDS | View instance parameters, snapshots, event logs |
| S3 | List buckets, view object metadata (not download sensitive data) |
| EC2 | View instances, security groups, VPC layout |
| IAM | View roles and policies (read-only) |
| SSM Parameter Store | View parameter names and metadata (not SecureString values) |
What you cannot do
- Create, modify, or delete any resource
- Download objects from
spwhi-platform-tfstateorspwhi-platform-velero-backup(permission boundary blocks this) - View SecureString parameter values (SSM) or Secrets Manager secret values
- Access the BreakGlass IAM user or any OIDC roles
Permission set details
| Property | Value |
|---|---|
| IAM Identity Center Permission Set | SPWHIReadOnlyDeveloper |
| Base policy | ReadOnlyAccess (AWS managed) |
| Permission boundary | Deny s3:GetObject on tfstate and velero-backup buckets |
| Identity source | Google Workspace (spwhealthcare.in domain) |
| Session duration | 8 hours (auto-expire, re-login required) |
| Assigned group | spwhi-platform-readonly (Google Workspace group) |
Add them to the spwhi-platform-readonly Google Workspace group. No AWS console changes needed. They will have access on next SSO login.
Naming Conventions
All resources in spwhi-platform follow these conventions. Ward Mitra is one app — prefixed accordingly.
S3 Buckets
| Bucket | Purpose |
|---|---|
spwhi-platform-tfstate | Terraform remote state (all modules) |
spwhi-platform-tflock (DynamoDB) | Terraform state lock table |
spwhi-platform-velero-backup | EKS namespace backups (all apps) |
spwhi-platform-loki-logs | Grafana Loki log storage (all apps) |
spwhi-platform-cloudtrail-logs | CloudTrail audit logs |
spwhi-ward-mitra-photos | Ward Mitra complaint photo uploads |
spwhi-ward-mitra-web-prod | Ward Mitra React app static files |
SSM Parameter Store paths
/spwhi/{app}/{env}/{service}/{key}
Examples:
/spwhi/ward-mitra/prod/app/jwt-secret
/spwhi/ward-mitra/prod/app/firebase-key
/spwhi/ward-mitra/prod/db/host
/spwhi/ward-mitra/staging/app/node-env
/spwhi/field-assist/prod/app/api-key ← future app, same pattern
/spwhi/platform/grafana/admin-password
IAM / IRSA Roles
spwhi-{app}-{service}
Examples:
spwhi-ward-mitra-backend ← Node.js API IRSA role
spwhi-ward-mitra-ai-inference ← AI inference IRSA role
spwhi-ward-mitra-admin ← Admin panel IRSA role
spwhi-platform-eso ← External Secrets Operator IRSA role
spwhi-platform-karpenter ← Karpenter controller IRSA role
spwhi-platform-velero ← Velero backup IRSA role
spwhi-platform-loki ← Loki S3 access IRSA role
github-actions-terraform ← GitHub Actions OIDC role (infra repo)
github-actions-ecr-push ← GitHub Actions OIDC role (app repo)
github-actions-s3-deploy ← GitHub Actions OIDC role (app repo)
EKS Namespaces
spwhi-platform EKS cluster
│
├── platform ← ArgoCD, Karpenter, ESO, ALB Controller, Monitoring, Velero
├── ward-mitra-prod ← Ward Mitra production workloads
├── ward-mitra-staging ← Ward Mitra staging
├── ward-mitra-dev ← Ward Mitra dev
├── field-assist-prod ← Future (same pattern)
└── field-assist-staging ← Future
GitHub Repository Structure
SPW-HEALTHCARE-INNOVATIONS-Pvt-Ltd (GitHub Org)
│
├── spwhi-bootstrap ← Day 0 only. OIDC provider + 3 GitHub Actions IAM roles.
│ │ Run manually once. No CI pipeline in this repo.
│ ├── main.tf ← aws_iam_openid_connect_provider
│ ├── iam_roles.tf ← github-actions-terraform/ecr-push/s3-deploy roles
│ ├── variables.tf
│ ├── outputs.tf ← exports all 3 role ARNs
│ └── README.md
│
├── spwhi-infra ← All AWS infrastructure (Terraform).
│ │ Pipeline: plan on PR, apply on main merge.
│ ├── bootstrap/ ← BreakGlass user, IAM Identity Center, SCPs, CloudTrail
│ ├── modules/
│ │ ├── vpc/
│ │ ├── eks/ ← cluster + node groups + Karpenter
│ │ ├── rds/
│ │ └── iam/ ← all IRSA roles, one module per app
│ ├── environments/
│ │ ├── dev/
│ │ ├── staging/
│ │ └── prod/
│ └── .github/workflows/
│ └── terraform.yml ← OIDC auth via github-actions-terraform role
│
├── wardmitra-api ← WardMitra API code
│ └── .github/workflows/
│ └── api.yml → EKS ward-mitra-prod namespace (ecr-push role)
│
├── wardmitra-ui ← WardMitra UI code
│ └── .github/workflows/
│ └── web.yml → S3 + CloudFront (s3-deploy role)
│
└── field-assist ← Future app (same split pattern as WardMitra)
Day 0 Bootstrap Sequence
Step 1 — Create spwhi-platform AWS account via AWS Organizations console
(manual, done by SparkOps + SPW CTO together)
Step 2 — Create all 4 GitHub repos in SPW-HEALTHCARE-INNOVATIONS-Pvt-Ltd org:
spwhi-bootstrap, spwhi-infra, wardmitra-api, wardmitra-ui
Step 3 — Run spwhi-bootstrap Terraform locally
cd spwhi-bootstrap/
terraform init
terraform apply
→ Creates: OIDC provider + 3 GitHub Actions IAM roles
→ Note the 3 role ARNs from outputs
Step 4 — Add role ARNs as GitHub Actions variables (not secrets):
spwhi-infra repo: TERRAFORM_ROLE_ARN
wardmitra-api repo: ECR_PUSH_ROLE_ARN
wardmitra-ui repo: S3_DEPLOY_ROLE_ARN, CF_DISTRIBUTION_ID
Step 5 — Run spwhi-infra bootstrap/ module locally (one time only):
cd spwhi-infra/bootstrap/
terraform init && terraform apply
→ Creates: BreakGlass IAM user, IAM Identity Center setup,
CloudTrail, SCPs on AWS Organizations
Step 6 — Set MFA on BreakGlass user immediately
Store credentials in secure vault
Verify CloudTrail alarm fires when logging in (test login)
Step 7 — All future infra changes go through spwhi-infra pipeline
Local AWS credentials never needed again from this point
Step 8 — Assign spwhi-platform-readonly group in Google Workspace
Add all developers → they can SSO into read-only console immediately
Step 9 — Run spwhi-infra environments/dev/ pipeline
First real Terraform apply via GitHub Actions: VPC, EKS, RDS bootstrap
Access Summary Table
| Terraform (OIDC) | BreakGlass | ReadOnly Dev | |
|---|---|---|---|
| Type | OIDC role (machine) | IAM user (human) | SSO permission set (human) |
| Count | N/A | Max 2 | All developers |
| Console | ✗ | ✅ (MFA required) | ✅ (read-only) |
| CLI / Programmatic | ✅ (temp STS creds) | ✗ (blocked by SCP) | ✗ |
| Can modify resources | ✅ (via Terraform) | ✅ (emergency only) | ✗ |
| Credential type | Temp STS (1hr TTL) | Console password + MFA | SSO session (8hr TTL) |
| Alert on use | ✗ | ✅ immediate Slack + email | ✗ |
| Post-use reconciliation | N/A | ✅ mandatory (24hr) | N/A |
| Provisioned by | spwhi-bootstrap | spwhi-infra/bootstrap | IAM Identity Center |
Related Documents
- WardMitra Infrastructure Architecture — platform target state and rollout guidance
- Kubernetes — cluster and orchestration notes
- CI/CD — delivery pipeline guidance
- Monitoring — logs, metrics, and alerts
SparkOps Advisory Services · Sanket Pethkar · March 2026 · Confidential — SPW Healthcare Innovations Pvt. Ltd.