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 policy_assignments

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

aml_engineer

ml_engineers

s3:level2, ecr:level2, pipeline:level1, sagemaker:level2, lambda:level1, bedrock:level1

adata_scientist

data_scientists

s3:level2, ecr:level1, sagemaker:level2, bedrock:level1

adata_engineer

data_engineers

s3:level3, ecr:level2, pipeline:level2

aqa_testing

qa_testing

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

mlops_engineers

aml_engineer

Cover for absent ML engineer — deploy models, run training

ml_engineers

adata_scientist

Cover for absent data scientist — explore data, run experiments

data_scientists

aml_engineer

Temporarily deploy models when ML engineer unavailable

data_engineers

adata_scientist

Cover for absent data scientist — data exploration, experiment runs

qa_testing

adata_scientist

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

platform_administrators

Already have AdministratorAccess

operations_support

Different skill domain — monitoring, not ML

business_consumers

Read-only consumers, no technical role to cover

external_contractors

Security boundary — must not gain additional access

security_team

Different function — security, not ML

finops_managers

Different function — cost management, not ML

ai_governance

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_admin are universal enterprise patterns

  • Hard 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

model_approver

Approve/reject models in SageMaker Model Registry

sagemaker:UpdateModelPackage (ModelApprovalStatus)

TODO #3

security_admin

Incident response and security service configuration

13 specific write actions across guardduty, securityhub, config, access-analyzer

TODO #4

finops_admin

Budget management and cost reporting

budgets:ModifyBudget, budgets:CreateBudget, ce:CreateReport, ce:UpdateReport

TODO #7

cross_account_ops

Monitor resources in other AWS accounts

Deferred — depends on client account architecture

TODO #5

Elevation Group Assignments

Group

Can Assume

Scenario

ai_governance

model_approver

Approve model for production deployment — intentional elevation with CloudTrail audit

security_team

security_admin

Incident response — enable GuardDuty detector, update Config rules, modify Security Hub settings

finops_managers

finops_admin

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:

  1. For cross-function roles (mirrors_group present):

    • Resolve mirrors_group → look up that group’s policy_assignments

    • Create IAM Role with same policy attachments

    • Set trust policy for same-account assumption

  2. For elevation roles (custom_permissions present):

    • Create IAM Role with the defined custom permissions

    • Set trust policy for same-account assumption

  3. For group-level assumable_roles arrays:

    • Add sts:AssumeRole permission 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.