Assumable Roles Design¶
Table of Contents¶
Overview¶
Assumable roles are the third pillar of the IAM architecture, alongside IAM Groups (baseline daily access) and Service Roles (machine identities). They provide temporary, auditable access elevation without persistent group membership changes.
Core principle: When a user assumes a role, the role’s policies REPLACE the user’s group policies for that session. It’s a context switch, not additive.
Why not just add users to groups temporarily?
Group membership is persistent — someone must remember to remove them
Additive — user gets both groups’ permissions simultaneously
No clear audit trail of why they were added
Risk of “temporary” becoming permanent
Assumable roles solve all of these: session-based, auto-expiring, CloudTrail-logged, context-switching.
Two Categories of Assumable Roles¶
Category |
What it does |
Policy source |
Example |
|---|---|---|---|
Cross-function |
“Wear someone else’s hat” — temporarily perform another job function |
Mirrors an existing group’s |
MLOps engineer covers for absent ML engineer |
Elevation |
“Unlock a higher capability” — perform a privileged action within your own domain |
Custom permissions outside the 6-service level system |
ai_governance approves a model for production |
Category 1: Cross-Function Roles¶
Design Decision: mirrors_group Pattern¶
Decision: Cross-function roles do NOT duplicate policy_assignments. They reference a group by name and inherit its policies at template generation time.
Rationale:
Single source of truth — if the mirrored group’s policies change, the role automatically reflects that
Zero drift risk between group and its corresponding assumable role
DRY — no duplicated policy_assignments to maintain
Config pattern:
aml_engineer:
description: Cross-function role mirroring ml_engineers group
mirrors_group: ml_engineers
The generator resolves mirrors_group → looks up that group’s policy_assignments → attaches the same policies to the IAM Role.
Cross-Function Role Definitions¶
Assumable Role |
Mirrors Group |
Effective Policies |
|---|---|---|
|
|
s3:level2, ecr:level2, pipeline:level1, sagemaker:level2, lambda:level1, bedrock:level1 |
|
|
s3:level2, ecr:level1, sagemaker:level2, bedrock:level1 |
|
|
s3:level3, ecr:level2, pipeline:level2 |
|
|
s3:level1, ecr:level1, sagemaker:level2, lambda:level1, bedrock:level1 |
Note: Effective policies are resolved at generation time from the mirrored group. If the group’s policy_assignments change, the role’s effective policies change automatically.
Cross-Function Group Assignments¶
Group |
Can Assume |
Scenario |
|---|---|---|
|
|
Cover for absent ML engineer — deploy models, run training |
|
|
Cover for absent data scientist — explore data, run experiments |
|
|
Temporarily deploy models when ML engineer unavailable |
|
|
Cover for absent data scientist — data exploration, experiment runs |
|
|
Reproduce data science experiments for validation |
Why These Pairings¶
Cross-function assumptions must reflect realistic skill overlap. A person can only “cover for” a role they have the technical skills to perform:
MLOps ↔ ML Engineers: Closest skill overlap in the ML lifecycle
ML Engineers → Data Scientists: ML engineers understand data science workflows
Data Engineers → Data Scientists: Data engineers understand data pipelines and exploration
Data Scientists → ML Engineers: Data scientists sometimes need to deploy models
QA → Data Scientists: QA needs to reproduce experiments for validation
Groups Excluded from Cross-Function Roles¶
Group |
Why no cross-function role |
|---|---|
|
Already have AdministratorAccess |
|
Different skill domain — monitoring, not ML |
|
Read-only consumers, no technical role to cover |
|
Security boundary — must not gain additional access |
|
Different function — security, not ML |
|
Different function — cost management, not ML |
|
Governance/oversight role, not hands-on ML |
Category 2: Elevation Roles¶
Design Decision: Best-Guess Defaults¶
Decision: Define three universal elevation roles with sensible default permissions. Defer one (cross_account_ops) to client engagement.
Rationale:
model_approver,security_admin,finops_adminare universal enterprise patternsHard to imagine an enterprise client that wouldn’t need them
Permissions are well-defined by AWS service boundaries
Marked as customizable per client — defaults can be adjusted
Deferred: cross_account_ops (TODO #5) depends on client account architecture (single vs multi-account). Cannot provide a meaningful default.
Elevation Role Definitions¶
Assumable Role |
Purpose |
Permissions (outside 6-service level system) |
Related TODO |
|---|---|---|---|
|
Approve/reject models in SageMaker Model Registry |
|
TODO #3 |
|
Incident response and security service configuration |
13 specific write actions across guardduty, securityhub, config, access-analyzer |
TODO #4 |
|
Budget management and cost reporting |
|
TODO #7 |
|
Monitor resources in other AWS accounts |
Deferred — depends on client account architecture |
TODO #5 |
Elevation Group Assignments¶
Group |
Can Assume |
Scenario |
|---|---|---|
|
|
Approve model for production deployment — intentional elevation with CloudTrail audit |
|
|
Incident response — enable GuardDuty detector, update Config rules, modify Security Hub settings |
|
|
Modify budgets, create cost reports — day-to-day is read-only, elevation for changes |
Config Structure¶
Top-Level assumable_roles Section¶
New section in master config, parallel to service_roles and cross_account_roles:
assumable_roles:
# Cross-function roles (mirrors_group pattern)
aml_engineer:
description: Cross-function role mirroring ml_engineers group
mirrors_group: ml_engineers
adata_scientist:
description: Cross-function role mirroring data_scientists group
mirrors_group: data_scientists
adata_engineer:
description: Cross-function role mirroring data_engineers group
mirrors_group: data_engineers
aqa_testing:
description: Cross-function role mirroring qa_testing group
mirrors_group: qa_testing
# Elevation roles (custom permissions — actions outside the 6-service level system)
model_approver:
description: Approve/reject models in SageMaker Model Registry
custom_permissions:
Version: '2012-10-17'
Statement:
- Sid: ModelApproval
Effect: Allow
Action:
- sagemaker:UpdateModelPackage
Resource: 'arn:aws:sagemaker:*:*:model-package/*'
security_admin:
description: Security incident response and governance write access
custom_permissions:
Version: '2012-10-17'
Statement:
- Sid: SecurityAdmin
Effect: Allow
Action:
- guardduty:CreateDetector
- guardduty:UpdateDetector
- guardduty:ArchiveFindings
- guardduty:UpdateFindings
- securityhub:BatchUpdateFindings
- securityhub:UpdateFindings
- securityhub:EnableSecurityHub
- config:PutConfigRule
- config:PutConfigurationRecorder
- config:StartConfigRulesEvaluation
- access-analyzer:CreateAnalyzer
- access-analyzer:CreateArchiveRule
- access-analyzer:StartResourceScan
Resource: '*'
finops_admin:
description: Budget management and Cost Explorer reporting for FinOps team
custom_permissions:
Version: '2012-10-17'
Statement:
- Sid: FinopsAdmin
Effect: Allow
Action:
- budgets:ModifyBudget
- budgets:CreateBudget
- ce:CreateReport
- ce:UpdateReport
Resource: '*'
Group-Level assumable_roles Arrays¶
Each group references the roles it can assume:
mlops_engineers:
assumable_roles:
- aml_engineer
ai_governance:
assumable_roles:
- model_approver
security_team:
assumable_roles:
- security_admin
finops_managers:
assumable_roles:
- finops_admin
Generator Behavior¶
At template generation time:
For cross-function roles (
mirrors_grouppresent):Resolve
mirrors_group→ look up that group’spolicy_assignmentsCreate IAM Role with same policy attachments
Set trust policy for same-account assumption
For elevation roles (
custom_permissionspresent):Create IAM Role with the defined custom permissions
Set trust policy for same-account assumption
For group-level
assumable_rolesarrays:Add
sts:AssumeRolepermission to the group for each listed role ARN
Trust Policy Pattern¶
All assumable roles (both categories) use the same trust policy pattern — allow assumption by IAM principals in the same AWS account:
trust_policy:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: arn:aws:iam::${account_id}:root
Action: sts:AssumeRole
Condition: {} # Future: MFA, session duration (AD-7)
The Condition block is intentionally empty for v1. AD-7 (MFA and Session Duration Controls) in ROLES_ARCHITECTURE.md tracks the future addition of aws:MultiFactorAuthPresent and session duration limits.
Access control is enforced on the caller side — only groups with the role listed in their assumable_roles array get the sts:AssumeRole permission for that role ARN.